diff --git a/packages/android_alarm_manager/CHANGELOG.md b/packages/android_alarm_manager/CHANGELOG.md index aff0e4a30982..1913f97a8218 100644 --- a/packages/android_alarm_manager/CHANGELOG.md +++ b/packages/android_alarm_manager/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.5+15 + +* Update android compileSdkVersion to 29. + ## 0.4.5+14 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/android_alarm_manager/android/build.gradle b/packages/android_alarm_manager/android/build.gradle index 8a59fed74438..5bbaf2291994 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 28 + compileSdkVersion 29 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 diff --git a/packages/android_alarm_manager/example/android/app/build.gradle b/packages/android_alarm_manager/example/android/app/build.gradle index f066040810c2..9722ec280205 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/android_alarm_manager/pubspec.yaml b/packages/android_alarm_manager/pubspec.yaml index d4292a0dc940..e12bedd11b70 100644 --- a/packages/android_alarm_manager/pubspec.yaml +++ b/packages/android_alarm_manager/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for accessing the Android AlarmManager service, and # 0.4.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.4.5+14 +version: 0.4.5+15 homepage: https://github.com/flutter/plugins/tree/master/packages/android_alarm_manager dependencies: diff --git a/packages/android_intent/CHANGELOG.md b/packages/android_intent/CHANGELOG.md index 711057ce3ec6..5f01fc9298c7 100644 --- a/packages/android_intent/CHANGELOG.md +++ b/packages/android_intent/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.7+6 + +* Update android compileSdkVersion to 29. + ## 0.3.7+5 * Android Code Inspection and Clean up. diff --git a/packages/android_intent/android/build.gradle b/packages/android_intent/android/build.gradle index 5d0f1cae7159..d7120919d654 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/android_intent/example/android/app/build.gradle b/packages/android_intent/example/android/app/build.gradle index 48178f2be030..a309dc2e2d5c 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/android_intent/pubspec.yaml b/packages/android_intent/pubspec.yaml index 764719e3901a..fef90e2f7602 100644 --- a/packages/android_intent/pubspec.yaml +++ b/packages/android_intent/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/android_intent # 0.3.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.3.7+5 +version: 0.3.7+6 flutter: plugin: diff --git a/packages/battery/battery/CHANGELOG.md b/packages/battery/battery/CHANGELOG.md index 8acfb5b075ad..8a38fde5d937 100644 --- a/packages/battery/battery/CHANGELOG.md +++ b/packages/battery/battery/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.7 + +* Update android compileSdkVersion to 29. + ## 1.0.6 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/battery/battery/android/build.gradle b/packages/battery/battery/android/build.gradle index ff485fbc6b29..30c4594c553c 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/battery/battery/example/android/app/build.gradle b/packages/battery/battery/example/android/app/build.gradle index e84c2c45889d..4fcd6ba0049e 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/battery/battery/pubspec.yaml b/packages/battery/battery/pubspec.yaml index 147d62ae46cd..d841805c7612 100644 --- a/packages/battery/battery/pubspec.yaml +++ b/packages/battery/battery/pubspec.yaml @@ -2,7 +2,7 @@ name: battery description: Flutter plugin for accessing information about the battery state (full, charging, discharging) on Android and iOS. homepage: https://github.com/flutter/plugins/tree/master/packages/battery/battery -version: 1.0.6 +version: 1.0.7 flutter: plugin: diff --git a/packages/camera/CHANGELOG.md b/packages/camera/CHANGELOG.md index fe5070d605e5..ebf654f49b22 100644 --- a/packages/camera/CHANGELOG.md +++ b/packages/camera/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.5.8+11 + +* Fix rare nullptr exception on Android. +* Updated README.md with information about handling App lifecycle changes. + +## 0.5.8+10 + +* Suppress the `deprecated_member_use` warning in the example app for `ScaffoldMessenger.showSnackBar`. + +## 0.5.8+9 + +* Update android compileSdkVersion to 29. + ## 0.5.8+8 * Fixed garbled audio (in video) by setting audio encoding bitrate. diff --git a/packages/camera/README.md b/packages/camera/README.md index fbd73929e6de..c56ed0dfe349 100644 --- a/packages/camera/README.md +++ b/packages/camera/README.md @@ -41,6 +41,27 @@ Change the minimum Android sdk version to 21 (or higher) in your `android/app/bu minSdkVersion 21 ``` +### Handling Lifecycle states + +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); + } + } + } +``` + ### Example Here is a small example flutter app displaying a full screen camera preview. diff --git a/packages/camera/android/build.gradle b/packages/camera/android/build.gradle index 13495b2bd29d..d19d6df7b447 100644 --- a/packages/camera/android/build.gradle +++ b/packages/camera/android/build.gradle @@ -27,7 +27,7 @@ project.getTasks().withType(JavaCompile){ apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 21 diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java index 63e4d03e982a..2384393c7d2b 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -410,6 +410,8 @@ public void resumeVideoRecording(@NonNull final Result result) { } public void startPreview() throws CameraAccessException { + if (pictureImageReader == null || pictureImageReader.getSurface() == null) return; + createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); } diff --git a/packages/camera/example/android/app/build.gradle b/packages/camera/example/android/app/build.gradle index e47b6db5e21e..7d0e281b74e8 100644 --- a/packages/camera/example/android/app/build.gradle +++ b/packages/camera/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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/camera/example/lib/main.dart b/packages/camera/example/lib/main.dart index ce8d37457123..3ec6604ad788 100644 --- a/packages/camera/example/lib/main.dart +++ b/packages/camera/example/lib/main.dart @@ -272,6 +272,7 @@ 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))); } diff --git a/packages/camera/pubspec.yaml b/packages/camera/pubspec.yaml index 64cae9be5ae3..7a2b187098ef 100644 --- a/packages/camera/pubspec.yaml +++ b/packages/camera/pubspec.yaml @@ -2,7 +2,7 @@ name: camera description: A Flutter plugin for getting information about and controlling the camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video, and streaming image buffers to dart. -version: 0.5.8+8 +version: 0.5.8+11 homepage: https://github.com/flutter/plugins/tree/master/packages/camera diff --git a/packages/connectivity/connectivity/CHANGELOG.md b/packages/connectivity/connectivity/CHANGELOG.md index a42eaf0a359c..8e2802ab5b04 100644 --- a/packages/connectivity/connectivity/CHANGELOG.md +++ b/packages/connectivity/connectivity/CHANGELOG.md @@ -1,3 +1,26 @@ +## 2.0.0 + +* [Breaking Change] The `getWifiName`, `getWifiBSSID` and `getWifiIP` are removed to [wifi_info_flutter](https://github.com/flutter/plugins/tree/master/packages/wifi_info_flutter) +* Migration guide: + + If you don't use any of the above APIs, your code should work as is. In addition, you can also remove `NSLocationAlwaysAndWhenInUseUsageDescription` and `NSLocationWhenInUseUsageDescription` in `ios/Runner/Info.plist` + + If you use any of the above APIs, you can find the same APIs in the [wifi_info_flutter](https://github.com/flutter/plugins/tree/master/packages/wifi_info_flutter/wifi_info_flutter) plugin. + For example, to migrate `getWifiName`, use the new plugin: + ```dart + final WifiInfo _wifiInfo = WifiInfo(); + final String wifiName = await _wifiInfo.getWifiName(); + ``` + +## 1.0.0 + +* Mark wifi related code deprecated. +* Announce 1.0.0! + +## 0.4.9+5 + +* Update android compileSdkVersion to 29. + ## 0.4.9+4 * Update README with the updated information about WifiInfo on Android O or higher. diff --git a/packages/connectivity/connectivity/README.md b/packages/connectivity/connectivity/README.md index 0a13749b9dc5..a6a1dcc2838a 100644 --- a/packages/connectivity/connectivity/README.md +++ b/packages/connectivity/connectivity/README.md @@ -7,13 +7,6 @@ This plugin works for iOS and Android. > 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. -**Please set your constraint to `connectivity: '>=0.4.y+x <2.0.0'`** - -## Backward compatible 1.0.0 version is coming -The plugin has reached a stable API, we guarantee that version `1.0.0` will be backward compatible with `0.4.y+z`. -Please use `connectivity: '>=0.4.y+x <2.0.0'` as your dependency constraint to allow a smoother ecosystem migration. -For more details see: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 - ## Usage Sample usage to check current status: @@ -59,58 +52,6 @@ dispose() { Note that connectivity changes are no longer communicated to Android apps in the background starting with Android O. *You should always check for connectivity status when your app is resumed.* The broadcast is only useful when your application is in the foreground. -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:connectivity/connectivity.dart'; - -var wifiBSSID = await (Connectivity().getWifiBSSID()); -var wifiIP = await (Connectivity().getWifiIP());network -var wifiName = await (Connectivity().getWifiName());wifi network -``` - -### 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 diff --git a/packages/connectivity/connectivity/android/build.gradle b/packages/connectivity/connectivity/android/build.gradle index 3542dbc6c01d..e1982a03cf09 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/connectivity/connectivity/android/src/main/AndroidManifest.xml b/packages/connectivity/connectivity/android/src/main/AndroidManifest.xml index f4eafe489d0c..52bbe9edafa0 100644 --- a/packages/connectivity/connectivity/android/src/main/AndroidManifest.xml +++ b/packages/connectivity/connectivity/android/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - diff --git a/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/Connectivity.java b/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/Connectivity.java index dbf8e25eb7ad..0a3a6bac0914 100644 --- a/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/Connectivity.java +++ b/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/Connectivity.java @@ -7,19 +7,14 @@ import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; import android.os.Build; /** Reports connectivity related information such as connectivity type and wifi information. */ class Connectivity { private ConnectivityManager connectivityManager; - private WifiManager wifiManager; - Connectivity(ConnectivityManager connectivityManager, WifiManager wifiManager) { + Connectivity(ConnectivityManager connectivityManager) { this.connectivityManager = connectivityManager; - this.wifiManager = wifiManager; } String getNetworkType() { @@ -41,48 +36,10 @@ String getNetworkType() { return getNetworkTypeLegacy(); } - String getWifiName() { - WifiInfo wifiInfo = getWifiInfo(); - String ssid = null; - if (wifiInfo != null) ssid = wifiInfo.getSSID(); - if (ssid != null) ssid = ssid.replaceAll("\"", ""); // Android returns "SSID" - return ssid; - } - - String getWifiBSSID() { - 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(); - } - @SuppressWarnings("deprecation") private String getNetworkTypeLegacy() { // handle type for Android versions less than Android 9 - NetworkInfo info = connectivityManager.getActiveNetworkInfo(); + android.net.NetworkInfo info = connectivityManager.getActiveNetworkInfo(); if (info == null || !info.isConnected()) { return "none"; } diff --git a/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/ConnectivityMethodChannelHandler.java b/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/ConnectivityMethodChannelHandler.java index 931b702d442a..de1958a1f4f1 100644 --- a/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/ConnectivityMethodChannelHandler.java +++ b/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/ConnectivityMethodChannelHandler.java @@ -31,15 +31,6 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { case "check": result.success(connectivity.getNetworkType()); break; - case "wifiName": - result.success(connectivity.getWifiName()); - break; - case "wifiBSSID": - result.success(connectivity.getWifiBSSID()); - break; - case "wifiIPAddress": - result.success(connectivity.getWifiIPAddress()); - break; default: result.notImplemented(); break; diff --git a/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/ConnectivityPlugin.java b/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/ConnectivityPlugin.java index f036a8aa15ae..3de60f06c67e 100644 --- a/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/ConnectivityPlugin.java +++ b/packages/connectivity/connectivity/android/src/main/java/io/flutter/plugins/connectivity/ConnectivityPlugin.java @@ -6,7 +6,6 @@ import android.content.Context; import android.net.ConnectivityManager; -import android.net.wifi.WifiManager; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.EventChannel; @@ -41,10 +40,8 @@ private void setupChannels(BinaryMessenger messenger, Context context) { eventChannel = new EventChannel(messenger, "plugins.flutter.io/connectivity_status"); ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - WifiManager wifiManager = - (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); - Connectivity connectivity = new Connectivity(connectivityManager, wifiManager); + Connectivity connectivity = new Connectivity(connectivityManager); ConnectivityMethodChannelHandler methodChannelHandler = new ConnectivityMethodChannelHandler(connectivity); diff --git a/packages/connectivity/connectivity/example/android/app/build.gradle b/packages/connectivity/connectivity/example/android/app/build.gradle index 5d1f138bfe1a..bacbc2f6f4c2 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/connectivity/connectivity/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/connectivity/connectivity/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index d22f10b2ab63..8122b0a0c2f2 100644 --- a/packages/connectivity/connectivity/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/packages/connectivity/connectivity/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,116 +1,121 @@ { "images" : [ { - "size" : "20x20", - "idiom" : "iphone", "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" }, { - "size" : "20x20", - "idiom" : "iphone", "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" }, { - "size" : "29x29", - "idiom" : "iphone", "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "iphone", "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "iphone", "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" }, { - "size" : "40x40", - "idiom" : "iphone", "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" }, { - "size" : "40x40", - "idiom" : "iphone", "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" }, { - "size" : "60x60", - "idiom" : "iphone", "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" }, { - "size" : "60x60", - "idiom" : "iphone", "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" }, { - "size" : "20x20", - "idiom" : "ipad", "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" }, { - "size" : "20x20", - "idiom" : "ipad", "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" }, { - "size" : "29x29", - "idiom" : "ipad", "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "ipad", "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" }, { - "size" : "40x40", - "idiom" : "ipad", "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" }, { - "size" : "40x40", - "idiom" : "ipad", "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" }, { - "size" : "76x76", - "idiom" : "ipad", "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" }, { - "size" : "76x76", - "idiom" : "ipad", "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" }, { - "size" : "83.5x83.5", - "idiom" : "ipad", "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } } diff --git a/packages/connectivity/connectivity/example/ios/Runner/Info.plist b/packages/connectivity/connectivity/example/ios/Runner/Info.plist index babbd80f1619..d76382b40acf 100644 --- a/packages/connectivity/connectivity/example/ios/Runner/Info.plist +++ b/packages/connectivity/connectivity/example/ios/Runner/Info.plist @@ -22,10 +22,6 @@ 1 LSRequiresIPhoneOS - 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. UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/packages/connectivity/connectivity/example/lib/main.dart b/packages/connectivity/connectivity/example/lib/main.dart index f8486165fa70..e05497136b86 100644 --- a/packages/connectivity/connectivity/example/lib/main.dart +++ b/packages/connectivity/connectivity/example/lib/main.dart @@ -100,66 +100,6 @@ class _MyHomePageState extends State { Future _updateConnectionStatus(ConnectivityResult result) async { switch (result) { case ConnectivityResult.wifi: - String wifiName, wifiBSSID, wifiIP; - - try { - if (!kIsWeb && 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 (!kIsWeb && 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()); diff --git a/packages/connectivity/connectivity/example/test_driver/integration_test/connectivity_test.dart b/packages/connectivity/connectivity/example/test_driver/integration_test/connectivity_test.dart index 54a67337285a..d48deae3403d 100644 --- a/packages/connectivity/connectivity/example/test_driver/integration_test/connectivity_test.dart +++ b/packages/connectivity/connectivity/example/test_driver/integration_test/connectivity_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:io'; import 'package:integration_test/integration_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:connectivity/connectivity.dart'; @@ -20,22 +19,6 @@ void main() { 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/integration_test/connectivity_test.dart b/packages/connectivity/connectivity/integration_test/connectivity_test.dart index 54a67337285a..d48deae3403d 100644 --- a/packages/connectivity/connectivity/integration_test/connectivity_test.dart +++ b/packages/connectivity/connectivity/integration_test/connectivity_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:io'; import 'package:integration_test/integration_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:connectivity/connectivity.dart'; @@ -20,22 +19,6 @@ void main() { 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/ios/Classes/FLTConnectivityPlugin.m b/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m index 526bee25d561..fb996c1b11ba 100644 --- a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m +++ b/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m @@ -7,7 +7,6 @@ #import "Reachability/Reachability.h" #import -#import "FLTConnectivityLocationHandler.h" #import "SystemConfiguration/CaptiveNetwork.h" #include @@ -16,8 +15,6 @@ @interface FLTConnectivityPlugin () -@property(strong, nonatomic) FLTConnectivityLocationHandler* locationHandler; - @end @implementation FLTConnectivityPlugin { @@ -39,58 +36,6 @@ + (void)registerWithRegistrar:(NSObject*)registrar { [streamChannel setStreamHandler:instance]; } -- (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; -} - - (NSString*)statusFromReachability:(Reachability*)reachability { NetworkStatus status = [reachability currentReachabilityStatus]; switch (status) { @@ -111,24 +56,6 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { // and the code // gets more involved. So for now, this will do. result([self statusFromReachability:[Reachability reachabilityForInternetConnection]]); - } else 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:[FLTConnectivityLocationHandler - 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); } @@ -139,34 +66,6 @@ - (void)onReachabilityDidChange:(NSNotification*)notification { _eventSink([self statusFromReachability:curReach]); } -- (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"; } - } -} - -- (FLTConnectivityLocationHandler*)locationHandler { - if (!_locationHandler) { - _locationHandler = [FLTConnectivityLocationHandler new]; - } - return _locationHandler; -} - #pragma mark FlutterStreamHandler impl - (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { diff --git a/packages/connectivity/connectivity/lib/connectivity.dart b/packages/connectivity/connectivity/lib/connectivity.dart index a5d9f25089cf..c9655365f772 100644 --- a/packages/connectivity/connectivity/lib/connectivity.dart +++ b/packages/connectivity/connectivity/lib/connectivity.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:flutter/services.dart'; import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; // Export enums from the platform_interface so plugin users can use them directly. @@ -46,125 +45,4 @@ class Connectivity { Future checkConnectivity() { return _platform.checkConnectivity(); } - - /// 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/connectivity/connectivity/pubspec.yaml b/packages/connectivity/connectivity/pubspec.yaml index 691c683a84e0..af26f703d8b9 100644 --- a/packages/connectivity/connectivity/pubspec.yaml +++ b/packages/connectivity/connectivity/pubspec.yaml @@ -2,10 +2,7 @@ name: connectivity description: Flutter plugin for discovering the state of the network (WiFi & mobile/cellular) connectivity on Android and iOS. homepage: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity -# 0.4.y+z is compatible with 1.0.0, if you land a breaking change bump -# the version to 2.0.0. -# See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.4.9+4 +version: 2.0.0 flutter: plugin: diff --git a/packages/connectivity/connectivity/test/connectivity_test.dart b/packages/connectivity/connectivity/test/connectivity_test.dart index 7ed2c8d7f7e2..b7749ca7a6be 100644 --- a/packages/connectivity/connectivity/test/connectivity_test.dart +++ b/packages/connectivity/connectivity/test/connectivity_test.dart @@ -11,9 +11,6 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:mockito/mockito.dart'; const ConnectivityResult kCheckConnectivityResult = ConnectivityResult.wifi; -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 = @@ -33,33 +30,6 @@ void main() { ConnectivityResult result = await connectivity.checkConnectivity(); expect(result, kCheckConnectivityResult); }); - - test('getWifiName', () async { - String result = await connectivity.getWifiName(); - expect(result, kWifiNameResult); - }); - - test('getWifiBSSID', () async { - String result = await connectivity.getWifiBSSID(); - expect(result, kWifiBSSIDResult); - }); - - test('getWifiIP', () async { - String result = await connectivity.getWifiIP(); - expect(result, kWifiIpAddressResult); - }); - - test('requestLocationServiceAuthorization', () async { - LocationAuthorizationStatus result = - await connectivity.requestLocationServiceAuthorization(); - expect(result, kRequestLocationResult); - }); - - test('getLocationServiceAuthorization', () async { - LocationAuthorizationStatus result = - await connectivity.getLocationServiceAuthorization(); - expect(result, kRequestLocationResult); - }); }); } @@ -69,26 +39,4 @@ class MockConnectivityPlatform extends Mock Future checkConnectivity() async { return kCheckConnectivityResult; } - - Future getWifiName() async { - return kWifiNameResult; - } - - Future getWifiBSSID() async { - return kWifiBSSIDResult; - } - - Future getWifiIP() async { - return kWifiIpAddressResult; - } - - Future requestLocationServiceAuthorization({ - bool requestAlwaysLocationUsage = false, - }) async { - return kRequestLocationResult; - } - - Future getLocationServiceAuthorization() async { - return kGetLocationResult; - } } diff --git a/packages/device_info/device_info/CHANGELOG.md b/packages/device_info/device_info/CHANGELOG.md index 9e627f90b30f..836d56232f95 100644 --- a/packages/device_info/device_info/CHANGELOG.md +++ b/packages/device_info/device_info/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.2+9 + +* Update android compileSdkVersion to 29. + ## 0.4.2+8 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/device_info/device_info/android/build.gradle b/packages/device_info/device_info/android/build.gradle index 58bdfd327631..998efae83369 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 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 43d6d0a1a8c5..eb0c628330be 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/device_info/device_info/pubspec.yaml b/packages/device_info/device_info/pubspec.yaml index 967a5bb0b585..bf36bf6f34d9 100644 --- a/packages/device_info/device_info/pubspec.yaml +++ b/packages/device_info/device_info/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/device_info # 0.4.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.4.2+8 +version: 0.4.2+9 flutter: plugin: diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index edc6bd6d2ba1..a9d61f1178c2 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+7 + +* Update android compileSdkVersion to 29. + ## 0.0.1+6 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index 4af1d3e8b67f..31218f83b0a9 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/espresso/example/android/app/build.gradle b/packages/espresso/example/android/app/build.gradle index 0be415652fdc..45fe0e173fe1 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 6eb371337e01..fe284260b73c 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -1,6 +1,6 @@ name: espresso description: Java classes for testing Flutter apps using Espresso. -version: 0.0.1+6 +version: 0.0.1+7 homepage: https://github.com/flutter/plugins/espresso environment: diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 7d5324a17e7c..5135fdd6e2e2 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 @@ +## 1.0.3 + +* Update android compileSdkVersion to 29. + ## 1.0.2 * Remove `io.flutter.embedded_views_preview` requirement from readme. 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 aa2771e8e3a5..f12e3f211131 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 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 786c7845db94..0bdb5a2c6a68 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 0300a7c54589..5a9aae8fedef 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -1,7 +1,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter -version: 1.0.2 +version: 1.0.3 dependencies: flutter: 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 42805c34926d..e4918ab1d5ca 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,14 @@ +## 0.1.0+5 + +* Update `package:google_maps` to `^3.4.5`. +* Fix `GoogleMapController.getLatLng()`. [Issue](https://github.com/flutter/flutter/issues/67606). +* Make `InfoWindow` contents clickable so `onTap` works as advertised. [Issue](https://github.com/flutter/flutter/issues/67289). +* Fix `InfoWindow` snippets when converting initial markers. [Issue](https://github.com/flutter/flutter/issues/67854). + +## 0.1.0+4 + +* Update `package:sanitize_html` to `^1.4.1` to prevent [a crash](https://github.com/flutter/flutter/issues/67854) when InfoWindow title/snippet have links. + ## 0.1.0+3 * Fix crash when converting initial polylines and polygons. [Issue](https://github.com/flutter/flutter/issues/65152). 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 3e1b1f182fc9..e8847fdd7b84 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 @@ -270,14 +270,15 @@ Set _rawOptionsToInitialMarkers(Map rawOptions) { if (rawMarker['position'] != null) { position = LatLng.fromJson(rawMarker['position']); } - if (rawMarker['infoWindow'] != null || rawMarker['snippet'] != null) { - String title = rawMarker['infoWindow'] != null - ? rawMarker['infoWindow']['title'] - : null; - infoWindow = InfoWindow( - title: title ?? '', - snippet: rawMarker['snippet'] ?? '', - ); + if (rawMarker['infoWindow'] != null) { + final String title = rawMarker['infoWindow']['title']; + final String snippet = rawMarker['infoWindow']['snippet']; + if (title != null || snippet != null) { + infoWindow = InfoWindow( + title: title ?? '', + snippet: snippet ?? '', + ); + } } return Marker( markerId: MarkerId(rawMarker['markerId']), @@ -378,13 +379,28 @@ gmaps.InfoWindowOptions _infoWindowOptionsFromMarker(Marker marker) { return null; } - final content = '

