2023-12-21 09:14:40 +00:00
|
|
|
|
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';
|
2024-03-07 03:46:50 +00:00
|
|
|
|
import 'package:guru_app/financial/igb/igb_manager.dart';
|
|
|
|
|
|
import 'package:guru_app/financial/igb/igb_product.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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';
|
2024-03-07 03:46:50 +00:00
|
|
|
|
import 'package:guru_app/inventory/db/inventory_database.dart';
|
|
|
|
|
|
import 'package:guru_app/inventory/inventory_manager.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
|
import 'package:guru_app/test/test_guru_app_creator.dart';
|
2024-03-07 03:46:50 +00:00
|
|
|
|
import 'package:guru_utils/collection/collectionutils.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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<ProductStore<IapProduct>> _productStoreSubject =
|
2024-03-07 03:46:50 +00:00
|
|
|
|
BehaviorSubject.seeded(ProductStore());
|
2023-12-21 09:14:40 +00:00
|
|
|
|
|
|
|
|
|
|
ProductStore<IapProduct> get currentProductStore => _productStoreSubject.value;
|
|
|
|
|
|
|
|
|
|
|
|
AssetsStore<Asset> get currentIapAssetStore => IapManager.instance.purchasedStore;
|
|
|
|
|
|
|
|
|
|
|
|
AssetsStore<Asset> get currentRewardedStore => RewardManager.instance.rewardedStore;
|
|
|
|
|
|
|
|
|
|
|
|
Stream<ProductStore<IapProduct>> get observableProductStore => _productStoreSubject.stream;
|
|
|
|
|
|
|
|
|
|
|
|
Stream<AssetsStore<Asset>> get observableIapPurchased => IapManager.instance.observableAssetStore;
|
|
|
|
|
|
|
|
|
|
|
|
Stream<AssetsStore<Asset>> get observableRewarded => RewardManager.instance.observableAssetStore;
|
|
|
|
|
|
|
|
|
|
|
|
Stream<AssetsStore<Asset>> 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<int> get observableIgcBalance => IgcManager.instance.observableCurrentBalance;
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
Stream<Map<String, InventoryItem>> get observableInventoryItems =>
|
|
|
|
|
|
InventoryManager.instance.observableData;
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
Future restorePurchases() async {
|
|
|
|
|
|
return await IapManager.instance.restorePurchases();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future clearIapAssets() async {
|
|
|
|
|
|
return await IapManager.instance.clearAssetRecord();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void observeIapProducts(Set<TransactionIntent> 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<RewardProduct> buildRewardProduct(TransactionIntent intent) {
|
|
|
|
|
|
return RewardManager.instance.buildRewardProduct(intent);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
Future<IgbProduct> 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<bool> 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
Future<bool> 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);
|
2024-03-07 03:46:50 +00:00
|
|
|
|
} else if (product is IgbProduct) {
|
|
|
|
|
|
return await IgbManager.instance.redeem(product);
|
2023-12-21 09:14:40 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|