From 537cc437c0bf4d502654113bba7d941a9266f4a8 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Fri, 10 Dec 2021 11:03:04 +1300 Subject: [PATCH 1/7] [path_provider] Use the application ID in the application support path (#2845) (#3077) Use the existing executable named directory if it exists, to allow backwards compatibility to work. --- .../path_provider_linux/CHANGELOG.md | 5 + .../lib/path_provider_linux.dart | 59 +----------- .../lib/src/get_application_id.dart | 9 ++ .../lib/src/get_application_id_real.dart | 42 +++++++++ .../lib/src/get_application_id_stub.dart | 6 ++ .../lib/src/path_provider_linux.dart | 92 +++++++++++++++++++ .../path_provider_linux/pubspec.yaml | 3 +- .../test/path_provider_linux_test.dart | 17 +++- 8 files changed, 172 insertions(+), 61 deletions(-) create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index 209fddbd61bd..f227570bdd1d 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.3 + +* Change getApplicationSupportPath from using executable name to application ID (if provided). + * If the executable name based directory exists, continue to use that so existing applications continue with the same behaviour. + ## 2.1.2 * Fixes link in README. diff --git a/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart b/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart index ab18db69ddfb..e32af1bf5f13 100644 --- a/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart +++ b/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart @@ -2,61 +2,4 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:path/path.dart' as path; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; -import 'package:xdg_directories/xdg_directories.dart' as xdg; - -/// The linux implementation of [PathProviderPlatform] -/// -/// This class implements the `package:path_provider` functionality for linux -class PathProviderLinux extends PathProviderPlatform { - /// Constructs an instance of [PathProviderLinux] - PathProviderLinux() : _environment = Platform.environment; - - /// Constructs an instance of [PathProviderLinux] with the given [environment] - @visibleForTesting - PathProviderLinux.private({ - required Map environment, - }) : _environment = environment; - - final Map _environment; - - /// Registers this class as the default instance of [PathProviderPlatform] - static void registerWith() { - PathProviderPlatform.instance = PathProviderLinux(); - } - - @override - Future getTemporaryPath() { - final String environmentTmpDir = _environment['TMPDIR'] ?? ''; - return Future.value( - environmentTmpDir.isEmpty ? '/tmp' : environmentTmpDir, - ); - } - - @override - Future getApplicationSupportPath() async { - final String processName = path.basenameWithoutExtension( - await File('/proc/self/exe').resolveSymbolicLinks()); - final Directory directory = - Directory(path.join(xdg.dataHome.path, processName)); - // Creating the directory if it doesn't exist, because mobile implementations assume the directory exists - if (!directory.existsSync()) { - await directory.create(recursive: true); - } - return directory.path; - } - - @override - Future getApplicationDocumentsPath() { - return Future.value(xdg.getUserDirectory('DOCUMENTS')?.path); - } - - @override - Future getDownloadsPath() { - return Future.value(xdg.getUserDirectory('DOWNLOAD')?.path); - } -} +export 'src/path_provider_linux.dart'; diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart new file mode 100644 index 000000000000..e169c025eef1 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// getApplicationId() is implemented using FFI; export a stub for platforms +// that don't support FFI (e.g., web) to avoid having transitive dependencies +// break web compilation. +export 'get_application_id_stub.dart' + if (dart.library.ffi) 'get_application_id_real.dart'; diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart new file mode 100644 index 000000000000..f6d25bbe783e --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +// GApplication* g_application_get_default(); +typedef _GApplicationGetDefaultC = IntPtr Function(); +typedef _GApplicationGetDefaultDart = int Function(); + +// const gchar* g_application_get_application_id(GApplication* application); +typedef _GApplicationGetApplicationIdC = Pointer Function(IntPtr); +typedef _GApplicationGetApplicationIdDart = Pointer Function(int); + +/// Gets the application ID for this app. +String? getApplicationId() { + DynamicLibrary gio; + try { + gio = DynamicLibrary.open('libgio-2.0.so'); + } on ArgumentError { + return null; + } + final _GApplicationGetDefaultDart gApplicationGetDefault = + gio.lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( + 'g_application_get_default'); + final int app = gApplicationGetDefault(); + if (app == 0) { + return null; + } + + final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = + gio.lookupFunction<_GApplicationGetApplicationIdC, + _GApplicationGetApplicationIdDart>( + 'g_application_get_application_id'); + final Pointer appId = gApplicationGetApplicationId(app); + if (appId == null) { + return null; + } + + return appId.toDartString(); +} diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart new file mode 100644 index 000000000000..909997693626 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart @@ -0,0 +1,6 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Gets the application ID for this app. +String? getApplicationId() => null; diff --git a/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart new file mode 100644 index 000000000000..1544dcea2984 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart @@ -0,0 +1,92 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:xdg_directories/xdg_directories.dart' as xdg; + +import 'get_application_id.dart'; + +/// The linux implementation of [PathProviderPlatform] +/// +/// This class implements the `package:path_provider` functionality for Linux. +class PathProviderLinux extends PathProviderPlatform { + /// Constructs an instance of [PathProviderLinux] + PathProviderLinux() : _environment = Platform.environment; + + /// Constructs an instance of [PathProviderLinux] with the given [environment] + @visibleForTesting + PathProviderLinux.private( + {Map environment = const {}, + String? executableName, + String? applicationId}) + : _environment = environment, + _executableName = executableName, + _applicationId = applicationId; + + final Map _environment; + String? _executableName; + String? _applicationId; + + /// Registers this class as the default instance of [PathProviderPlatform] + static void registerWith() { + PathProviderPlatform.instance = PathProviderLinux(); + } + + @override + Future getTemporaryPath() { + final String environmentTmpDir = _environment['TMPDIR'] ?? ''; + return Future.value( + environmentTmpDir.isEmpty ? '/tmp' : environmentTmpDir, + ); + } + + @override + Future getApplicationSupportPath() async { + final Directory directory = + Directory(path.join(xdg.dataHome.path, await _getId())); + if (directory.existsSync()) { + return directory.path; + } + + // This plugin originally used the executable name as a directory. + // Use that if it exists for backwards compatibility. + final Directory legacyDirectory = + Directory(path.join(xdg.dataHome.path, await _getExecutableName())); + if (legacyDirectory.existsSync()) { + return legacyDirectory.path; + } + + // Create the directory, because mobile implementations assume the directory exists. + await directory.create(recursive: true); + return directory.path; + } + + @override + Future getApplicationDocumentsPath() { + return Future.value(xdg.getUserDirectory('DOCUMENTS')?.path); + } + + @override + Future getDownloadsPath() { + return Future.value(xdg.getUserDirectory('DOWNLOAD')?.path); + } + + // Gets the name of this executable. + Future _getExecutableName() async { + _executableName ??= path.basenameWithoutExtension( + await File('/proc/self/exe').resolveSymbolicLinks()); + return _executableName!; + } + + // Gets the unique ID for this application. + Future _getId() async { + _applicationId ??= getApplicationId(); + // If no application ID then fall back to using the executable name. + return _applicationId ?? await _getExecutableName(); + } +} diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 67638cdef332..25d1af9936c8 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.2 +version: 2.1.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -16,6 +16,7 @@ flutter: dartPluginClass: PathProviderLinux dependencies: + ffi: ^1.1.2 flutter: sdk: flutter path: ^1.8.0 diff --git a/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart b/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart index 6dd35000a8ea..1f567c00513d 100644 --- a/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart +++ b/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:xdg_directories/xdg_directories.dart' as xdg; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -35,8 +36,20 @@ void main() { }); test('getApplicationSupportPath', () async { - final PathProviderPlatform plugin = PathProviderPlatform.instance; - expect(await plugin.getApplicationSupportPath(), startsWith('/')); + final PathProviderPlatform plugin = PathProviderLinux.private( + executableName: 'path_provider_linux_test_binary', + applicationId: 'com.example.Test'); + // Note this will fail if ${xdg.dataHome.path}/path_provider_linux_test_binary exists on the local filesystem. + expect(await plugin.getApplicationSupportPath(), + '${xdg.dataHome.path}/com.example.Test'); + }); + + test('getApplicationSupportPath uses executable name if no application Id', + () async { + final PathProviderPlatform plugin = PathProviderLinux.private( + executableName: 'path_provider_linux_test_binary'); + expect(await plugin.getApplicationSupportPath(), + '${xdg.dataHome.path}/path_provider_linux_test_binary'); }); test('getApplicationDocumentsPath', () async { From e74df9bbef32563ec0326896382a0cd5bba43cad Mon Sep 17 00:00:00 2001 From: Alan Ou <50572272+HiIamAlanOu@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:16:42 +0800 Subject: [PATCH 2/7] [in_app_purchase] Fix upgrading subscription by deferred proration mode on android. (#4560) This PR is for updating the bug of upgrading subscription by deferred mode on android, the purchaseStream of PurchaseStatus will return error. Fixes [flutter/flutter#94439] --- .../in_app_purchase_android/CHANGELOG.md | 4 ++ .../src/in_app_purchase_android_platform.dart | 10 +++-- .../in_app_purchase_android/pubspec.yaml | 2 +- ...in_app_purchase_android_platform_test.dart | 45 +++++++++++++++++++ 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 9bce54c2df0f..5c34de267737 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2 + +* Fixes the `purchaseStream` incorrectly reporting `PurchaseStatus.error` when user upgrades subscription by deferred proration mode. + ## 0.2.1 * Deprecated the `InAppPurchaseAndroidPlatformAddition.enablePendingPurchases()` method and `InAppPurchaseAndroidPlatformAddition.enablePendingPurchase` property. Since Google Play no longer accepts App submissions that don't support pending purchases it is no longer necessary to acknowledge this through code. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart index 7deec6e6ab69..7c8ca529c0f5 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart @@ -270,13 +270,17 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { if (purchases.isNotEmpty) { return Future.wait(purchases); } else { + PurchaseStatus status = PurchaseStatus.error; + if (resultWrapper.responseCode == BillingResponse.userCanceled) { + status = PurchaseStatus.canceled; + } else if (resultWrapper.responseCode == BillingResponse.ok) { + status = PurchaseStatus.purchased; + } return [ PurchaseDetails( purchaseID: '', productID: '', - status: resultWrapper.responseCode == BillingResponse.userCanceled - ? PurchaseStatus.canceled - : PurchaseStatus.error, + status: status, transactionDate: null, verificationData: PurchaseVerificationData( localVerificationData: '', diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 63bfa134377f..4d8864357b57 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.1 +version: 0.2.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart index 9f00bc79f52a..9c167e5ea3be 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart @@ -682,6 +682,51 @@ void main() { GooglePlayPurchaseDetails result = await completer.future; expect(result.status, PurchaseStatus.canceled); }); + + test( + 'should get purchased purchase status when upgrading subscription by deferred proration mode', + () async { + final SkuDetailsWrapper skuDetails = dummySkuDetails; + final String accountId = "hashedAccountId"; + const String debugMessage = 'dummy message'; + final BillingResponse sentCode = BillingResponse.ok; + final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + responseCode: sentCode, debugMessage: debugMessage); + stubPlatform.addResponse( + name: launchMethodName, + value: buildBillingResultMap(expectedBillingResult), + additionalStepBeforeReturn: (_) { + // Mock java update purchase callback. + MethodCall call = MethodCall(kOnPurchasesUpdated, { + 'billingResult': buildBillingResultMap(expectedBillingResult), + 'responseCode': BillingResponseConverter().toJson(sentCode), + 'purchasesList': [] + }); + iapAndroidPlatform.billingClient.callHandler(call); + }); + + Completer completer = Completer(); + PurchaseDetails purchaseDetails; + Stream purchaseStream = iapAndroidPlatform.purchaseStream; + late StreamSubscription subscription; + subscription = purchaseStream.listen((_) { + purchaseDetails = _.first; + completer.complete(purchaseDetails); + subscription.cancel(); + }, onDone: () {}); + final GooglePlayPurchaseParam purchaseParam = GooglePlayPurchaseParam( + productDetails: GooglePlayProductDetails.fromSkuDetails(skuDetails), + applicationUserName: accountId, + changeSubscriptionParam: ChangeSubscriptionParam( + oldPurchaseDetails: GooglePlayPurchaseDetails.fromPurchase( + dummyUnacknowledgedPurchase), + prorationMode: ProrationMode.deferred, + )); + await iapAndroidPlatform.buyNonConsumable(purchaseParam: purchaseParam); + + PurchaseDetails result = await completer.future; + expect(result.status, PurchaseStatus.purchased); + }); }); group('complete purchase', () { From f9f6597ad844f5d5c1d76ecbf8576da5e7f8ba5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Uek=C3=B6tter?= Date: Fri, 10 Dec 2021 12:17:17 +0100 Subject: [PATCH 3/7] [shared_preferences] Fix example in readme for mocking values (#4411) This fixes the example for mocking values in the readme of shared_preferences. After the NNBD migration mock values can only be of type Object. --- packages/shared_preferences/shared_preferences/CHANGELOG.md | 4 ++++ packages/shared_preferences/shared_preferences/README.md | 3 ++- packages/shared_preferences/shared_preferences/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index cc29dd98fc89..d8c4331133aa 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.11 + +* Corrects example for mocking in readme. + ## 2.0.10 * Removes obsolete manual registration of Windows and Linux implementations. diff --git a/packages/shared_preferences/shared_preferences/README.md b/packages/shared_preferences/shared_preferences/README.md index e51ddea1c890..46d022f4647c 100644 --- a/packages/shared_preferences/shared_preferences/README.md +++ b/packages/shared_preferences/shared_preferences/README.md @@ -42,5 +42,6 @@ _incrementCounter() async { You can populate `SharedPreferences` with initial values in your tests by running this code: ```dart -SharedPreferences.setMockInitialValues (Map values); +Map values = {'counter': 1}; +SharedPreferences.setMockInitialValues(values); ``` diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 156e2e230565..a16cd2009738 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.10 +version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" From 684c34f5692808ce2fbf9a3d89290d3d7a4f7e33 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Fri, 10 Dec 2021 03:18:07 -0800 Subject: [PATCH 4/7] Add missing return for nullably typed function (#4598) --- script/tool/lib/src/pubspec_check_command.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 04c3848a7dec..4f2b208524d9 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -211,6 +211,7 @@ class PubspecCheckCommand extends PackageLoopingCommand { return '"description" is too long. pub.dev recommends package ' 'descriptions of 60-180 characters.'; } + return null; } bool _checkIssueLink(Pubspec pubspec) { From b78018a365046629be142a429712c405896ff5a8 Mon Sep 17 00:00:00 2001 From: Daewon Kim Date: Fri, 10 Dec 2021 06:20:58 -0500 Subject: [PATCH 5/7] [in_app-purchase] Updated the ReadMe to remove the deprecated `InAppPurchaseAndroidPlatformAddition.enablePendingPurchases ` method (#4597) This will resolve flutter/flutter#93837 & this work is just the follow-up from #4527 PR. We need to update the documentation in the README.md of the in_app_purchase package to remove the instruction of initialization with the deprecated `InAppPurchaseAndroidPlatformAddition.enablePendingPurchases` method. Fixes https://github.com/flutter/flutter/issues/94977 --- .../in_app_purchase/CHANGELOG.md | 4 ++++ .../in_app_purchase/in_app_purchase/README.md | 22 ------------------- .../in_app_purchase/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 9e82ebdfe7a8..232dacf0b71e 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.1 + +* Removes the instructions on initializing the plugin since this functionality is deprecated. + ## 2.0.0 * **BREAKING CHANGES**: diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index f3ed1e0570f7..b67f9e5b52ac 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -54,7 +54,6 @@ See also the codelab for [in-app purchases in Flutter](https://codelabs.develope This section has examples of code for the following tasks: -* [Initializing the plugin](#initializing-the-plugin) * [Listening to purchase updates](#listening-to-purchase-updates) * [Connecting to the underlying store](#connecting-to-the-underlying-store) * [Loading products for sale](#loading-products-for-sale) @@ -65,27 +64,6 @@ This section has examples of code for the following tasks: * [Accessing platform specific product or purchase properties](#accessing-platform-specific-product-or-purchase-properties) * [Presenting a code redemption sheet (iOS 14)](#presenting-a-code-redemption-sheet-ios-14) -### Initializing the plugin - -The following initialization code is required for Google Play: - -```dart -// Import `in_app_purchase_android.dart` to be able to access the -// `InAppPurchaseAndroidPlatformAddition` class. -import 'package:in_app_purchase_android/in_app_purchase_android.dart'; -import 'package:flutter/foundation.dart'; - -void main() { - // Inform the plugin that this app supports pending purchases on Android. - // An error will occur on Android if you access the plugin `instance` - // without this call. - if (defaultTargetPlatform == TargetPlatform.android) { - InAppPurchaseAndroidPlatformAddition.enablePendingPurchases(); - } - runApp(MyApp()); -} -``` - **Note:** It is not necessary to depend on `com.android.billingclient:billing` in your own app's `android/app/build.gradle` file. If you choose to do so know that conflicts might occur. ### Listening to purchase updates diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 72fac1d29f55..c7bb11f38f8f 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 2.0.0 +version: 2.0.1 environment: sdk: ">=2.12.0 <3.0.0" From e7dbb5f4a59fc13514183b278be50dd5b6b24eb7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 10 Dec 2021 14:33:10 -0500 Subject: [PATCH 6/7] [path_provider] Fix handling of null application ID (#4606) The lookup of application ID was handling `null` (Dart null), but not `nullptr` (Dart representation of a C null pointer), so was throwing an exception in applications without an application ID. This includes the `shared_preferences_linux` example application, so this fixes tree breakage. --- .../path_provider_linux/CHANGELOG.md | 5 ++ .../lib/src/get_application_id_real.dart | 68 ++++++++++++++----- .../path_provider_linux/pubspec.yaml | 3 +- .../test/get_application_id_test.dart | 62 +++++++++++++++++ 4 files changed, 121 insertions(+), 17 deletions(-) create mode 100644 packages/path_provider/path_provider_linux/test/get_application_id_test.dart diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index f227570bdd1d..cfea5702dbe1 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.4 + +* Fixes `getApplicationSupportPath` handling of applications where the + application ID is not set. + ## 2.1.3 * Change getApplicationSupportPath from using executable name to application ID (if provided). diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart index f6d25bbe783e..d2e60fb6ff82 100644 --- a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart @@ -4,6 +4,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'package:meta/meta.dart'; // GApplication* g_application_get_default(); typedef _GApplicationGetDefaultC = IntPtr Function(); @@ -13,30 +14,65 @@ typedef _GApplicationGetDefaultDart = int Function(); typedef _GApplicationGetApplicationIdC = Pointer Function(IntPtr); typedef _GApplicationGetApplicationIdDart = Pointer Function(int); +/// Interface for interacting with libgio. +@visibleForTesting +class GioUtils { + /// Creates a default instance that uses the real libgio. + GioUtils() { + try { + _gio = DynamicLibrary.open('libgio-2.0.so'); + } on ArgumentError { + _gio = null; + } + } + + DynamicLibrary? _gio; + + /// True if libgio was opened successfully. + bool get libraryIsPresent => _gio != null; + + /// Wraps `g_application_get_default`. + int gApplicationGetDefault() { + if (_gio == null) { + return 0; + } + final _GApplicationGetDefaultDart getDefault = _gio! + .lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( + 'g_application_get_default'); + return getDefault(); + } + + /// Wraps g_application_get_application_id. + Pointer gApplicationGetApplicationId(int app) { + if (_gio == null) { + return nullptr; + } + final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = _gio! + .lookupFunction<_GApplicationGetApplicationIdC, + _GApplicationGetApplicationIdDart>( + 'g_application_get_application_id'); + return gApplicationGetApplicationId(app); + } +} + +/// Allows overriding the default GioUtils instance with a fake for testing. +@visibleForTesting +GioUtils? gioUtilsOverride; + /// Gets the application ID for this app. String? getApplicationId() { - DynamicLibrary gio; - try { - gio = DynamicLibrary.open('libgio-2.0.so'); - } on ArgumentError { + final GioUtils gio = gioUtilsOverride ?? GioUtils(); + if (!gio.libraryIsPresent) { return null; } - final _GApplicationGetDefaultDart gApplicationGetDefault = - gio.lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( - 'g_application_get_default'); - final int app = gApplicationGetDefault(); + + final int app = gio.gApplicationGetDefault(); if (app == 0) { return null; } - - final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = - gio.lookupFunction<_GApplicationGetApplicationIdC, - _GApplicationGetApplicationIdDart>( - 'g_application_get_application_id'); - final Pointer appId = gApplicationGetApplicationId(app); - if (appId == null) { + final Pointer appId = gio.gApplicationGetApplicationId(app); + if (appId == null || appId == nullptr) { return null; } - return appId.toDartString(); } diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 25d1af9936c8..ec1d8d8b3148 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.3 +version: 2.1.4 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,6 +19,7 @@ dependencies: ffi: ^1.1.2 flutter: sdk: flutter + meta: ^1.3.0 path: ^1.8.0 path_provider_platform_interface: ^2.0.0 xdg_directories: ^0.2.0 diff --git a/packages/path_provider/path_provider_linux/test/get_application_id_test.dart b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart new file mode 100644 index 000000000000..d9eb5163b5fe --- /dev/null +++ b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_linux/src/get_application_id_real.dart'; + +class _FakeGioUtils implements GioUtils { + int? application; + Pointer? applicationId; + + @override + bool libraryIsPresent = false; + + @override + int gApplicationGetDefault() => application!; + + @override + Pointer gApplicationGetApplicationId(int app) => applicationId!; +} + +void main() { + late _FakeGioUtils fakeGio; + + setUp(() { + fakeGio = _FakeGioUtils(); + gioUtilsOverride = fakeGio; + }); + + tearDown(() { + gioUtilsOverride = null; + }); + + test('returns null if libgio is not available', () { + expect(getApplicationId(), null); + }); + + test('returns null if g_paplication_get_default returns 0', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 0; + expect(getApplicationId(), null); + }); + + test('returns null if g_application_get_application_id returns nullptr', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 1; + fakeGio.applicationId = nullptr; + expect(getApplicationId(), null); + }); + + test('returns value if g_application_get_application_id returns a value', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 1; + const String id = 'foo'; + final Pointer idPtr = id.toNativeUtf8(); + fakeGio.applicationId = idPtr; + expect(getApplicationId(), id); + calloc.free(idPtr); + }); +} From a5b664db7c7b053328202a20830dcdcc4010353c Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 10 Dec 2021 16:09:20 -0800 Subject: [PATCH 7/7] [tools] fix typo in tools (#4607) --- script/tool/lib/src/lint_android_command.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index a7b5c4f2e8bf..49b2181a4615 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -12,9 +12,9 @@ import 'common/package_looping_command.dart'; import 'common/process_runner.dart'; import 'common/repository_package.dart'; -/// Lint the CocoaPod podspecs and run unit tests. +/// Run 'gradlew lint'. /// -/// See https://guides.cocoapods.org/terminal/commands.html#pod_lib_lint. +/// See https://developer.android.com/studio/write/lint. class LintAndroidCommand extends PackageLoopingCommand { /// Creates an instance of the linter command. LintAndroidCommand(