' + - sanitizeHtml(marker.infoWindow.title ?? "") + - '

' + - sanitizeHtml(marker.infoWindow.snippet ?? ""); + // Add an outer wrapper to the contents of the infowindow, we need it to listen + // to click events... + final HtmlElement container = DivElement() + ..id = 'gmaps-marker-${marker.markerId.value}-infowindow'; + if (marker.infoWindow.title?.isNotEmpty ?? false) { + final HtmlElement title = HeadingElement.h3() + ..className = 'infowindow-title' + ..innerText = marker.infoWindow.title; + container.children.add(title); + } + if (marker.infoWindow.snippet?.isNotEmpty ?? false) { + final HtmlElement snippet = DivElement() + ..className = 'infowindow-snippet' + ..setInnerHtml( + sanitizeHtml(marker.infoWindow.snippet), + treeSanitizer: NodeTreeSanitizer.trusted, + ); + container.children.add(snippet); + } return gmaps.InfoWindowOptions() - ..content = content + ..content = container ..zIndex = marker.zIndex; // TODO: Compute the pixelOffset of the infoWindow, from the size of the Marker, // and the marker.infoWindow.anchor property. 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 707af828e2c6..8195473ee6ba 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 @@ -256,9 +256,8 @@ class GoogleMapController { /// Returns the [LatLng] for a `screenCoordinate` (in pixels) of the viewport. Future getLatLng(ScreenCoordinate screenCoordinate) async { - final latLng = _googleMap.projection.fromPointToLatLng( - gmaps.Point(screenCoordinate.x, screenCoordinate.y), - ); + final gmaps.LatLng latLng = + _pixelToLatLng(_googleMap, screenCoordinate.x, screenCoordinate.y); return _gmLatLngToLatLng(latLng); } 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 a067e352732f..30db477ee5f4 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 @@ -46,6 +46,10 @@ class MarkerController { /// Returns the [gmaps.Marker] associated to this controller. gmaps.Marker get marker => _marker; + /// Returns the [gmaps.InfoWindow] associated to the marker. + @visibleForTesting + gmaps.InfoWindow get infoWindow => _infoWindow; + /// Updates the options of the wrapped [gmaps.Marker] object. void update( gmaps.MarkerOptions options, { 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 ebb478d20b06..a0428adcdc2e 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 @@ -38,10 +38,14 @@ class MarkersController extends GeometryController { gmaps.InfoWindow gmInfoWindow; if (infoWindowOptions != null) { - gmInfoWindow = gmaps.InfoWindow(infoWindowOptions) - ..addListener('click', () { + 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) { + infoWindowOptions.content.onClick.listen((_) { _onInfoWindowTap(marker.markerId); }); + } } final currentMarker = _markerIdToController[marker.markerId]?.marker; 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 3fb80d4a5c18..a44d5c185c1f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -1,7 +1,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter -version: 0.1.0+3 +version: 0.1.0+5 flutter: plugin: @@ -17,9 +17,9 @@ dependencies: sdk: flutter meta: ^1.1.7 google_maps_flutter_platform_interface: ^1.0.4 - google_maps: ^3.0.0 + google_maps: ^3.4.5 stream_transform: ^1.2.0 - sanitize_html: ^1.3.0 + sanitize_html: ^1.4.1 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart index 625ecb355a31..dfd01c6faf55 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart @@ -146,7 +146,13 @@ void main() { {'circleId': 'circle-1'} ], 'markersToAdd': [ - {'markerId': 'marker-1'} + { + 'markerId': 'marker-1', + 'infoWindow': { + 'title': 'title for test', + 'snippet': 'snippet for test', + }, + }, ], 'polygonsToAdd': [ { @@ -191,10 +197,34 @@ void main() { expect(capturedCircles.first.circleId.value, 'circle-1'); expect(capturedMarkers.first.markerId.value, 'marker-1'); + expect(capturedMarkers.first.infoWindow.snippet, 'snippet for test'); + expect(capturedMarkers.first.infoWindow.title, 'title for test'); expect(capturedPolygons.first.polygonId.value, 'polygon-1'); expect(capturedPolylines.first.polylineId.value, 'polyline-1'); }); + testWidgets('empty infoWindow does not create InfoWindow instance.', + (WidgetTester tester) async { + controller = _createController(options: { + 'markersToAdd': [ + { + 'markerId': 'marker-1', + 'infoWindow': {}, + }, + ], + }); + controller.debugSetOverrides( + markers: markers, + ); + + controller.init(); + + final capturedMarkers = + verify(markers.addMarkers(captureAny)).captured[0] as Set; + + expect(capturedMarkers.first.infoWindow, isNull); + }); + group('Initialization options', () { gmaps.MapOptions capturedOptions; setUp(() { @@ -390,6 +420,8 @@ 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 }); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart index 76ddf017985c..a813ff86188e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:html'; import 'package:integration_test/integration_test.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; @@ -110,5 +111,56 @@ void main() { expect(controller.markers.length, 1); expect(controller.markers[MarkerId('1')].marker.icon, isNull); }); + + // https://github.com/flutter/flutter/issues/67854 + testWidgets('InfoWindow snippet can have links', + (WidgetTester tester) async { + final markers = { + Marker( + markerId: MarkerId('1'), + infoWindow: InfoWindow( + title: 'title for test', + snippet: 'Go to Google >>>', + ), + ), + }; + + controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final content = + controller.markers[MarkerId('1')].infoWindow.content as HtmlElement; + expect(content.innerHtml, contains('title for test')); + expect( + content.innerHtml, + contains( + 'Go to Google >>>')); + }); + + // https://github.com/flutter/flutter/issues/67289 + testWidgets('InfoWindow content is clickable', (WidgetTester tester) async { + final markers = { + Marker( + markerId: MarkerId('1'), + infoWindow: InfoWindow( + title: 'title for test', + snippet: 'some snippet', + ), + ), + }; + + controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final content = + controller.markers[MarkerId('1')].infoWindow.content as HtmlElement; + + content.click(); + + final event = await stream.stream.first; + + expect(event, isA()); + expect((event as InfoWindowTapEvent).value, equals(MarkerId('1'))); + }); }); } diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md index 7f35c8490e1f..3425ff6c34b0 100644 --- a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* Update android compileSdkVersion to 29. + ## 1.0.0 * First published version. diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle index e6da1a0aebf5..2952c3b9c463 100755 --- a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/pubspec.yaml b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/pubspec.yaml index f94b5e505946..c978ce446714 100644 --- a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/pubspec.yaml +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/pubspec.yaml @@ -6,7 +6,7 @@ name: extension_google_sign_in_as_googleapis_auth description: A bridge package between google_sign_in and googleapis_auth, to create Authenticated Clients from google_sign_in user credentials. -version: 1.0.0 +version: 1.0.1 homepage: https://github.com/flutter/plugins/google_sign_in/extension_google_sign_in_as_googleapis_auth dependencies: diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 8de8a3badc1b..7d3dc2840c0a 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 @@ +## 4.5.5 + +* Update android compileSdkVersion to 29. + ## 4.5.4 * Keep handling deprecated Android v1 classes for backward compatibility. 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 144739559b5c..a1df23e7f92a 100755 --- 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 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 e6da1a0aebf5..2952c3b9c463 100755 --- 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 2b7aea7cc95c..26186de065d7 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -2,7 +2,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. homepage: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in -version: 4.5.4 +version: 4.5.5 flutter: plugin: diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index f8dbee9417cf..33ee46894cda 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.7+12 + +* Update android compileSdkVersion to 29. + ## 0.6.7+11 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/image_picker/image_picker/android/build.gradle b/packages/image_picker/image_picker/android/build.gradle index 493d72caf1a8..79d0e598d99f 100755 --- a/packages/image_picker/image_picker/android/build.gradle +++ b/packages/image_picker/image_picker/android/build.gradle @@ -25,7 +25,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 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 f4b1e02ede35..7b25d0746b86 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 28 + compileSdkVersion 29 testOptions.unitTests.includeAndroidResources = true lintOptions { diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 6eaa0d685bd8..baecbd2d3c38 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker -version: 0.6.7+11 +version: 0.6.7+12 flutter: plugin: diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md index 851e3b923f10..2193d0199b65 100644 --- a/packages/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.3.4+15 + +* Update android compileSdkVersion to 29. + +## 0.3.4+14 + +* Add test target to iOS example app Podfile + ## 0.3.4+13 * Android Code Inspection and Clean up. diff --git a/packages/in_app_purchase/android/build.gradle b/packages/in_app_purchase/android/build.gradle index 96163c0d20bd..2539f507ed26 100644 --- a/packages/in_app_purchase/android/build.gradle +++ b/packages/in_app_purchase/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/in_app_purchase/example/android/app/build.gradle b/packages/in_app_purchase/example/android/app/build.gradle index a383eb4a965b..261c7f0fe58e 100644 --- a/packages/in_app_purchase/example/android/app/build.gradle +++ b/packages/in_app_purchase/example/android/app/build.gradle @@ -63,7 +63,7 @@ android { } } - compileSdkVersion 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/in_app_purchase/example/ios/Podfile b/packages/in_app_purchase/example/ios/Podfile new file mode 100644 index 000000000000..7079e94dc672 --- /dev/null +++ b/packages/in_app_purchase/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 'in_app_purchase_pluginTests' do + inherit! :search_paths + + # Matches in_app_purchase test_spec dependency. + 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/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj index 65c38e4c31b4..1065ac201e4a 100644 --- a/packages/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,14 +9,11 @@ /* 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 */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 523686A0BE5A2D2269D4F386 /* libPods-in_app_purchase_pluginTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E20838C66ABCD8667B0BB95D /* libPods-in_app_purchase_pluginTests.a */; }; 688DE35121F2A5A100EA2684 /* TranslatorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 688DE35021F2A5A100EA2684 /* TranslatorTest.m */; }; 6896B34621E9363700D37AEF /* ProductRequestHandlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6896B34521E9363700D37AEF /* ProductRequestHandlerTest.m */; }; 6896B34C21EEB4B800D37AEF /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = 6896B34B21EEB4B800D37AEF /* Stubs.m */; }; 861D0D93B0757D95C8A69620 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B2AB6BE1D4E2232AB5D4A002 /* libPods-Runner.a */; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 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 */; }; @@ -44,8 +41,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -56,7 +51,6 @@ 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 = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 688DE35021F2A5A100EA2684 /* TranslatorTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TranslatorTest.m; path = ../../../ios/Tests/TranslatorTest.m; sourceTree = ""; }; 6896B34521E9363700D37AEF /* ProductRequestHandlerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ProductRequestHandlerTest.m; path = ../../../ios/Tests/ProductRequestHandlerTest.m; sourceTree = ""; }; 6896B34A21EEB4B800D37AEF /* Stubs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Stubs.h; path = ../../../ios/Tests/Stubs.h; sourceTree = ""; }; @@ -66,7 +60,6 @@ 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 = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; 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 = ""; }; @@ -77,9 +70,12 @@ A59001A421E69658004A3E5E /* in_app_purchase_pluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = in_app_purchase_pluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A59001A621E69658004A3E5E /* InAppPurchasePluginTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = InAppPurchasePluginTest.m; path = ../../../ios/Tests/InAppPurchasePluginTest.m; sourceTree = ""; }; A59001A821E69658004A3E5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + ACAF3B1D3B61187149C0FF81 /* Pods-in_app_purchase_pluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-in_app_purchase_pluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-in_app_purchase_pluginTests/Pods-in_app_purchase_pluginTests.release.xcconfig"; sourceTree = ""; }; B2AB6BE1D4E2232AB5D4A002 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BE95F46E12942F78BF67E55B /* 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 = ""; }; + CC2B3FFB29B2574DEDD718A6 /* Pods-in_app_purchase_pluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-in_app_purchase_pluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-in_app_purchase_pluginTests/Pods-in_app_purchase_pluginTests.debug.xcconfig"; sourceTree = ""; }; DE7EEEE26E27ACC04BA9951D /* 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 = ""; }; + E20838C66ABCD8667B0BB95D /* libPods-in_app_purchase_pluginTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-in_app_purchase_pluginTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F78AF3132342BC89008449C7 /* PaymentQueueTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PaymentQueueTest.m; path = ../../../ios/Tests/PaymentQueueTest.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -88,8 +84,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 861D0D93B0757D95C8A69620 /* libPods-Runner.a in Frameworks */, A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */, ); @@ -99,6 +93,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 523686A0BE5A2D2269D4F386 /* libPods-in_app_purchase_pluginTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -110,6 +105,8 @@ children = ( DE7EEEE26E27ACC04BA9951D /* Pods-Runner.debug.xcconfig */, BE95F46E12942F78BF67E55B /* Pods-Runner.release.xcconfig */, + CC2B3FFB29B2574DEDD718A6 /* Pods-in_app_purchase_pluginTests.debug.xcconfig */, + ACAF3B1D3B61187149C0FF81 /* Pods-in_app_purchase_pluginTests.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -117,9 +114,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -191,6 +186,7 @@ children = ( A5279297219369C600FF69E6 /* StoreKit.framework */, B2AB6BE1D4E2232AB5D4A002 /* libPods-Runner.a */, + E20838C66ABCD8667B0BB95D /* libPods-in_app_purchase_pluginTests.a */, ); name = Frameworks; sourceTree = ""; @@ -224,6 +220,7 @@ isa = PBXNativeTarget; buildConfigurationList = A59001AD21E69658004A3E5E /* Build configuration list for PBXNativeTarget "in_app_purchase_pluginTests" */; buildPhases = ( + 4EA84B170943DF9C4A2CF33C /* [CP] Check Pods Manifest.lock */, A59001A021E69658004A3E5E /* Sources */, A59001A121E69658004A3E5E /* Frameworks */, A59001A221E69658004A3E5E /* Resources */, @@ -316,7 +313,29 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 4EA84B170943DF9C4A2CF33C /* [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-in_app_purchase_pluginTests-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; }; 5DF63B80D489A62B306EA07A /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -356,9 +375,12 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -422,7 +444,6 @@ /* Begin XCBuildConfiguration section */ 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -479,7 +500,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -576,6 +596,7 @@ }; A59001AB21E69658004A3E5E /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = CC2B3FFB29B2574DEDD718A6 /* Pods-in_app_purchase_pluginTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -597,6 +618,7 @@ }; A59001AC21E69658004A3E5E /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = ACAF3B1D3B61187149C0FF81 /* Pods-in_app_purchase_pluginTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/packages/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/pubspec.yaml index 7d307cfef194..31c8350c351d 100644 --- a/packages/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/pubspec.yaml @@ -1,7 +1,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. homepage: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase -version: 0.3.4+13 +version: 0.3.4+15 dependencies: async: ^2.0.8 diff --git a/packages/integration_test/CHANGELOG.md b/packages/integration_test/CHANGELOG.md index 409af072d0e1..07ee671d77f9 100644 --- a/packages/integration_test/CHANGELOG.md +++ b/packages/integration_test/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.2+1 + +* Update android compileSdkVersion to 29. + ## 0.9.2 * Add `watchPerformance` for performance test. diff --git a/packages/integration_test/android/build.gradle b/packages/integration_test/android/build.gradle index 9f5a3896be0d..f35815281949 100644 --- a/packages/integration_test/android/build.gradle +++ b/packages/integration_test/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/integration_test/example/android/app/build.gradle b/packages/integration_test/example/android/app/build.gradle index 73bd5f4bc41e..3ef8aebce197 100644 --- a/packages/integration_test/example/android/app/build.gradle +++ b/packages/integration_test/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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/integration_test/pubspec.yaml b/packages/integration_test/pubspec.yaml index d9a27eb74424..44aa2c56790f 100644 --- a/packages/integration_test/pubspec.yaml +++ b/packages/integration_test/pubspec.yaml @@ -1,6 +1,6 @@ name: integration_test description: Runs tests that use the flutter_test API as integration tests. -version: 0.9.2 +version: 0.9.2+1 homepage: https://github.com/flutter/plugins/tree/master/packages/integration_test environment: diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md index a1dc7aaa4e16..57295ab32012 100644 --- a/packages/local_auth/CHANGELOG.md +++ b/packages/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.3+3 + +* Update android compileSdkVersion to 29. + ## 0.6.3+2 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/local_auth/example/android/app/build.gradle b/packages/local_auth/example/android/app/build.gradle index eaccbe3cd9f9..34b3fe2d69e3 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml index db34a461e0d8..9505c774e24d 100644 --- a/packages/local_auth/pubspec.yaml +++ b/packages/local_auth/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth description: Flutter plugin for Android and iOS device authentication sensors such as Fingerprint Reader and Touch ID. homepage: https://github.com/flutter/plugins/tree/master/packages/local_auth -version: 0.6.3+2 +version: 0.6.3+3 flutter: plugin: diff --git a/packages/package_info/CHANGELOG.md b/packages/package_info/CHANGELOG.md index f88c270d92a3..535232fbcd97 100644 --- a/packages/package_info/CHANGELOG.md +++ b/packages/package_info/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.3+1 + +* Update android compileSdkVersion to 29. + ## 0.4.3 * Update package:e2e -> package:integration_test diff --git a/packages/package_info/android/build.gradle b/packages/package_info/android/build.gradle index d05fa363d3c1..29453b53c6be 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/package_info/example/android/app/build.gradle b/packages/package_info/example/android/app/build.gradle index c24eba0fe81d..5099f3213fd8 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/package_info/pubspec.yaml b/packages/package_info/pubspec.yaml index 0705dd11d139..16349a619986 100644 --- a/packages/package_info/pubspec.yaml +++ b/packages/package_info/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/package_info # 0.4.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.4.3 +version: 0.4.3+1 flutter: plugin: diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index fe92462627df..6b1ccc78527b 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,15 @@ +## 1.6.22 + +* Switch to guava-android dependency instead of full guava. + +## 1.6.21 + +* Update android compileSdkVersion to 29. + +## 1.6.20 + +* Check in linux/ directory for example/ + ## 1.6.19 * Android implementation does path queries in the background thread rather than UI thread. diff --git a/packages/path_provider/path_provider/android/build.gradle b/packages/path_provider/path_provider/android/build.gradle index 91bedf7f29a3..9659efbc03d2 100644 --- a/packages/path_provider/path_provider/android/build.gradle +++ b/packages/path_provider/path_provider/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 @@ -41,6 +41,6 @@ android { dependencies { implementation 'androidx.annotation:annotation:1.1.0' - implementation 'com.google.guava:guava:20.0' + implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.12' } 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 0404c7203903..e7f1bfb111a2 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/path_provider/path_provider/example/linux/.gitignore b/packages/path_provider/path_provider/example/linux/.gitignore new file mode 100644 index 000000000000..d3896c98444f --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/path_provider/path_provider/example/linux/CMakeLists.txt b/packages/path_provider/path_provider/example/linux/CMakeLists.txt new file mode 100644 index 000000000000..279007eb351f --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "io.flutter.plugins.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +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() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +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. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/path_provider/path_provider/example/linux/flutter/CMakeLists.txt b/packages/path_provider/path_provider/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000000..4f48a7ced5f4 --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required(VERSION 3.10) + +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. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# 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/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID +) +add_dependencies(flutter 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. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) 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 new file mode 100644 index 000000000000..890de29bbab1 --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,7 @@ +// +// Generated file. Do not edit. +// + +#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 new file mode 100644 index 000000000000..9bf7478940c1 --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#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/linux/flutter/generated_plugins.cmake b/packages/path_provider/path_provider/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..51436ae8c982 --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux 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/path_provider/path_provider/example/linux/main.cc b/packages/path_provider/path_provider/example/linux/main.cc new file mode 100644 index 000000000000..10835acb58ed --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/main.cc @@ -0,0 +1,11 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + // Only X11 is currently supported. + // Wayland support is being developed: + // https://github.com/flutter/flutter/issues/57932. + gdk_set_allowed_backends("x11"); + + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/path_provider/path_provider/example/linux/my_application.cc b/packages/path_provider/path_provider/example/linux/my_application.cc new file mode 100644 index 000000000000..67ed0b9025b2 --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/my_application.cc @@ -0,0 +1,45 @@ +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new( + my_application_get_type(), "application-id", APPLICATION_ID, nullptr)); +} diff --git a/packages/path_provider/path_provider/example/linux/my_application.h b/packages/path_provider/path_provider/example/linux/my_application.h new file mode 100644 index 000000000000..72271d5e4170 --- /dev/null +++ b/packages/path_provider/path_provider/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 0eadc40637b0..c583efa0c8eb 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -1,7 +1,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. homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider -version: 1.6.19 +version: 1.6.22 flutter: plugin: diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index baec603b426f..21f336c5f0f2 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.1+1 + +* Check in linux/ directory for example/ + ## 0.1.1 - NOT PUBLISHED * Reverts changes on 0.1.0, which broke the tree. diff --git a/packages/path_provider/path_provider_linux/example/linux/.gitignore b/packages/path_provider/path_provider_linux/example/linux/.gitignore new file mode 100644 index 000000000000..d3896c98444f --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/path_provider/path_provider_linux/example/linux/CMakeLists.txt b/packages/path_provider/path_provider_linux/example/linux/CMakeLists.txt new file mode 100644 index 000000000000..290c3e841e82 --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "com.example.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +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() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +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. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/path_provider/path_provider_linux/example/linux/flutter/CMakeLists.txt b/packages/path_provider/path_provider_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000000..4f48a7ced5f4 --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required(VERSION 3.10) + +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. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# 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/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID +) +add_dependencies(flutter 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. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) 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 new file mode 100644 index 000000000000..890de29bbab1 --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,7 @@ +// +// Generated file. Do not edit. +// + +#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 new file mode 100644 index 000000000000..9bf7478940c1 --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#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_linux/example/linux/flutter/generated_plugins.cmake b/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..51436ae8c982 --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux 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/path_provider/path_provider_linux/example/linux/main.cc b/packages/path_provider/path_provider_linux/example/linux/main.cc new file mode 100644 index 000000000000..10835acb58ed --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/main.cc @@ -0,0 +1,11 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + // Only X11 is currently supported. + // Wayland support is being developed: + // https://github.com/flutter/flutter/issues/57932. + gdk_set_allowed_backends("x11"); + + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/path_provider/path_provider_linux/example/linux/my_application.cc b/packages/path_provider/path_provider_linux/example/linux/my_application.cc new file mode 100644 index 000000000000..67ed0b9025b2 --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/my_application.cc @@ -0,0 +1,45 @@ +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new( + my_application_get_type(), "application-id", APPLICATION_ID, nullptr)); +} diff --git a/packages/path_provider/path_provider_linux/example/linux/my_application.h b/packages/path_provider/path_provider_linux/example/linux/my_application.h new file mode 100644 index 000000000000..72271d5e4170 --- /dev/null +++ b/packages/path_provider/path_provider_linux/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index b0188fd2a4c7..feac1a6ec2f7 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 -version: 0.1.1 +version: 0.1.1+1 homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux flutter: diff --git a/packages/quick_actions/CHANGELOG.md b/packages/quick_actions/CHANGELOG.md index 49815fc68b29..0742d73f500a 100644 --- a/packages/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.0+10 + +* Update android compileSdkVersion to 29. + ## 0.4.0+9 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/quick_actions/android/build.gradle b/packages/quick_actions/android/build.gradle index 648b654dbfcd..12eff4444fba 100644 --- a/packages/quick_actions/android/build.gradle +++ b/packages/quick_actions/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/quick_actions/example/android/app/build.gradle b/packages/quick_actions/example/android/app/build.gradle index b8ca2db446f8..57de7f6e5e03 100644 --- a/packages/quick_actions/example/android/app/build.gradle +++ b/packages/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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/quick_actions/pubspec.yaml b/packages/quick_actions/pubspec.yaml index f612ecdafa63..187c5707ddea 100644 --- a/packages/quick_actions/pubspec.yaml +++ b/packages/quick_actions/pubspec.yaml @@ -2,7 +2,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. homepage: https://github.com/flutter/plugins/tree/master/packages/quick_actions -version: 0.4.0+9 +version: 0.4.0+10 flutter: plugin: diff --git a/packages/sensors/CHANGELOG.md b/packages/sensors/CHANGELOG.md index aa9f2bc4fbc5..a2693dd7ed0b 100644 --- a/packages/sensors/CHANGELOG.md +++ b/packages/sensors/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.2+6 + +* Update android compileSdkVersion to 29. + ## 0.4.2+5 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/sensors/android/build.gradle b/packages/sensors/android/build.gradle index c42795200e62..402af35ed338 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/sensors/example/android/app/build.gradle b/packages/sensors/example/android/app/build.gradle index 987def463562..d9c1e41f0759 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/sensors/pubspec.yaml b/packages/sensors/pubspec.yaml index 3e44f1e1ea8e..4e830d9d0480 100644 --- a/packages/sensors/pubspec.yaml +++ b/packages/sensors/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/sensors # 0.4.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.4.2+5 +version: 0.4.2+6 flutter: plugin: diff --git a/packages/share/CHANGELOG.md b/packages/share/CHANGELOG.md index f13e819b8b74..36445472557d 100644 --- a/packages/share/CHANGELOG.md +++ b/packages/share/CHANGELOG.md @@ -1,3 +1,8 @@ +## 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. diff --git a/packages/share/android/build.gradle b/packages/share/android/build.gradle index ffa1432fa17a..e5748ff2104d 100644 --- a/packages/share/android/build.gradle +++ b/packages/share/android/build.gradle @@ -1,6 +1,5 @@ group 'io.flutter.plugins.share' version '1.0-SNAPSHOT' -def args = ["-Xlint:deprecation","-Xlint:unchecked","-Werror"] buildscript { repositories { @@ -9,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.5.0' } } @@ -20,14 +19,10 @@ rootProject.allprojects { } } -project.getTasks().withType(JavaCompile){ - options.compilerArgs.addAll(args) -} - apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 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 index eb856bf572ee..be7280eea673 100644 --- 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 @@ -10,7 +10,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; -import android.os.Environment; import androidx.annotation.NonNull; import androidx.core.content.FileProvider; import java.io.File; @@ -166,7 +165,7 @@ private String getMimeTypeBase(String mimeType) { private boolean fileIsOnExternal(File file) { try { String filePath = file.getCanonicalPath(); - File externalDir = Environment.getExternalStorageDirectory(); + File externalDir = context.getExternalFilesDir(null); return externalDir != null && filePath.startsWith(externalDir.getCanonicalPath()); } catch (IOException e) { return false; diff --git a/packages/share/example/android/app/build.gradle b/packages/share/example/android/app/build.gradle index 5fca10f77210..5b7b30bbad26 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' @@ -34,7 +34,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.shareexample" minSdkVersion 16 - targetSdkVersion 28 + targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/share/example/android/build.gradle b/packages/share/example/android/build.gradle index 541636cc492a..e0d7ae2c11af 100644 --- a/packages/share/example/android/build.gradle +++ b/packages/share/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.5.0' } } diff --git a/packages/share/pubspec.yaml b/packages/share/pubspec.yaml index dc55ed765453..5a659019bda4 100644 --- a/packages/share/pubspec.yaml +++ b/packages/share/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/share # 0.6.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.6.5+2 +version: 0.6.5+3 flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 139df388ec73..268e1ab55d16 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.5.12+2 + +* Update android compileSdkVersion to 29. + +## 0.5.12+1 + +* Check in linux/ directory for example/ + ## 0.5.12 * Keep handling deprecated Android v1 classes for backward compatibility. diff --git a/packages/shared_preferences/shared_preferences/android/build.gradle b/packages/shared_preferences/shared_preferences/android/build.gradle index 8ba16e9dd900..5fc746c8e71f 100644 --- a/packages/shared_preferences/shared_preferences/android/build.gradle +++ b/packages/shared_preferences/shared_preferences/android/build.gradle @@ -30,7 +30,7 @@ allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 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 7a285ba704ab..58a6567a161c 100644 --- a/packages/shared_preferences/shared_preferences/example/android/app/build.gradle +++ b/packages/shared_preferences/shared_preferences/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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/shared_preferences/shared_preferences/example/linux/.gitignore b/packages/shared_preferences/shared_preferences/example/linux/.gitignore new file mode 100644 index 000000000000..d3896c98444f --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/shared_preferences/shared_preferences/example/linux/CMakeLists.txt b/packages/shared_preferences/shared_preferences/example/linux/CMakeLists.txt new file mode 100644 index 000000000000..279007eb351f --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "io.flutter.plugins.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +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() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +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. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/shared_preferences/shared_preferences/example/linux/flutter/CMakeLists.txt b/packages/shared_preferences/shared_preferences/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000000..4f48a7ced5f4 --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required(VERSION 3.10) + +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. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# 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/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID +) +add_dependencies(flutter 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. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) 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 new file mode 100644 index 000000000000..890de29bbab1 --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,7 @@ +// +// Generated file. Do not edit. +// + +#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 new file mode 100644 index 000000000000..9bf7478940c1 --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#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/linux/flutter/generated_plugins.cmake b/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..51436ae8c982 --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux 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/shared_preferences/shared_preferences/example/linux/main.cc b/packages/shared_preferences/shared_preferences/example/linux/main.cc new file mode 100644 index 000000000000..10835acb58ed --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/main.cc @@ -0,0 +1,11 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + // Only X11 is currently supported. + // Wayland support is being developed: + // https://github.com/flutter/flutter/issues/57932. + gdk_set_allowed_backends("x11"); + + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/shared_preferences/shared_preferences/example/linux/my_application.cc b/packages/shared_preferences/shared_preferences/example/linux/my_application.cc new file mode 100644 index 000000000000..67ed0b9025b2 --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/my_application.cc @@ -0,0 +1,45 @@ +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new( + my_application_get_type(), "application-id", APPLICATION_ID, nullptr)); +} diff --git a/packages/shared_preferences/shared_preferences/example/linux/my_application.h b/packages/shared_preferences/shared_preferences/example/linux/my_application.h new file mode 100644 index 000000000000..72271d5e4170 --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 91404eced74d..703ff4391bec 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/shared_prefere # 0.5.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.5.12 +version: 0.5.12+2 flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 5bfd6651edfa..e6d98c7ba333 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.2+3 + +* Check in linux/ directory for example/ + ## 0.0.2+2 * Bump the `file` package dependency to resolve dep conflicts with `flutter_driver`. diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/.gitignore b/packages/shared_preferences/shared_preferences_linux/example/linux/.gitignore new file mode 100644 index 000000000000..d3896c98444f --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8a19c46fd531..791515e669ad 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 -version: 0.0.2+2 +version: 0.0.2+3 homepage: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_linux flutter: diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 2b499a55427d..ab636c60137a 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,27 @@ +## 5.7.8 + +* Fixed a situation where an app would crash if the url_launcher’s `launch` method can’t find an app to open the provided url. It will now throw a clear Dart PlatformException. + +## 5.7.7 + +* Introduce the Link widget with an implementation for native platforms. + +## 5.7.6 + +* Suppress deprecation warning on the `shouldOverrideUrlLoading` method on Android of the `FlutterWebChromeClient` class. + +## 5.7.5 + +* Improved documentation of the `headers` parameter. + +## 5.7.4 + +* Update android compileSdkVersion to 29. + +## 5.7.3 + +* Check in linux/ directory for example/ + ## 5.7.2 * Add API documentation explaining the [canLaunch] method returns `false` if package visibility (Android API 30) is not managed correctly. diff --git a/packages/url_launcher/url_launcher/android/build.gradle b/packages/url_launcher/url_launcher/android/build.gradle index c02b29a5814f..2ddf8e470422 100644 --- a/packages/url_launcher/url_launcher/android/build.gradle +++ b/packages/url_launcher/url_launcher/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 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/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java index 0b90dfaf32bc..2ca6b7ce3fde 100644 --- a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java +++ b/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java @@ -92,6 +92,11 @@ private void onLaunch(MethodCall call, Result result, String url) { if (launchStatus == LaunchStatus.NO_ACTIVITY) { result.error("NO_ACTIVITY", "Launching a URL requires a foreground activity.", null); + } else if (launchStatus == LaunchStatus.ACTIVITY_NOT_FOUND) { + result.error( + "ACTIVITY_NOT_FOUND", + String.format("No Activity found to handle intent { %s }", url), + null); } else { result.success(true); } 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 40f2a51f1db2..44ecc337c43b 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 @@ -1,6 +1,7 @@ package io.flutter.plugins.urllauncher; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -48,7 +49,8 @@ boolean canLaunch(String url) { * @param enableJavaScript Only used if {@param useWebView} is true. Enables JS in the WebView. * @param enableDomStorage Only used if {@param useWebView} is true. Enables DOM storage in the * @return {@link LaunchStatus#NO_ACTIVITY} if there's no available {@code applicationContext}. - * {@link LaunchStatus#OK} otherwise. + * {@link LaunchStatus#ACTIVITY_NOT_FOUND} if there's no activity found to handle {@code + * launchIntent}. {@link LaunchStatus#OK} otherwise. */ LaunchStatus launch( String url, @@ -72,7 +74,12 @@ LaunchStatus launch( .putExtra(Browser.EXTRA_HEADERS, headersBundle); } - activity.startActivity(launchIntent); + try { + activity.startActivity(launchIntent); + } catch (ActivityNotFoundException e) { + return LaunchStatus.ACTIVITY_NOT_FOUND; + } + return LaunchStatus.OK; } @@ -87,5 +94,7 @@ enum LaunchStatus { OK, /** No activity was found to launch. */ NO_ACTIVITY, + /** No Activity found that can handle given intent. */ + ACTIVITY_NOT_FOUND, } } 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/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java index 98c5613e6c62..7c6978fd657d 100644 --- a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java +++ b/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java @@ -86,6 +86,11 @@ public boolean shouldOverrideUrlLoading( return true; } + /* + * This method is deprecated in API 24. Still overridden to support + * earlier Android versions. + */ + @SuppressWarnings("deprecation") @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { webview.loadUrl(url); 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/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java index 63ce46f6d0cb..e759dedd5f75 100644 --- a/packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java +++ b/packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java @@ -8,6 +8,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.os.Bundle; import androidx.test.core.app.ApplicationProvider; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler; @@ -105,6 +106,95 @@ public void onMethodCall_canLaunchReturnsFalse() { verify(result, times(1)).success(false); } + @Test + public void onMethodCall_launchReturnsNoActivityError() { + // Setup mock objects + urlLauncher = mock(UrlLauncher.class); + Result result = mock(Result.class); + // Setup expected values + String url = "foo"; + boolean useWebView = false; + boolean enableJavaScript = false; + boolean enableDomStorage = false; + // Setup arguments map send on the method channel + Map args = new HashMap<>(); + args.put("url", url); + args.put("useWebView", useWebView); + args.put("enableJavaScript", enableJavaScript); + args.put("enableDomStorage", enableDomStorage); + args.put("headers", new HashMap<>()); + // Mock the launch method on the urlLauncher class + when(urlLauncher.launch( + eq(url), any(Bundle.class), eq(useWebView), eq(enableJavaScript), eq(enableDomStorage))) + .thenReturn(UrlLauncher.LaunchStatus.NO_ACTIVITY); + // Act by calling the "launch" method on the method channel + methodCallHandler = new MethodCallHandlerImpl(urlLauncher); + methodCallHandler.onMethodCall(new MethodCall("launch", args), result); + // Verify the results and assert + verify(result, times(1)) + .error("NO_ACTIVITY", "Launching a URL requires a foreground activity.", null); + } + + @Test + public void onMethodCall_launchReturnsActivityNotFoundError() { + // Setup mock objects + urlLauncher = mock(UrlLauncher.class); + Result result = mock(Result.class); + // Setup expected values + String url = "foo"; + boolean useWebView = false; + boolean enableJavaScript = false; + boolean enableDomStorage = false; + // Setup arguments map send on the method channel + Map args = new HashMap<>(); + args.put("url", url); + args.put("useWebView", useWebView); + args.put("enableJavaScript", enableJavaScript); + args.put("enableDomStorage", enableDomStorage); + args.put("headers", new HashMap<>()); + // Mock the launch method on the urlLauncher class + when(urlLauncher.launch( + eq(url), any(Bundle.class), eq(useWebView), eq(enableJavaScript), eq(enableDomStorage))) + .thenReturn(UrlLauncher.LaunchStatus.ACTIVITY_NOT_FOUND); + // Act by calling the "launch" method on the method channel + methodCallHandler = new MethodCallHandlerImpl(urlLauncher); + methodCallHandler.onMethodCall(new MethodCall("launch", args), result); + // Verify the results and assert + verify(result, times(1)) + .error( + "ACTIVITY_NOT_FOUND", + String.format("No Activity found to handle intent { %s }", url), + null); + } + + @Test + public void onMethodCall_launchReturnsTrue() { + // Setup mock objects + urlLauncher = mock(UrlLauncher.class); + Result result = mock(Result.class); + // Setup expected values + String url = "foo"; + boolean useWebView = false; + boolean enableJavaScript = false; + boolean enableDomStorage = false; + // Setup arguments map send on the method channel + Map args = new HashMap<>(); + args.put("url", url); + args.put("useWebView", useWebView); + args.put("enableJavaScript", enableJavaScript); + args.put("enableDomStorage", enableDomStorage); + args.put("headers", new HashMap<>()); + // Mock the launch method on the urlLauncher class + when(urlLauncher.launch( + eq(url), any(Bundle.class), eq(useWebView), eq(enableJavaScript), eq(enableDomStorage))) + .thenReturn(UrlLauncher.LaunchStatus.OK); + // Act by calling the "launch" method on the method channel + methodCallHandler = new MethodCallHandlerImpl(urlLauncher); + methodCallHandler.onMethodCall(new MethodCall("launch", args), result); + // Verify the results and assert + verify(result, times(1)).success(true); + } + @Test public void onMethodCall_closeWebView() { urlLauncher = mock(UrlLauncher.class); 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 7a6cf5df0d33..4620c8963b47 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/url_launcher/url_launcher/example/linux/.gitignore b/packages/url_launcher/url_launcher/example/linux/.gitignore new file mode 100644 index 000000000000..d3896c98444f --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/url_launcher/url_launcher/lib/link.dart b/packages/url_launcher/url_launcher/lib/link.dart new file mode 100644 index 000000000000..ac1d4064d10f --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/link.dart @@ -0,0 +1,7 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/link.dart' show Link; +export 'package:url_launcher_platform_interface/link.dart' + show FollowLink, LinkTarget, LinkWidgetBuilder; diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart new file mode 100644 index 000000000000..bd54789accfb --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -0,0 +1,132 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +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'; + +/// A widget that renders a real link on the web, and uses WebViews in native +/// platforms to open links. +/// +/// Example link to an external URL: +/// +/// ```dart +/// Link( +/// uri: Uri.parse('https://flutter.dev'), +/// builder: (BuildContext context, FollowLink followLink) => RaisedButton( +/// onPressed: followLink, +/// // ... other properties here ... +/// )}, +/// ); +/// ``` +/// +/// Example link to a route name within the app: +/// +/// ```dart +/// Link( +/// uri: Uri.parse('/home'), +/// builder: (BuildContext context, FollowLink followLink) => RaisedButton( +/// onPressed: followLink, +/// // ... other properties here ... +/// )}, +/// ); +/// ``` +class Link extends StatelessWidget implements LinkInfo { + /// Called at build time to construct the widget tree under the link. + final LinkWidgetBuilder builder; + + /// The destination that this link leads to. + final Uri uri; + + /// The target indicating where to open the link. + final LinkTarget target; + + /// Whether the link is disabled or not. + 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, + LinkTarget target, + @required this.builder, + }) : target = target ?? LinkTarget.defaultTarget, + super(key: key); + + LinkDelegate get _effectiveDelegate { + return UrlLauncherPlatform.instance.linkDelegate ?? + DefaultLinkDelegate.create; + } + + @override + Widget build(BuildContext context) { + return _effectiveDelegate(this); + } +} + +/// The default delegate used on non-web platforms. +/// +/// For external URIs, it uses url_launche APIs. For app route names, it uses +/// 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); + + /// Given a [link], creates an instance of [DefaultLinkDelegate]. + /// + /// This is a static method so it can be used as a tear-off. + static DefaultLinkDelegate create(LinkInfo link) { + return DefaultLinkDelegate(link); + } + + /// Information about the link built by the app. + final LinkInfo link; + + bool get _useWebView { + if (link.target == LinkTarget.self) return true; + if (link.target == LinkTarget.blank) return false; + return null; + } + + Future _followLink(BuildContext context) async { + if (!link.uri.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. + final String routeName = link.uri.toString(); + return pushRouteNameToFramework(context, routeName); + } + + // 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, + ); + } else { + FlutterError.reportError(FlutterErrorDetails( + exception: 'Could not launch link $urlString', + stack: StackTrace.current, + library: 'url_launcher', + context: ErrorDescription('during launching a link'), + )); + } + return Future.value(null); + } + + @override + Widget build(BuildContext context) { + return link.builder( + context, + link.isDisabled ? null : () => _followLink(context), + ); + } +} diff --git a/packages/url_launcher/url_launcher/lib/url_launcher.dart b/packages/url_launcher/url_launcher/lib/url_launcher.dart index ccc6833f0941..25aa623a590f 100644 --- a/packages/url_launcher/url_launcher/lib/url_launcher.dart +++ b/packages/url_launcher/url_launcher/lib/url_launcher.dart @@ -44,6 +44,9 @@ import 'package:url_launcher_platform_interface/url_launcher_platform_interface. /// [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. diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 0b6a50f8b8fd..7ffc20c6e5fe 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher description: Flutter plugin for launching a URL on Android and iOS. Supports web, phone, SMS, and email schemes. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher -version: 5.7.2 +version: 5.7.8 flutter: plugin: @@ -24,13 +24,13 @@ flutter: dependencies: flutter: sdk: flutter - url_launcher_platform_interface: ^1.0.8 + url_launcher_platform_interface: ^1.0.9 # 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. # TODO(amirh): Revisit this (either update this part in the design or the pub tool). # https://github.com/flutter/flutter/issues/46264 - url_launcher_web: ^0.1.3 + url_launcher_web: ^0.1.5 url_launcher_linux: ^0.0.1 url_launcher_macos: ^0.0.1 url_launcher_windows: ^0.0.1 diff --git a/packages/url_launcher/url_launcher/test/link_test.dart b/packages/url_launcher/url_launcher/test/link_test.dart new file mode 100644 index 000000000000..d525153dc0a0 --- /dev/null +++ b/packages/url_launcher/url_launcher/test/link_test.dart @@ -0,0 +1,272 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.8 + +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:flutter/services.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:url_launcher/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +final MethodCodec _codec = const JSONMethodCodec(); + +void main() { + final MockUrlLauncher mock = MockUrlLauncher(); + UrlLauncherPlatform.instance = mock; + + PlatformMessageCallback realOnPlatformMessage; + setUp(() { + realOnPlatformMessage = window.onPlatformMessage; + }); + tearDown(() { + window.onPlatformMessage = realOnPlatformMessage; + }); + + group('$Link', () { + testWidgets('handles null uri correctly', (WidgetTester tester) async { + bool isBuilt = false; + FollowLink followLink; + + final Link link = Link( + uri: null, + builder: (BuildContext context, FollowLink followLink2) { + isBuilt = true; + followLink = followLink2; + return Container(); + }, + ); + await tester.pumpWidget(link); + + expect(link.isDisabled, isTrue); + expect(isBuilt, isTrue); + expect(followLink, isNull); + }); + + testWidgets('calls url_launcher for external URLs with blank target', + (WidgetTester tester) async { + FollowLink followLink; + + await tester.pumpWidget(Link( + uri: Uri.parse('http://example.com/foobar'), + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink followLink2) { + followLink = followLink2; + return Container(); + }, + )); + + when(mock.canLaunch('http://example.com/foobar')) + .thenAnswer((realInvocation) => Future.value(true)); + clearInteractions(mock); + await followLink(); + + verifyInOrder([ + mock.canLaunch('http://example.com/foobar'), + mock.launch( + 'http://example.com/foobar', + useSafariVC: false, + useWebView: false, + universalLinksOnly: false, + enableJavaScript: false, + enableDomStorage: false, + headers: {}, + ) + ]); + }); + + testWidgets('calls url_launcher for external URLs with self target', + (WidgetTester tester) async { + FollowLink followLink; + + await tester.pumpWidget(Link( + uri: Uri.parse('http://example.com/foobar'), + target: LinkTarget.self, + builder: (BuildContext context, FollowLink followLink2) { + followLink = followLink2; + return Container(); + }, + )); + + when(mock.canLaunch('http://example.com/foobar')) + .thenAnswer((realInvocation) => Future.value(true)); + clearInteractions(mock); + await followLink(); + + verifyInOrder([ + mock.canLaunch('http://example.com/foobar'), + mock.launch( + 'http://example.com/foobar', + useSafariVC: true, + useWebView: true, + universalLinksOnly: false, + enableJavaScript: false, + enableDomStorage: false, + headers: {}, + ) + ]); + }); + + testWidgets('sends navigation platform messages for internal route names', + (WidgetTester tester) async { + // Intercept messages sent to the engine. + final List engineCalls = []; + SystemChannels.navigation.setMockMethodCallHandler((MethodCall call) { + engineCalls.add(call); + return Future.value(); + }); + + // Intercept messages sent to the framework. + final List frameworkCalls = []; + window.onPlatformMessage = ( + String name, + ByteData data, + PlatformMessageResponseCallback callback, + ) { + frameworkCalls.add(_codec.decodeMethodCall(data)); + realOnPlatformMessage(name, data, callback); + }; + + final Uri uri = Uri.parse('/foo/bar'); + FollowLink followLink; + + await tester.pumpWidget(MaterialApp( + routes: { + '/': (BuildContext context) => Link( + uri: uri, + builder: (BuildContext context, FollowLink followLink2) { + followLink = followLink2; + return Container(); + }, + ), + '/foo/bar': (BuildContext context) => Container(), + }, + )); + + engineCalls.clear(); + frameworkCalls.clear(); + clearInteractions(mock); + await followLink(); + + // Shouldn't use url_launcher when uri is an internal route name. + verifyZeroInteractions(mock); + + // A message should've been sent to the engine (by the Navigator, not by + // the Link widget). + // + // Even though this message isn't being sent by Link, we still want to + // have a test for it because we rely on it for Link to work correctly. + expect(engineCalls, hasLength(1)); + expect( + engineCalls.single, + isMethodCall('routeUpdated', arguments: { + 'previousRouteName': '/', + 'routeName': '/foo/bar', + }), + ); + + // Pushes route to the framework. + expect(frameworkCalls, hasLength(1)); + expect( + frameworkCalls.single, + isMethodCall('pushRoute', arguments: '/foo/bar'), + ); + }); + + testWidgets('sends router platform messages for internal route names', + (WidgetTester tester) async { + // Intercept messages sent to the engine. + final List engineCalls = []; + SystemChannels.navigation.setMockMethodCallHandler((MethodCall call) { + engineCalls.add(call); + return Future.value(); + }); + + // Intercept messages sent to the framework. + final List frameworkCalls = []; + window.onPlatformMessage = ( + String name, + ByteData data, + PlatformMessageResponseCallback callback, + ) { + frameworkCalls.add(_codec.decodeMethodCall(data)); + realOnPlatformMessage(name, data, callback); + }; + + final Uri uri = Uri.parse('/foo/bar'); + FollowLink followLink; + + final Link link = Link( + uri: uri, + builder: (BuildContext context, FollowLink followLink2) { + followLink = followLink2; + return Container(); + }, + ); + await tester.pumpWidget(MaterialApp.router( + routeInformationParser: MockRouteInformationParser(), + routerDelegate: MockRouterDelegate( + builder: (BuildContext context) => link, + ), + )); + + engineCalls.clear(); + frameworkCalls.clear(); + clearInteractions(mock); + await followLink(); + + // Shouldn't use url_launcher when uri is an internal route name. + verifyZeroInteractions(mock); + + // Sends route information update to the engine. + expect(engineCalls, hasLength(1)); + expect( + engineCalls.single, + isMethodCall('routeInformationUpdated', arguments: { + 'location': '/foo/bar', + 'state': null + }), + ); + + // Also pushes route information update to the Router. + expect(frameworkCalls, hasLength(1)); + expect( + frameworkCalls.single, + isMethodCall( + 'pushRouteInformation', + arguments: { + 'location': '/foo/bar', + 'state': null, + }, + ), + ); + }); + }); +} + +class MockUrlLauncher extends Mock + with MockPlatformInterfaceMixin + implements UrlLauncherPlatform {} + +class MockRouteInformationParser extends Mock + implements RouteInformationParser { + @override + Future parseRouteInformation(RouteInformation routeInformation) { + return Future.value(true); + } +} + +class MockRouterDelegate extends Mock implements RouterDelegate { + MockRouterDelegate({@required this.builder}); + + final WidgetBuilder builder; + + @override + Widget build(BuildContext context) { + return builder(context); + } +} diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index e03b1d387c55..c6a00423e16a 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.0.1+3 + +* Add a missing include. + +## 0.0.1+2 + +* Check in linux/ directory for example/ + # 0.0.1+1 * README update for endorsement by url_launcher. 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 592bb965e83f..8ef1a8aedbc2 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 @@ -7,6 +7,8 @@ #include #include +#include + // See url_launcher_channel.dart for documentation. const char kChannelName[] = "plugins.flutter.io/url_launcher"; const char kBadArgumentsError[] = "Bad Arguments"; diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index 74c2968aba69..c4f6f8c70a73 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. -version: 0.0.1+1 +version: 0.0.1+3 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux flutter: @@ -16,4 +16,3 @@ environment: dependencies: flutter: sdk: flutter - diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md index 768042be4cef..10057e147adf 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 @@ +## 1.0.9 + +* Laid the groundwork for introducing a Link widget. + ## 1.0.8 * Added webOnlyWindowName parameter diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart new file mode 100644 index 000000000000..425dc886d29f --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -0,0 +1,113 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +/// Signature for a function provided by the [Link] widget that instructs it to +/// follow the link. +typedef FollowLink = Future Function(); + +/// Signature for a builder function passed to the [Link] widget to construct +/// the widget tree under it. +typedef LinkWidgetBuilder = Widget Function( + BuildContext context, + FollowLink followLink, +); + +/// Signature for a delegate function to build the [Link] widget. +typedef LinkDelegate = Widget Function(LinkInfo linkWidget); + +final MethodCodec _codec = const JSONMethodCodec(); + +/// Defines where a Link URL should be open. +/// +/// This is a class instead of an enum to allow future customizability e.g. +/// opening a link in a specific iframe. +class LinkTarget { + /// Const private constructor with a [debugLabel] to allow the creation of + /// multiple distinct const instances. + const LinkTarget._({this.debugLabel}); + + /// Used to distinguish multiple const instances of [LinkTarget]. + final String debugLabel; + + /// Use the default target for each platform. + /// + /// On Android, the default is [blank]. On the web, the default is [self]. + /// + /// iOS, on the other hand, defaults to [self] for web URLs, and [blank] for + /// non-web URLs. + static const 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'); + + /// 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'); +} + +/// Encapsulates all the information necessary to build a Link widget. +abstract class LinkInfo { + /// Called at build time to construct the widget tree under the link. + LinkWidgetBuilder get builder; + + /// The destination that this link leads to. + Uri get uri; + + /// The target indicating where to open the link. + LinkTarget get target; + + /// Whether the link is disabled or not. + bool get isDisabled; +} + +/// Pushes the [routeName] into Flutter's navigation system via a platform +/// message. +Future pushRouteNameToFramework( + BuildContext context, + String routeName, { + @visibleForTesting bool debugForceRouter = false, +}) { + final Completer completer = Completer(); + if (debugForceRouter || _hasRouter(context)) { + SystemNavigator.routeInformationUpdated(location: routeName); + window.onPlatformMessage( + 'flutter/navigation', + _codec.encodeMethodCall( + MethodCall('pushRouteInformation', { + 'location': routeName, + 'state': null, + }), + ), + completer.complete, + ); + } else { + window.onPlatformMessage( + 'flutter/navigation', + _codec.encodeMethodCall(MethodCall('pushRoute', routeName)), + completer.complete, + ); + } + return completer.future; +} + +bool _hasRouter(BuildContext context) { + try { + return Router.of(context) != null; + } on AssertionError { + // When a `Router` can't be found, an assertion error is thrown. + return false; + } +} 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 f87630ee3045..ac5bfa230289 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 @@ -7,12 +7,16 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart' show required; +import 'link.dart'; import 'url_launcher_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/url_launcher'); /// An implementation of [UrlLauncherPlatform] that uses method channels. class MethodChannelUrlLauncher extends UrlLauncherPlatform { + @override + final LinkDelegate linkDelegate = null; + @override Future canLaunch(String url) { return _channel.invokeMethod( 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 1de5742c1f6f..75002ff9eb4d 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 @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:meta/meta.dart' show required; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:url_launcher_platform_interface/link.dart'; import 'method_channel_url_launcher.dart'; @@ -38,6 +39,9 @@ abstract class UrlLauncherPlatform extends PlatformInterface { _instance = instance; } + /// The delegate used by the Link widget to build itself. + LinkDelegate get linkDelegate; + /// Returns `true` if this platform is able to launch [url]. Future canLaunch(String url) { throw UnimplementedError('canLaunch() has not been implemented.'); diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index 0c4096278bcb..ce0fdd936c9a 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -3,7 +3,7 @@ description: A common platform interface for the url_launcher plugin. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface # 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.8 +version: 1.0.9 dependencies: flutter: @@ -19,4 +19,4 @@ dev_dependencies: environment: sdk: ">=2.1.0 <3.0.0" - flutter: ">=1.9.1+hotfix.4 <2.0.0" + flutter: ">=1.22.0 <2.0.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 new file mode 100644 index 000000000000..99a885ccc179 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart @@ -0,0 +1,71 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:mockito/mockito.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:url_launcher_platform_interface/link.dart'; + +final MethodCodec _codec = const JSONMethodCodec(); + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + PlatformMessageCallback oldHandler; + MethodCall lastCall; + + setUp(() { + oldHandler = window.onPlatformMessage; + window.onPlatformMessage = ( + String name, + ByteData data, + PlatformMessageResponseCallback callback, + ) { + lastCall = _codec.decodeMethodCall(data); + callback(_codec.encodeSuccessEnvelope(true)); + }; + }); + + tearDown(() { + window.onPlatformMessage = oldHandler; + }); + + test('pushRouteNameToFramework() calls pushRoute when no Router', () async { + await pushRouteNameToFramework(CustomBuildContext(), '/foo/bar'); + expect( + lastCall, + isMethodCall( + 'pushRoute', + arguments: '/foo/bar', + ), + ); + }); + + test( + 'pushRouteNameToFramework() calls pushRouteInformation when Router exists', + () async { + await pushRouteNameToFramework( + CustomBuildContext(), + '/foo/bar', + debugForceRouter: true, + ); + expect( + lastCall, + isMethodCall( + 'pushRouteInformation', + arguments: { + 'location': '/foo/bar', + 'state': null, + }, + ), + ); + }, + ); +} + +class CustomBuildContext extends Mock implements BuildContext {} 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 628ab48498ec..d88f53ad58d0 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 @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.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'; @@ -286,4 +287,7 @@ class UrlLauncherPlatformMock extends Mock class ImplementsUrlLauncherPlatform extends Mock implements UrlLauncherPlatform {} -class ExtendsUrlLauncherPlatform extends UrlLauncherPlatform {} +class ExtendsUrlLauncherPlatform extends UrlLauncherPlatform { + @override + final LinkDelegate linkDelegate = null; +} diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index 456d458834bf..e7abd1301c88 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.5 + +- Added the web implementation of the Link widget. + # 0.1.4+2 - Move `lib/third_party` to `lib/src/third_party`. diff --git a/packages/url_launcher/url_launcher_web/analysis_options.yaml b/packages/url_launcher/url_launcher_web/analysis_options.yaml new file mode 100644 index 000000000000..443b16551ec9 --- /dev/null +++ b/packages/url_launcher/url_launcher_web/analysis_options.yaml @@ -0,0 +1,10 @@ +# This is a temporary file to allow us to unblock the flutter/plugins repo CI. +# It disables some of lints that were disabled inline. Disabling lints inline +# is no longer possible, so this file is required. +# TODO(ditman) https://github.com/flutter/flutter/issues/55000 (clean this up) + +include: ../../../analysis_options.yaml + +analyzer: + errors: + undefined_prefixed_name: ignore diff --git a/packages/url_launcher/url_launcher_web/lib/src/link.dart b/packages/url_launcher/url_launcher_web/lib/src/link.dart new file mode 100644 index 000000000000..e8a6d68348bb --- /dev/null +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -0,0 +1,295 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; + +import 'package:url_launcher_platform_interface/link.dart'; + +/// The unique identifier for the view type to be used for link platform views. +const String linkViewType = '__url_launcher::link'; + +/// The name of the property used to set the viewId on the DOM element. +const String linkViewIdProperty = '__url_launcher::link::viewId'; + +/// Signature for a function that takes a unique [id] and creates an HTML element. +typedef HtmlViewFactory = html.Element Function(int viewId); + +/// Factory that returns the link DOM element for each unique view id. +HtmlViewFactory get linkViewFactory => LinkViewController._viewFactory; + +/// The delegate for building the [Link] widget on the web. +/// +/// 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); + + /// Information about the link built by the app. + final LinkInfo link; + + @override + WebLinkDelegateState createState() => WebLinkDelegateState(); +} + +/// The link delegate used on the web platform. +/// +/// For external URIs, it lets the browser do its thing. For app route names, it +/// pushes the route name to the framework. +class WebLinkDelegateState extends State { + LinkViewController _controller; + + @override + void didUpdateWidget(WebLinkDelegate oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.link.uri != oldWidget.link.uri) { + _controller?.setUri(widget.link.uri); + } + if (widget.link.target != oldWidget.link.target) { + _controller?.setTarget(widget.link.target); + } + } + + Future _followLink() { + LinkViewController.registerHitTest(_controller); + return Future.value(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + widget.link.builder( + context, + widget.link.isDisabled ? null : _followLink, + ), + Positioned.fill( + child: PlatformViewLink( + viewType: linkViewType, + onCreatePlatformView: (PlatformViewCreationParams params) { + _controller = LinkViewController.fromParams(params, context); + return _controller + ..setUri(widget.link.uri) + ..setTarget(widget.link.target); + }, + surfaceFactory: + (BuildContext context, PlatformViewController controller) { + return PlatformViewSurface( + controller: controller, + gestureRecognizers: + Set>(), + hitTestBehavior: PlatformViewHitTestBehavior.transparent, + ); + }, + ), + ), + ], + ); + } +} + +/// Controls link views. +class LinkViewController extends PlatformViewController { + /// Creates a [LinkViewController] instance with the unique [viewId]. + LinkViewController(this.viewId, this.context) { + if (_instances.isEmpty) { + // This is the first controller being created, attach the global click + // listener. + _clickSubscription = html.window.onClick.listen(_onGlobalClick); + } + _instances[viewId] = this; + } + + /// Creates and initializes a [LinkViewController] instance with the given + /// platform view [params]. + factory LinkViewController.fromParams( + PlatformViewCreationParams params, + BuildContext context, + ) { + final int viewId = params.id; + final LinkViewController controller = LinkViewController(viewId, context); + controller._initialize().then((_) { + params.onPlatformViewCreated(viewId); + }); + return controller; + } + + static Map _instances = {}; + + static html.Element _viewFactory(int viewId) { + return _instances[viewId]?._element; + } + + static int _hitTestedViewId; + + static StreamSubscription _clickSubscription; + + static void _onGlobalClick(html.MouseEvent event) { + final int viewId = getViewIdFromTarget(event); + _instances[viewId]?._onDomClick(event); + // After the DOM click event has been received, clean up the hit test state + // so we can start fresh on the next click. + unregisterHitTest(); + } + + /// Call this method to indicate that a hit test has been registered for the + /// given [controller]. + /// + /// The [onClick] callback is invoked when the anchor element receives a + /// `click` from the browser. + static void registerHitTest(LinkViewController controller) { + _hitTestedViewId = controller.viewId; + } + + /// Removes all information about previously registered hit tests. + static void unregisterHitTest() { + _hitTestedViewId = null; + } + + @override + final int viewId; + + /// The context of the [Link] widget that created this controller. + final BuildContext context; + + html.Element _element; + bool get _isInitialized => _element != null; + + Future _initialize() async { + _element = html.Element.tag('a'); + setProperty(_element, linkViewIdProperty, viewId); + _element.style + ..opacity = '0' + ..display = 'block' + ..cursor = 'unset'; + + // This is recommended on MDN: + // - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target + _element.setAttribute('rel', 'noreferrer noopener'); + + final Map args = { + 'id': viewId, + 'viewType': linkViewType, + }; + await SystemChannels.platform_views.invokeMethod('create', args); + } + + void _onDomClick(html.MouseEvent event) { + final bool isHitTested = _hitTestedViewId == viewId; + if (!isHitTested) { + // There was no hit test registered for this click. This means the click + // landed on the anchor element but not on the underlying widget. In this + // case, we prevent the browser from following the click. + event.preventDefault(); + return; + } + + if (_uri.hasScheme) { + // External links will be handled by the browser, so we don't have to do + // anything. + return; + } + + // 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. + event.preventDefault(); + final String routeName = _uri.toString(); + pushRouteNameToFramework(context, routeName); + } + + Uri _uri; + + /// Set the [Uri] value for this link. + void setUri(Uri uri) { + assert(_isInitialized); + _uri = uri; + if (uri == null) { + _element.removeAttribute('href'); + } else { + _element.setAttribute('href', uri.toString()); + } + } + + /// Set the [LinkTarget] value for this link. + void setTarget(LinkTarget target) { + assert(_isInitialized); + _element.setAttribute('target', _getHtmlTarget(target)); + } + + String _getHtmlTarget(LinkTarget target) { + switch (target) { + case LinkTarget.defaultTarget: + case LinkTarget.self: + return '_self'; + case LinkTarget.blank: + return '_blank'; + default: + throw Exception('Unknown LinkTarget value $target.'); + } + } + + @override + Future clearFocus() async { + // Currently this does nothing on Flutter Web. + // TODO(het): Implement this. See https://github.com/flutter/flutter/issues/39496 + } + + @override + Future dispatchPointerEvent(PointerEvent event) async { + // We do not dispatch pointer events to HTML views because they may contain + // cross-origin iframes, which only accept user-generated events. + } + + @override + Future dispose() async { + if (_isInitialized) { + assert(_instances[viewId] == this); + _instances.remove(viewId); + if (_instances.isEmpty) { + await _clickSubscription.cancel(); + } + await SystemChannels.platform_views.invokeMethod('dispose', viewId); + } + } +} + +/// Finds the view id of the DOM element targeted by the [event]. +int getViewIdFromTarget(html.Event event) { + final html.Element linkElement = getLinkElementFromTarget(event); + if (linkElement != null) { + return getProperty(linkElement, linkViewIdProperty); + } + return null; +} + +/// Finds the targeted DOM element by the [event]. +/// +/// It handles the case where the target element is inside a shadow DOM too. +html.Element getLinkElementFromTarget(html.Event event) { + final html.Element target = event.target; + if (isLinkElement(target)) { + return target; + } + if (target.shadowRoot != null) { + final html.Element child = target.shadowRoot.lastChild; + if (isLinkElement(child)) { + return child; + } + } + return null; +} + +/// Checks if the given [element] is a link that was created by +/// [LinkViewController]. +bool isLinkElement(html.Element element) { + return element.tagName == 'A' && hasProperty(element, linkViewIdProperty); +} 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 093e06a4d8ed..e7367b3a2f6d 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,11 +4,15 @@ import 'dart:async'; import 'dart:html' as html; +// ignore: undefined_shown_name +import 'dart:ui' as ui show platformViewRegistry; 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'; +import 'src/link.dart'; import 'src/third_party/platform_detect/browser.dart'; const _safariTargetTopSchemes = { @@ -43,6 +47,12 @@ 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); + } + + @override + LinkDelegate get linkDelegate { + return (LinkInfo linkInfo) => WebLinkDelegate(linkInfo); } /// Opens the given [url] in the specified [webOnlyWindowName]. diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index 957b25757036..7ae84cdfb07e 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/u # 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.1.4+2 +version: 0.1.5 flutter: plugin: @@ -14,7 +14,7 @@ flutter: fileName: url_launcher_web.dart dependencies: - url_launcher_platform_interface: ^1.0.8 + url_launcher_platform_interface: ^1.0.9 flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart b/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart index d0dd6e38ee46..4d103443deb9 100644 --- a/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart +++ b/packages/url_launcher/url_launcher_web/test/test_driver/url_launcher_web_integration.dart @@ -3,8 +3,12 @@ // found in the LICENSE file. import 'dart:html' as html; +import 'dart:js_util'; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_web/url_launcher_web.dart'; +import 'package:url_launcher_web/src/link.dart'; import 'package:mockito/mockito.dart'; import 'package:integration_test/integration_test.dart'; @@ -228,4 +232,88 @@ void main() { }); }); }); + + group('link', () { + testWidgets('creates anchor with correct attributes', + (WidgetTester tester) async { + final Uri uri = Uri.parse('http://foobar/example?q=1'); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink followLink) { + return Container(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + + final html.Element anchor = _findSingleAnchor(); + expect(anchor.getAttribute('href'), uri.toString()); + expect(anchor.getAttribute('target'), '_blank'); + + final Uri uri2 = Uri.parse('http://foobar2/example?q=2'); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: WebLinkDelegate(TestLinkInfo( + uri: uri2, + target: LinkTarget.self, + builder: (BuildContext context, FollowLink followLink) { + return Container(width: 100, height: 100); + }, + )), + )); + await tester.pumpAndSettle(); + + // Check that the same anchor has been updated. + expect(anchor.getAttribute('href'), uri2.toString()); + expect(anchor.getAttribute('target'), '_self'); + }); + }); +} + +html.Element _findSingleAnchor() { + final List foundAnchors = []; + for (final html.Element anchor in html.document.querySelectorAll('a')) { + if (hasProperty(anchor, linkViewIdProperty)) { + foundAnchors.add(anchor); + } + } + + // Search inside platform views with shadow roots as well. + for (final html.Element platformView + in html.document.querySelectorAll('flt-platform-view')) { + final html.ShadowRoot shadowRoot = platformView.shadowRoot; + if (shadowRoot != null) { + for (final html.Element anchor in shadowRoot.querySelectorAll('a')) { + if (hasProperty(anchor, linkViewIdProperty)) { + foundAnchors.add(anchor); + } + } + } + } + + return foundAnchors.single; +} + +class TestLinkInfo extends LinkInfo { + @override + final LinkWidgetBuilder builder; + + @override + final Uri uri; + + @override + final LinkTarget target; + + @override + bool get isDisabled => uri == null; + + TestLinkInfo({ + @required this.uri, + @required this.target, + @required this.builder, + }); } diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index b06691468945..01601c7cc44c 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.11.1+2 + +* Update android compileSdkVersion to 29. + +## 0.11.1+1 + +* Fixed uncanceled timers when calling `play` on the controller multiple times before `pause`, which + caused value listeners to be called indefinitely (after `pause`) and more often than needed. + ## 0.11.1 * Enable TLSv1.1 & TLSv1.2 for API 19 and below. diff --git a/packages/video_player/video_player/android/build.gradle b/packages/video_player/video_player/android/build.gradle index 12725f3f7142..50636ae54591 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 28 + compileSdkVersion 29 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 47e7214822cc..2ab52e1baea1 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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index bf98096af45d..ac1645085e36 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -391,6 +391,9 @@ class VideoPlayerController extends ValueNotifier { } if (value.isPlaying) { await _videoPlayerPlatform.play(_textureId); + + // Cancel previous timer. + _timer?.cancel(); _timer = Timer.periodic( const Duration(milliseconds: 500), (Timer timer) async { diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 34e982851e29..96ced42cedcf 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for displaying inline video with other Flutter # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.11.1 +version: 0.11.1+2 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player flutter: diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md index 4270e11ba090..29c3cd8dc6b3 100644 --- a/packages/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/CHANGELOG.md @@ -1,3 +1,15 @@ +## 1.0.5 + +* Fix example in the readme. + +## 1.0.4 + +* Suppress the `deprecated_member_use` warning in the example app for `ScaffoldMessenger.showSnackBar`. + +## 1.0.3 + +* Update android compileSdkVersion to 29. + ## 1.0.2 * Android Code Inspection and Clean up. diff --git a/packages/webview_flutter/README.md b/packages/webview_flutter/README.md index c7b55f1c89dd..6852ebb34522 100644 --- a/packages/webview_flutter/README.md +++ b/packages/webview_flutter/README.md @@ -37,9 +37,15 @@ 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(); } diff --git a/packages/webview_flutter/android/build.gradle b/packages/webview_flutter/android/build.gradle index 893badc0e175..307dfcfa0b8e 100644 --- a/packages/webview_flutter/android/build.gradle +++ b/packages/webview_flutter/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 diff --git a/packages/webview_flutter/example/android/app/build.gradle b/packages/webview_flutter/example/android/app/build.gradle index 706d501c4060..619a3aead7b2 100644 --- a/packages/webview_flutter/example/android/app/build.gradle +++ b/packages/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 28 + compileSdkVersion 29 lintOptions { disable 'InvalidPackage' diff --git a/packages/webview_flutter/example/lib/main.dart b/packages/webview_flutter/example/lib/main.dart index ff25e8a16c9b..7ec3008337d8 100644 --- a/packages/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/example/lib/main.dart @@ -92,6 +92,7 @@ class _WebViewExampleState extends State { return JavascriptChannel( name: 'Toaster', onMessageReceived: (JavascriptMessage message) { + // ignore: deprecated_member_use Scaffold.of(context).showSnackBar( SnackBar(content: Text(message.message)), ); @@ -107,6 +108,7 @@ class _WebViewExampleState extends State { return FloatingActionButton( onPressed: () async { final String url = await controller.data.currentUrl(); + // ignore: deprecated_member_use Scaffold.of(context).showSnackBar( SnackBar(content: Text('Favorited $url')), ); @@ -215,6 +217,7 @@ class SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { final String cookies = await controller.evaluateJavascript('document.cookie'); + // ignore: deprecated_member_use Scaffold.of(context).showSnackBar(SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, @@ -230,6 +233,7 @@ class SampleMenu extends StatelessWidget { void _onAddToCache(WebViewController controller, BuildContext context) async { await controller.evaluateJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); + // ignore: deprecated_member_use Scaffold.of(context).showSnackBar(const SnackBar( content: Text('Added a test entry to cache.'), )); @@ -243,6 +247,7 @@ class SampleMenu extends StatelessWidget { void _onClearCache(WebViewController controller, BuildContext context) async { await controller.clearCache(); + // ignore: deprecated_member_use Scaffold.of(context).showSnackBar(const SnackBar( content: Text("Cache cleared."), )); @@ -254,6 +259,7 @@ class SampleMenu extends StatelessWidget { if (!hadCookies) { message = 'There are no cookies.'; } + // ignore: deprecated_member_use Scaffold.of(context).showSnackBar(SnackBar( content: Text(message), )); @@ -306,6 +312,7 @@ class NavigationControls extends StatelessWidget { if (await controller.canGoBack()) { await controller.goBack(); } else { + // ignore: deprecated_member_use Scaffold.of(context).showSnackBar( const SnackBar(content: Text("No back history item")), ); @@ -321,6 +328,7 @@ class NavigationControls extends StatelessWidget { if (await controller.canGoForward()) { await controller.goForward(); } else { + // ignore: deprecated_member_use Scaffold.of(context).showSnackBar( const SnackBar( content: Text("No forward history item")), diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml index ad1c356a67df..504e2ce97247 100644 --- a/packages/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. -version: 1.0.2 +version: 1.0.5 homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter environment: diff --git a/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md b/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md index 41cc7d8192ec..d85be8ba509d 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md +++ b/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md @@ -1,3 +1,4 @@ -## 0.0.1 +## 1.0.0 -* TODO: Describe initial release. +* 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/README.md b/packages/wifi_info_flutter/wifi_info_flutter/README.md index cf33a8e82532..0c2eff383c16 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/README.md +++ b/packages/wifi_info_flutter/wifi_info_flutter/README.md @@ -1,15 +1,71 @@ # wifi_info_flutter -A new flutter plugin project. +This plugin retrieves information about a device's connection to wifi. -## Getting Started +> 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 WifiFlutter().getWifiBSSID(); +var wifiIP = await WifiFlutter().getWifiIP(); +var wifiName = await WifiFlutter().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 -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. +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](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +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/src/main/AndroidManifest.xml b/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/AndroidManifest.xml index 730c0a71943c..03ac924f9427 100644 --- 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 @@ -1,3 +1,4 @@ + 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 new file mode 100644 index 000000000000..f74e4f0f75e1 --- /dev/null +++ b/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutter.java @@ -0,0 +1,56 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.wifi_info_flutter; + +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; + +/** Reports wifi information. */ +class WifiInfoFlutter { + private WifiManager wifiManager; + + WifiInfoFlutter(WifiManager wifiManager) { + this.wifiManager = wifiManager; + } + + String getWifiName() { + 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() { + 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(); + } +} 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 new file mode 100644 index 000000000000..b996d32255e3 --- /dev/null +++ b/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutterMethodChannelHandler.java @@ -0,0 +1,44 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.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 index 791d88c4b14e..407bbc0dd560 100644 --- 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 @@ -1,37 +1,46 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + package io.flutter.plugins.wifi_info_flutter; -import androidx.annotation.NonNull; +import android.content.Context; +import android.net.wifi.WifiManager; import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; /** WifiInfoFlutterPlugin */ -public class WifiInfoFlutterPlugin implements FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private MethodChannel channel; +public class WifiInfoFlutterPlugin implements FlutterPlugin { + private MethodChannel methodChannel; - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "wifi_info_flutter"); - channel.setMethodCallHandler(this); + /** 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 onMethodCall(@NonNull MethodCall call, @NonNull Result result) { - if (call.method.equals("getPlatformVersion")) { - result.success("Android " + android.os.Build.VERSION.RELEASE); - } else { - result.notImplemented(); - } + public void onAttachedToEngine(FlutterPluginBinding binding) { + setupChannels(binding.getBinaryMessenger(), binding.getApplicationContext()); } @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); + 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_flutter_info"); + final WifiManager wifiManager = + (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + + final WifiInfoFlutter wifiInfoFlutter = new WifiInfoFlutter(wifiManager); + + final WifiInfoFlutterMethodChannelHandler methodChannelHandler = + new WifiInfoFlutterMethodChannelHandler(wifiInfoFlutter); + methodChannel.setMethodCallHandler(methodChannelHandler); } } 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 index 94adc3a3f97a..a6738207fd15 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/gradle.properties +++ b/packages/wifi_info_flutter/wifi_info_flutter/example/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true 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 index 11cd5cfedbed..2c7f6b8c6b85 100644 --- 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 @@ -41,5 +41,9 @@ 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/lib/main.dart b/packages/wifi_info_flutter/wifi_info_flutter/example/lib/main.dart index b9b8f6b20df7..17a9e76ad946 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/lib/main.dart +++ b/packages/wifi_info_flutter/wifi_info_flutter/example/lib/main.dart @@ -1,60 +1,173 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + // ignore_for_file: public_member_api_docs -import 'package:flutter/material.dart'; 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 StatefulWidget { +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 - _MyAppState createState() => _MyAppState(); + _MyHomePageState createState() => _MyHomePageState(); } -class _MyAppState extends State { - String _platformVersion = 'Unknown'; +class _MyHomePageState extends State { + String _connectionStatus = 'Unknown'; + final Connectivity _connectivity = Connectivity(); + final WifiInfo _wifiInfo = WifiInfo(); + StreamSubscription _connectivitySubscription; @override void initState() { super.initState(); - initPlatformState(); + 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 initPlatformState() async { - String platformVersion; + Future initConnectivity() async { + ConnectivityResult result; // Platform messages may fail, so we use a try/catch PlatformException. try { - platformVersion = await WifiInfoFlutter.platformVersion; - } on PlatformException { - platformVersion = 'Failed to get platform version.'; + 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; + if (!mounted) { + return Future.value(null); + } - setState(() { - _platformVersion = platformVersion; - }); + return _updateConnectionStatus(result); } @override Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Center( - child: Text('Running on: $_platformVersion\n'), - ), + 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 _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 (!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 _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/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml b/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml index 1bf8a8d5dbf3..7ebc1573ddcf 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml +++ b/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml @@ -1,71 +1,23 @@ name: wifi_info_flutter_example description: Demonstrates how to use the wifi_info_flutter plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: 'none' environment: sdk: ">=2.7.0 <3.0.0" dependencies: + connectivity: 0.4.9+3 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: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 dev_dependencies: + integration_test: + path: ../../../integration_test flutter_test: sdk: flutter -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/test/widget_test.dart b/packages/wifi_info_flutter/wifi_info_flutter/example/test/widget_test.dart deleted file mode 100644 index 1857e9a37942..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/test/widget_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:wifi_info_flutter_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => - widget is Text && widget.data.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/test_driver/integration_test/wifi_info_test.dart b/packages/wifi_info_flutter/wifi_info_flutter/example/test_driver/integration_test/wifi_info_test.dart new file mode 100644 index 000000000000..103dc54a1eaa --- /dev/null +++ b/packages/wifi_info_flutter/wifi_info_flutter/example/test_driver/integration_test/wifi_info_test.dart @@ -0,0 +1,27 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +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', () { + 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/test_driver/test/integration_test.dart b/packages/wifi_info_flutter/wifi_info_flutter/example/test_driver/test/integration_test.dart new file mode 100644 index 000000000000..8a77ec87bbac --- /dev/null +++ b/packages/wifi_info_flutter/wifi_info_flutter/example/test_driver/test/integration_test.dart @@ -0,0 +1,16 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter_driver/flutter_driver.dart'; + +Future main() async { + final FlutterDriver driver = await FlutterDriver.connect(); + final String data = + await driver.requestData(null, timeout: const Duration(minutes: 1)); + await driver.close(); + final Map result = jsonDecode(data); + exit(result['result'] == 'true' ? 0 : 1); +} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/integration_test/wifi_info_test.dart b/packages/wifi_info_flutter/wifi_info_flutter/integration_test/wifi_info_test.dart new file mode 100644 index 000000000000..103dc54a1eaa --- /dev/null +++ b/packages/wifi_info_flutter/wifi_info_flutter/integration_test/wifi_info_test.dart @@ -0,0 +1,27 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +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', () { + 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/connectivity/connectivity/ios/Classes/FLTConnectivityLocationHandler.h b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.h similarity index 50% rename from packages/connectivity/connectivity/ios/Classes/FLTConnectivityLocationHandler.h rename to packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.h index 1731d56fe782..7cfe42df6079 100644 --- a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityLocationHandler.h +++ b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.h @@ -1,21 +1,22 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + #import #import NS_ASSUME_NONNULL_BEGIN -@class FLTConnectivityLocationDelegate; +@class FLTWifiInfoLocationDelegate; -typedef void (^FLTConnectivityLocationCompletion)(CLAuthorizationStatus); +typedef void (^FLTWifiInfoLocationCompletion)(CLAuthorizationStatus); -@interface FLTConnectivityLocationHandler : NSObject +@interface FLTWifiInfoLocationHandler : NSObject + (CLAuthorizationStatus)locationAuthorizationStatus; - (void)requestLocationAuthorization:(BOOL)always - completion:(_Nonnull FLTConnectivityLocationCompletion)completionHnadler; + completion:(_Nonnull FLTWifiInfoLocationCompletion)completionHnadler; @end diff --git a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityLocationHandler.m b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.m similarity index 77% rename from packages/connectivity/connectivity/ios/Classes/FLTConnectivityLocationHandler.m rename to packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.m index bbb93aea6a5b..2f237a1d3f15 100644 --- a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityLocationHandler.m +++ b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.m @@ -1,24 +1,24 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "FLTConnectivityLocationHandler.h" +#import "FLTWifiInfoLocationHandler.h" -@interface FLTConnectivityLocationHandler () +@interface FLTWifiInfoLocationHandler () -@property(copy, nonatomic) FLTConnectivityLocationCompletion completion; +@property(copy, nonatomic) FLTWifiInfoLocationCompletion completion; @property(strong, nonatomic) CLLocationManager *locationManager; @end -@implementation FLTConnectivityLocationHandler +@implementation FLTWifiInfoLocationHandler + (CLAuthorizationStatus)locationAuthorizationStatus { return CLLocationManager.authorizationStatus; } - (void)requestLocationAuthorization:(BOOL)always - completion:(FLTConnectivityLocationCompletion)completionHandler { + completion:(FLTWifiInfoLocationCompletion)completionHandler { CLAuthorizationStatus status = CLLocationManager.authorizationStatus; if (status != kCLAuthorizationStatusAuthorizedWhenInUse && always) { completionHandler(kCLAuthorizationStatusDenied); 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 index 212cb2122411..92d843793990 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.h +++ b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.h @@ -1,3 +1,7 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #import @interface WifiInfoFlutterPlugin : NSObject 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 index d4577610e3db..3abdbeef5744 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.m +++ b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.m @@ -1,20 +1,132 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #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:@"wifi_info_flutter" + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/wifi_info_flutter" binaryMessenger:[registrar messenger]]; - WifiInfoFlutterPlugin* instance = [[WifiInfoFlutterPlugin alloc] init]; [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 ([@"getPlatformVersion" isEqualToString:call.method]) { - result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); + 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/lib/wifi_info_flutter.dart b/packages/wifi_info_flutter/wifi_info_flutter/lib/wifi_info_flutter.dart index 5711e4be147d..1183bf6c74bf 100644 --- 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 @@ -1,15 +1,153 @@ -// ignore_for_file: public_member_api_docs +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. import 'dart:async'; import 'package:flutter/services.dart'; +import 'package:wifi_info_flutter_platform_interface/wifi_info_flutter_platform_interface.dart'; -class WifiInfoFlutter { - static const MethodChannel _channel = - const MethodChannel('wifi_info_flutter'); +// 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; - static Future get platformVersion async { - final String version = await _channel.invokeMethod('getPlatformVersion'); - return version; +/// Checks WI-FI status and more. +class WifiInfo { + /// Constructs a singleton instance of [WifiInfo]. + /// + /// [WifiInfo] is designed to work as a singleton. + factory WifiInfo() { + if (_singleton == null) { + _singleton = WifiInfo._(); + } + return _singleton; + } + + WifiInfo._(); + + static WifiInfo _singleton; + + 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 index 6a7276b97f33..567a0912a8fc 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/pubspec.yaml +++ b/packages/wifi_info_flutter/wifi_info_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: wifi_info_flutter description: A new flutter plugin project. -version: 0.0.1 +version: 1.0.0 homepage: https://github.com/flutter/plugins/tree/master/packages/wifi_info_flutter/wifi_info_flutter environment: @@ -10,20 +10,17 @@ environment: dependencies: flutter: sdk: flutter + wifi_info_flutter_platform_interface: ^1.0.0 dev_dependencies: + mockito: ^4.1.1 + plugin_platform_interface: ^1.0.0 + integration_test: + path: ../../integration_test flutter_test: sdk: flutter -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' and Android 'package' identifiers should not ordinarily - # be modified. They are used by the tooling to maintain consistency when - # adding or updating assets for this project. plugin: platforms: android: @@ -31,34 +28,3 @@ flutter: pluginClass: WifiInfoFlutterPlugin ios: pluginClass: WifiInfoFlutterPlugin - - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/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 index 93256726db43..a3a55170bce5 100644 --- 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 @@ -1,23 +1,85 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.8 + 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'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:mockito/mockito.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() { - const MethodChannel channel = MethodChannel('wifi_info_flutter'); + group('$WifiInfo', () { + WifiInfo wifiInfo; + MockWifiInfoFlutterPlatform fakePlatform; - TestWidgetsFlutterBinding.ensureInitialized(); + setUp(() async { + fakePlatform = MockWifiInfoFlutterPlatform(); + WifiInfoFlutterPlatform.instance = fakePlatform; + wifiInfo = WifiInfo(); + }); - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; + test('getWifiName', () async { + String result = await wifiInfo.getWifiName(); + expect(result, kWifiNameResult); }); - }); - tearDown(() { - channel.setMockMethodCallHandler(null); - }); + 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('getPlatformVersion', () async { - expect(await WifiInfoFlutter.platformVersion, '42'); + test('getLocationServiceAuthorization', () async { + LocationAuthorizationStatus result = + await wifiInfo.getLocationServiceAuthorization(); + expect(result, kRequestLocationResult); + }); }); } + +class MockWifiInfoFlutterPlatform extends Mock + with MockPlatformInterfaceMixin + implements WifiInfoFlutterPlatform { + Future getWifiName() async { + return kWifiNameResult; + } + + Future getWifiBSSID() async { + return kWifiBSSIDResult; + } + + Future getWifiIP() async { + return kWifiIpAddressResult; + } + + Future requestLocationServiceAuthorization({ + bool requestAlwaysLocationUsage = false, + }) async { + return kRequestLocationResult; + } + + Future getLocationServiceAuthorization() async { + return kGetLocationResult; + } +} 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 index ac071598e5d4..a845f1956ab4 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/CHANGELOG.md +++ b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,4 @@ -## [0.0.1] - TODO: Add release date. +## 1.0.0 -* TODO: Describe initial release. +* 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/README.md b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/README.md index 88ac7acc6f04..f2039c3d5865 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/README.md +++ b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/README.md @@ -1,14 +1,26 @@ # wifi_info_flutter_platform_interface -A new Flutter package project. +A common platform interface for the [`wifi_info_flutter`][1] plugin. -## Getting Started +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. -This project is a starting point for a Dart -[package](https://flutter.dev/developing-packages/), -a library module containing code that can be shared easily across -multiple Flutter or Dart projects. +# Usage -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. +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 new file mode 100644 index 000000000000..4529938b4761 --- /dev/null +++ b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/src/enums.dart @@ -0,0 +1,24 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// 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 new file mode 100644 index 000000000000..ef390c3cde1e --- /dev/null +++ b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/src/method_channel_wifi_info_flutter.dart @@ -0,0 +1,58 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +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 index 7aa6f0b86660..85034b2cbbff 100644 --- 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 @@ -1,7 +1,74 @@ -library wifi_info_flutter_platform_interface; +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -/// A Calculator. -class Calculator { - /// Returns [value] plus 1. - int addOne(int value) => value + 1; +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 index 4b167fd4a1d5..68d516d515a8 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/pubspec.yaml +++ b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/pubspec.yaml @@ -1,6 +1,8 @@ name: wifi_info_flutter_platform_interface -description: A new Flutter package project. -version: 0.0.1 +description: A common platform interface for the wifi_info_flutter plugin. +version: 1.0.0 +# 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 homepage: https://github.com/flutter/plugins/tree/master/packages/wifi_info_flutter/wifi_info_flutter_platform_interface environment: @@ -8,46 +10,11 @@ environment: flutter: ">=1.17.0 <2.0.0" dependencies: + plugin_platform_interface: ^1.0.3 flutter: sdk: flutter dev_dependencies: + pedantic: ^1.9.2 flutter_test: sdk: flutter - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # To add assets to your package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/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 new file mode 100644 index 000000000000..b48b2c4110bb --- /dev/null +++ b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/test/method_channel_wifi_info_flutter_test.dart @@ -0,0 +1,114 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +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 = []; + 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/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/test/wifi_info_flutter_platform_interface_test.dart b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/test/wifi_info_flutter_platform_interface_test.dart deleted file mode 100644 index d104d40e56d3..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/test/wifi_info_flutter_platform_interface_test.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -import 'package:wifi_info_flutter_platform_interface/wifi_info_flutter_platform_interface.dart'; - -void main() { - test('adds one to input values', () { - final calculator = Calculator(); - expect(calculator.addOne(2), 3); - expect(calculator.addOne(-7), -6); - expect(calculator.addOne(0), 1); - expect(() => calculator.addOne(null), throwsNoSuchMethodError); - }); -} diff --git a/script/incremental_build.sh b/script/incremental_build.sh index 30c166b4c666..8e9cf34b1cda 100755 --- a/script/incremental_build.sh +++ b/script/incremental_build.sh @@ -24,6 +24,7 @@ CUSTOM_ANALYSIS_PLUGINS=( "camera" "video_player/video_player_web" "google_maps_flutter/google_maps_flutter_web" + "url_launcher/url_launcher_web" ) # Comma-separated string of the list above readonly CUSTOM_FLAG=$(IFS=, ; echo "${CUSTOM_ANALYSIS_PLUGINS[*]}")