import 'package:guru_app/financial/asset/assets_model.dart'; import 'package:guru_app/financial/asset/assets_store.dart'; import 'package:guru_app/financial/financial_manager.dart'; import 'package:guru_app/financial/iap/iap_manager.dart'; import 'package:guru_app/financial/iap/iap_model.dart'; import 'package:guru_app/financial/igb/igb_manager.dart'; import 'package:guru_app/financial/igb/igb_product.dart'; import 'package:guru_app/financial/igc/igc_manager.dart'; import 'package:guru_app/financial/igc/igc_model.dart'; import 'package:guru_app/financial/product/product_store.dart'; import 'package:guru_app/financial/reward/reward_manager.dart'; import 'package:guru_app/financial/reward/reward_model.dart'; import 'package:guru_app/guru_app.dart'; import 'package:guru_app/inventory/db/inventory_database.dart'; import 'package:guru_app/inventory/inventory_manager.dart'; import 'package:guru_app/test/test_guru_app_creator.dart'; import 'package:guru_utils/collection/collectionutils.dart'; import 'package:guru_utils/datetime/datetime_utils.dart'; import 'package:guru_utils/extensions/extensions.dart'; import 'package:guru_utils/controller/controller.dart'; /// Created by Haoyi on 2022/4/7 mixin AssetsAware on LifecycleController { final BehaviorSubject> _productStoreSubject = BehaviorSubject.seeded(ProductStore()); ProductStore get currentProductStore => _productStoreSubject.value; AssetsStore get currentIapAssetStore => IapManager.instance.purchasedStore; AssetsStore get currentRewardedStore => RewardManager.instance.rewardedStore; Stream> get observableProductStore => _productStoreSubject.stream; Stream> get observableIapPurchased => IapManager.instance.observableAssetStore; Stream> get observableRewarded => RewardManager.instance.observableAssetStore; Stream> get observableAssets => FinancialManager.instance.observableAssets; int _latestRefreshIapProductTimestamp = 0; bool get isIapCanceled => IapManager.instance.latestIapCause == IapCause.canceled; bool get isIapError => IapManager.instance.latestIapCause == IapCause.error; int get currentIgcBalance => IgcManager.instance.currentBalance; Stream get observableIgcBalance => IgcManager.instance.observableCurrentBalance; Stream> get observableInventoryItems => InventoryManager.instance.observableData; Future restorePurchases() async { return await IapManager.instance.restorePurchases(); } Future clearIapAssets() async { return await IapManager.instance.clearAssetRecord(); } void observeIapProducts(Set intents) { addSubscription(IapManager.instance.observableProductDetails.listen((details) async { final productStore = await IapManager.instance.buildProducts(intents); _productStoreSubject.addEx(productStore); })); } void refreshIapProducts() async { final now = DateTimeUtils.currentTimeInMillis(); if (now - _latestRefreshIapProductTimestamp > DateTimeUtils.minuteInMillis) { IapManager.instance.refreshProducts(); _latestRefreshIapProductTimestamp = now; } else { Log.w("refreshIapProducts Too Frequency!", tag: "IAP"); } } Future buildRewardProduct(TransactionIntent intent) { return RewardManager.instance.buildRewardProduct(intent); } Future buildIgbProduct(TransactionIntent intent) { return IgbManager.instance.buildIgbProduct(intent); } int getInventoryBalance(String sku) { return InventoryManager.instance.getData(sku)?.balance ?? 0; } TimeSensitiveData getInventoryTimeSensitiveData(String sku) { return InventoryManager.instance.getData(sku)?.timeSensitive ?? const TimeSensitiveData(); } /// 使用指定[sku]的道具,[amount]为使用数量,[action]的行为,[scene]为使用场景 /// useProp最终会得到一个行为上的收益,因此这里为了方便针对道具使用进行统一的行为分析 /// 这里的 [action]和[scene]最终会通过 spend_virtual_currency 事件进行统一的统计 /// propCategory可以参照 [PropCategory] 中的定义 /// 具体参数对照如下: /// - **`item_name`**: [intent] 如果这里指定了使用道具的意图,那么这里就会使用这个意图,否则使用场景, /// - **`item_category`**: [category], /// - **`virtual_currency_name`**: [propSku], /// - **`value`**: [amount], /// - **`balance`**: balance, /// - **`scene`**: [scene], /// - **`level_name`**: levelName /// /// 返回值为是否成功使用道具,false表示道具不足,true表示使用成功 /// Future useProp( String propSku, String scene, { int amount = 1, String? intent, String category = PropCategory.boosts, bool timeSensitiveOnly = false, int? transactionTs, }) async { final manifest = Manifest.action(category, scene, extras: CollectionUtils.filterOutNulls({ ExtraReservedField.contentId: intent ?? scene, // 如果这里指定了使用道具的意图,那么这里就会使用这个意图,否则使用场景 ExtraReservedField.transactionTs: transactionTs // 如果这里指定了交易时间,那么就会使用这个时间,否则使用当前时间 })); return await InventoryManager.instance.consume( [StockItem.consumable(propSku, amount)], manifest, timeSensitiveOnly: timeSensitiveOnly); } Future requestProduct(Product product, {String from = ""}) async { if (product is IapProduct) { return await IapManager.instance.buy(product); } else if (product is IgcProduct) { return await IgcManager.instance.purchase(product); } else if (product is RewardProduct) { return await RewardManager.instance.claim(product); } else if (product is IgbProduct) { return await IgbManager.instance.redeem(product); } else { return false; } } }