--- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,7 +37,6 @@ google_fonts: ^6.1.0 - in_app_purchase: ^3.2.0 logging: ^1.2.0 mini_sprite: ^0.1.0 --- a/pubspec.lock +++ b/pubspec.lock @@ -229,46 +229,14 @@ url: "https://pub.dev" source: hosted version: "4.9.1" - in_app_purchase: - dependency: "direct main" - description: - name: in_app_purchase - sha256: "0e9510b80b0074e89ab0a8e0fc901439b779dc9ae575ab8d419253c6e1627716" - url: "https://pub.dev" - source: hosted - version: "3.3.0" - in_app_purchase_android: - dependency: transitive - description: - name: in_app_purchase_android - sha256: c04e2cad0470fc868cb0ea06477648f003220b1b6612909db83528ca83ac0905 - url: "https://pub.dev" - source: hosted - version: "0.5.2" - in_app_purchase_platform_interface: - dependency: transitive - description: - name: in_app_purchase_platform_interface - sha256: "0b0076cac8ce4fa7048f01e76af8b123aeb6a7c4e0dea2a5206d6664454f3e36" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - in_app_purchase_storekit: - dependency: transitive - description: - name: in_app_purchase_storekit - sha256: "702a23c3d2ddc177b075d521d264900e82f01663881e4ef3ce17775de298c0e3" - url: "https://pub.dev" - source: hosted - version: "0.4.11" intl: dependency: "direct dev" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.20.3" jni: dependency: transitive description: @@ -293,14 +261,6 @@ url: "https://pub.dev" source: hosted version: "1.0.0" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" - url: "https://pub.dev" - source: hosted - version: "4.12.0" l10n_languages: dependency: transitive description: @@ -353,10 +313,10 @@ dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -369,10 +329,10 @@ dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" mini_sprite: dependency: "direct main" description: @@ -694,10 +654,10 @@ dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" typed_data: dependency: transitive description: @@ -726,10 +686,10 @@ dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: fa49e1674cb84acb900141714e6c6e1f9ae099827c24d141430ff6819db4e1bc url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.4" vm_service: dependency: transitive description: --- a/lib/ads/iap.dart +++ b/lib/ads/iap.dart @@ -1,11 +1,5 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:in_app_purchase/in_app_purchase.dart'; -import 'package:logging/logging.dart'; +import 'package:flutter/material.dart'; import 'package:ricochlime/ads/iap_state.dart'; -import 'package:ricochlime/utils/stows.dart'; import 'package:stow_plain/stow_plain.dart'; enum RicochlimeProduct { @@ -26,11 +20,7 @@ return null; } - /// The price of the product, formatted with currency symbol ("$0.99"). - /// If the product details are not loaded yet, returns "?". - String get price => _details[this]?.price ?? '\$\$'; - bool get isPriceLoaded => _details.containsKey(this); - static Map _details = {}; + String get price => '\$\$'; PlainStow get state => _states![this]!; static Map>? _states; @@ -46,113 +36,14 @@ } abstract final class RicochlimeIAP { - static StreamSubscription>? _subscription; - static final _log = Logger('RicochlimeIAP'); - - static final _inAppPurchasesSupported = - !kIsWeb && (Platform.isAndroid || Platform.isIOS || Platform.isMacOS); @visibleForTesting static bool? forceInAppPurchasesSupported; static bool get inAppPurchasesSupported => - forceInAppPurchasesSupported ?? _inAppPurchasesSupported; - - static Future init() async { - RicochlimeProduct.init(); - - if (!inAppPurchasesSupported) return; - - unawaited(_subscription?.cancel()); - _subscription = InAppPurchase.instance.purchaseStream.listen( - _onData, - onDone: _onDone, - ); - - final response = await InAppPurchase.instance.queryProductDetails( - RicochlimeProduct.allIds, - ); - if (response.notFoundIDs.isNotEmpty) { - _log.warning('Some IAPs not found: ${response.notFoundIDs}'); - } - RicochlimeProduct._details = { - for (final product in response.productDetails) - .fromId(product.id)!: product, - }; - _log.info('IAPs loaded: ${RicochlimeProduct._details}'); - } + forceInAppPurchasesSupported ?? false; - static Future _onData(List purchaseDetailsList) async { - for (final purchaseDetails in purchaseDetailsList) { - switch (purchaseDetails.status) { - case .pending: - // TODO(adil192): show pending UI - break; - case .error: - case .canceled: - // TODO(adil192): show "purchase not completed" UI - break; - case .purchased: - case .restored: - _deliverProduct(purchaseDetails); - } - if (purchaseDetails.pendingCompletePurchase) { - await InAppPurchase.instance.completePurchase(purchaseDetails); - } - } - } - - static Future buy(RicochlimeProduct product) async { - final details = RicochlimeProduct._details[product]; - if (details == null) { - _log.severe('Product details not found: $product'); - return false; - } - - return product.consumable - ? InAppPurchase.instance.buyConsumable( - purchaseParam: PurchaseParam(productDetails: details), - ) - : InAppPurchase.instance.buyNonConsumable( - purchaseParam: PurchaseParam(productDetails: details), - ); - } - - static Future restorePurchases() => - InAppPurchase.instance.restorePurchases(); - - static void _onDone() { - _subscription?.cancel(); - } - - static void dispose() { - _subscription?.cancel(); - } - - static void _deliverProduct(PurchaseDetails purchaseDetails) { - assert( - purchaseDetails.status == .purchased || - purchaseDetails.status == .restored, - ); - - final product = RicochlimeProduct.fromId(purchaseDetails.productID); - if (product == null) { - _log.severe('Unknown product to deliver: ${purchaseDetails.productID}'); - return; - } - - if (product.consumable && purchaseDetails.status == .restored) { - // Don't restore consumable purchases (they've already been consumed) - _log.warning( - 'Attempted to restore consumable purchase: ' - '${purchaseDetails.productID}', - ); - return; - } - - switch (product) { - case .buy1000Coins: - stows.addCoins(1000, allowOverMax: true); - case .buy5000Coins: - stows.addCoins(5000, allowOverMax: true); - } - } + static Future init() async => RicochlimeProduct.init(); + static void listen() {} + static void dispose() {} + static Future buy(RicochlimeProduct product) async => false; + static Future restorePurchases() async {} } --- a/lib/ads/_iap_foss.dart +++ b/lib/ads/_iap_foss.dart @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:ricochlime/ads/iap_state.dart'; -import 'package:stow_plain/stow_plain.dart'; - -enum RicochlimeProduct { - buy1000Coins('buy_1000_coins', consumable: true), - buy5000Coins('buy_5000_coins', consumable: true); - - const RicochlimeProduct(this.id, {required this.consumable}); - - final String id; - final bool consumable; - - static final allIds = values.map((product) => product.id).toSet(); - - static RicochlimeProduct? fromId(String id) { - for (final product in values) { - if (product.id == id) return product; - } - return null; - } - - String get price => '\$\$'; - - PlainStow get state => _states![this]!; - static Map>? _states; - @visibleForTesting - static void init() => _states ??= { - for (final product in values) - product: PlainStow( - 'iap_${product.id}_state', - .unpurchased, - codec: IAPState.codec, - ), - }; -} - -abstract final class RicochlimeIAP { - @visibleForTesting - static bool? forceInAppPurchasesSupported; - static bool get inAppPurchasesSupported => - forceInAppPurchasesSupported ?? false; - - static Future init() async => RicochlimeProduct.init(); - static void listen() {} - static void dispose() {} - static Future buy(RicochlimeProduct product) async => false; - static Future restorePurchases() async {} -} --- a/lib/utils/stows.dart +++ b/lib/utils/stows.dart @@ -20,8 +20,8 @@ final stylizedPageTransitions = PlainStow('stylizedPageTransitions', true); final biggerBullets = PlainStow('biggerBullets', false); - final bgmVolume = PlainStow('bgmVolume', 0.0); - final sfxVolume = PlainStow('sfxVolume', 0.0); + final bgmVolume = PlainStow('bgmVolume', 0.5); + final sfxVolume = PlainStow('sfxVolume', 0.5); final showUndoButton = PlainStow('showUndoButton', true); final showReflectionInAimGuide = PlainStow('showReflectionInAimGuide', true);