diff --git a/android/build.gradle b/android/build.gradle index 9e70743b..7933084d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -10,7 +10,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.2' classpath 'com.google.gms:google-services:3.2.1' classpath 'io.fabric.tools:gradle:1.+' } diff --git a/lib/features.dart b/lib/features.dart index 2a4c9d4b..e0ba859e 100644 --- a/lib/features.dart +++ b/lib/features.dart @@ -1,4 +1,4 @@ class Features { static bool perFolderConfig = false; - static bool purchaseProModeAvailable = false; + static bool purchaseProModeAvailable = true; } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index d72b3ad5..baea1a2a 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:gitjournal/core/flattened_notes_folder.dart'; +import 'package:gitjournal/iap.dart'; import 'package:provider/provider.dart'; import 'package:gitjournal/core/notes_folder_fs.dart'; @@ -17,6 +18,9 @@ class _HomeScreenState extends State { @override void initState() { super.initState(); + + InAppPurchases.confirmProPurchase(); + Future.delayed(Duration.zero, () { if (!mounted) return; final rootFolder = Provider.of(context); diff --git a/lib/screens/purchase_screen.dart b/lib/screens/purchase_screen.dart index 60ac096e..fd02d665 100644 --- a/lib/screens/purchase_screen.dart +++ b/lib/screens/purchase_screen.dart @@ -2,9 +2,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:gitjournal/analytics.dart'; +import 'package:gitjournal/.env.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'; +import 'package:gitjournal/settings.dart'; +import 'package:purchases_flutter/purchases_flutter.dart'; class PurchaseScreen extends StatefulWidget { @override @@ -12,13 +14,7 @@ class PurchaseScreen extends StatefulWidget { } class _PurchaseScreenState extends State { - String _platformVersion = 'Unknown'; - var _skus = ['sku_monthly_min']; - List _iapItems = []; - - StreamSubscription _purchaseUpdatedSubscription; - StreamSubscription _purchaseErrorSubscription; - StreamSubscription _conectionSubscription; + Offerings _offerings; @override void initState() { @@ -27,36 +23,18 @@ class _PurchaseScreenState extends State { } Future initPlatformState() async { - // prepare - var result = await FlutterInappPurchase.instance.initConnection; - print('result: $result'); + Purchases.setDebugLogsEnabled(true); + await Purchases.setup(environment['revenueCat']); + + Offerings offerings = await Purchases.getOfferings(); // 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; - try { - _iapItems = await FlutterInappPurchase.instance.getSubscriptions(_skus); - setState(() {}); - print("IAP ITEMS $_iapItems"); - } catch (err) { - print('getSubscriptions error: $err'); - } - - _conectionSubscription = - FlutterInappPurchase.connectionUpdated.listen((connected) { - print('connected: $connected'); - }); - - _purchaseUpdatedSubscription = - FlutterInappPurchase.purchaseUpdated.listen((productItem) { - print('purchase-updated: $productItem'); - }); - - _purchaseErrorSubscription = - FlutterInappPurchase.purchaseError.listen((purchaseError) { - print('purchase-error: $purchaseError'); + setState(() { + _offerings = offerings; }); } @@ -65,10 +43,11 @@ class _PurchaseScreenState extends State { var theme = Theme.of(context); var textTheme = theme.textTheme; - if (_iapItems.isEmpty) { + if (_offerings == null) { return const PurchaseLoadingScreen(); } - var iap = _iapItems[0]; + var offering = _offerings.current; + var monthly = offering.monthly; // FIXME: This screen needs to be made way way more beautiful // It's an extrememly important screen @@ -77,12 +56,7 @@ class _PurchaseScreenState extends State { children: [ Text('Pro Version', style: textTheme.display2), Text('Support GitJournal by going Pro', style: textTheme.subhead), - RaisedButton( - child: Text('Subscribe for ${iap.localizedPrice} / month'), - onPressed: () { - FlutterInappPurchase.instance.requestSubscription(_skus[0]); - }, - ), + PurchaseButton(monthly), ], mainAxisAlignment: MainAxisAlignment.spaceAround, ); @@ -104,6 +78,62 @@ class _PurchaseScreenState extends State { } } +class PurchaseButton extends StatelessWidget { + final Package package; + + PurchaseButton(this.package); + + @override + Widget build(BuildContext context) { + return RaisedButton( + child: Text('Subscribe for ${package.product.priceString} / month'), + onPressed: () async { + try { + var purchaserInfo = await Purchases.purchasePackage(package); + var isPro = purchaserInfo.entitlements.all["pro"].isActive; + if (isPro) { + Settings.instance.proMode = true; + Settings.instance.save(); + + // vHanda FIXME: Show some screen to indicate bought purchase? + Navigator.of(context).pop(); + return; + } + } on PlatformException catch (e) { + var errorCode = PurchasesErrorHelper.getErrorCode(e); + var errorContent = ""; + switch (errorCode) { + case PurchasesErrorCode.purchaseCancelledError: + errorContent = "User cancelled"; + break; + + case PurchasesErrorCode.purchaseNotAllowedError: + errorContent = "User not allowed to purchase"; + break; + + default: + errorContent = errorCode.toString(); + break; + } + + var dialog = AlertDialog( + title: const Text("Purchase Failed"), + content: Text(errorContent), + actions: [ + FlatButton( + child: const Text("OK"), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ); + await showDialog(context: context, builder: (context) => dialog); + } + return null; + }, + ); + } +} + class PurchaseLoadingScreen extends StatelessWidget { const PurchaseLoadingScreen({Key key}) : super(key: key); diff --git a/lib/widgets/app_drawer.dart b/lib/widgets/app_drawer.dart index ac66897e..27aa57b1 100644 --- a/lib/widgets/app_drawer.dart +++ b/lib/widgets/app_drawer.dart @@ -80,7 +80,7 @@ class AppDrawer extends StatelessWidget { ); }, ), - if (Features.purchaseProModeAvailable && !!Settings.instance.proMode) + if (Features.purchaseProModeAvailable && !Settings.instance.proMode) divider, _buildDrawerTile( context, diff --git a/pubspec.lock b/pubspec.lock index c4283317..cad09a9f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -207,13 +207,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1+1" - flutter_inapp_purchase: - dependency: "direct main" - description: - name: flutter_inapp_purchase - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.4" flutter_launcher_icons: dependency: "direct dev" description: @@ -537,6 +530,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.4.2" + purchases_flutter: + dependency: "direct main" + description: + name: purchases_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" quiver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8abd89b3..cad5469e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: font_awesome_flutter: ^8.7.0 sentry: ">=3.0.0 <4.0.0" equatable: ^1.1.0 - flutter_inapp_purchase: ^2.1.4 + purchases_flutter: ^1.1.0 dev_dependencies: flutter_launcher_icons: "^0.7.2"