Commit 83fae7df authored by wanglei's avatar wanglei

....

parent 2beef962
......@@ -107,8 +107,9 @@ dependencies {
//firebase
implementation platform('com.google.firebase:firebase-bom:32.3.1')
implementation 'com.google.firebase:firebase-analytics:21.6.2'
implementation("com.google.firebase:firebase-messaging")
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'com.google.firebase:firebase-analytics:21.6.2'
//google 内购订阅
......
......@@ -25,9 +25,6 @@
android:supportsRtl="true"
android:theme="@style/Theme.DataRecovery"
tools:targetApi="34">
<activity
android:name=".billing.BillingActivity"
android:exported="false" />
<activity
android:name=".activity.SplashActivity"
android:exported="true"
......
......@@ -9,7 +9,6 @@ import com.base.filerecoveryrecyclebin.activity.SplashActivity
import com.base.filerecoveryrecyclebin.ads.AdmobMaxHelper
import com.base.filerecoveryrecyclebin.ads.admob.AdmobOpenUtils
import com.base.filerecoveryrecyclebin.bean.ConstObject.ifAgreePrivacy
import com.base.filerecoveryrecyclebin.billing.BillingClientLifecycle
import com.base.filerecoveryrecyclebin.fcm.FCMManager
import com.base.filerecoveryrecyclebin.fcm.RecoveryTimerManager
import com.base.filerecoveryrecyclebin.fcm.ScreenStatusReceiver
......@@ -32,8 +31,6 @@ class MyApplication : BaseApplication() {
private val TAG = "MyApplication"
var uuid = ""
val billingClientLifecycle: BillingClientLifecycle
get() = BillingClientLifecycle.getInstance(this)
companion object {
@JvmField
......
......@@ -7,10 +7,7 @@ import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.base.filerecoveryrecyclebin.adapter.MediaAdapter
import com.base.filerecoveryrecyclebin.ads.AdmobMaxHelper
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils
import com.base.filerecoveryrecyclebin.ads.admob.AdmobInterstitialUtils.showInterAdSp
import com.base.filerecoveryrecyclebin.ads.admob.AdmobNativeUtils
import com.base.filerecoveryrecyclebin.ads.max.AdMaxInterstitialUtils
import com.base.filerecoveryrecyclebin.bean.MediaBean
import com.base.filerecoveryrecyclebin.bean.MediaTimeBean
import com.base.filerecoveryrecyclebin.databinding.ActivityRepeatBinding
......
......@@ -6,6 +6,7 @@ import android.util.Log;
import com.base.filerecoveryrecyclebin.BuildConfig;
import com.base.filerecoveryrecyclebin.help.BaseApplication;
import com.base.filerecoveryrecyclebin.utils.AppPreferences;
import com.base.filerecoveryrecyclebin.utils.EventUtils;
import com.base.filerecoveryrecyclebin.utils.LogEx;
......@@ -18,37 +19,47 @@ import java.util.Date;
import java.util.Locale;
public class AdDisplayUtils {
private static final int DEFAULT_MAX_AD_DISPLAY_COUNT = 45; // 总广告展示次数限制默认值
private static final int DEFAULT_MAX_AD_CLICK_COUNT = 10; // 单个广告点击次数限制默认值
private static final int DEFAULT_MAX_AD_REQUEST_FAIL_COUNT = 20; // 单个广告点击次数限制默认值
private String TAG = "AdDisplayUtils";
public static final int DEFAULT_MAX_AD_REQUEST_COUNT = 100; // 广告请求次数限制默认值
public static final int DEFAULT_MAX_AD_REQUEST_FAIL_COUNT = 20; // 单个广告点击次数限制默认值
public static final int DEFAULT_MAX_AD_DISPLAY_COUNT = 45; // 总广告展示次数限制默认值
public static final int DEFAULT_MAX_AD_CLICK_COUNT = 10; // 单个广告点击次数限制默认值
private static final String AD_PREFS_NAME = "ad_prefs"; // SharedPreferences 名称
private static final String AD_DISPLAY_COUNT_KEY = "ad_display_count"; // 广告展示次数的键
private static final String AD_CLICK_COUNT_KEY = "ad_click_count"; // 广告点击次数的键
private static final String MAX_AD_DISPLAY_COUNT_KEY = "max_ad_display_count"; // 总广告展示次数限制的键
private static final String MAX_AD_CLICK_COUNT_KEY = "max_ad_click_count"; // 单个广告点击次数限制的键
private static final String MAX_AD_REQUEST_FAIL_COUNT_KEY = "max_ad_request_fail_count"; // 单个广告点击次数限制的键
private static final String AD_REQUEST_FAIL_COUNT_KEY = "ad_request_fail_count"; // 单个广告点击次数限制的键
private static AdDisplayUtils instance; // 单例对象
private int adDisplayCount = 0; // 当前广告展示次数
private int adClickCount = 0; // 当前广告点击次数
private int maxAdDisplayCount; // 总广告展示次数限制
private int maxAdClickCount; // 单个广告点击次数限制
private int maxAdRequestFailCount; // 请求失败总
private int adRequestFailCount = 0; // 请求失败当前
private String currentDate; // 当前日期
public void saveSp() {
this.maxAdRequestCount = Integer.parseInt(AppPreferences.getInstance().getString("adMaxRequestCount", String.valueOf(DEFAULT_MAX_AD_REQUEST_COUNT)));
saveMaxAdRequestCount();
this.maxAdRequestFailCount = Integer.parseInt(AppPreferences.getInstance().getString("adRequestFailCount", String.valueOf(DEFAULT_MAX_AD_REQUEST_FAIL_COUNT)));
saveMaxAdRequestFailCount();
this.maxAdDisplayCount = Integer.parseInt(AppPreferences.getInstance().getString("adShowCount", String.valueOf(DEFAULT_MAX_AD_DISPLAY_COUNT)));
saveMaxAdDisplayCount();
this.maxAdClickCount = Integer.parseInt(AppPreferences.getInstance().getString("adClickCount", String.valueOf(DEFAULT_MAX_AD_CLICK_COUNT)));
saveMaxAdClickCount();
}
private AdDisplayUtils() {
currentDate = getCurrentDate();
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
maxAdRequestCount = prefs.getInt(MAX_AD_REQUEST_COUNT_KEY, DEFAULT_MAX_AD_REQUEST_COUNT);
maxAdRequestFailCount = prefs.getInt(MAX_AD_REQUEST_FAIL_COUNT_KEY, DEFAULT_MAX_AD_REQUEST_FAIL_COUNT);
maxAdDisplayCount = prefs.getInt(MAX_AD_DISPLAY_COUNT_KEY, DEFAULT_MAX_AD_DISPLAY_COUNT);
maxAdClickCount = prefs.getInt(MAX_AD_CLICK_COUNT_KEY, DEFAULT_MAX_AD_CLICK_COUNT);
maxAdRequestFailCount = prefs.getInt(MAX_AD_REQUEST_FAIL_COUNT_KEY, DEFAULT_MAX_AD_REQUEST_FAIL_COUNT);
adRequestCount = prefs.getInt(getAdRequestCountKey(), 0);
adRequestFailCount = prefs.getInt(getAdRequestFailCountKey(), 0);
adDisplayCount = prefs.getInt(getAdDisplayCountKey(), 0);
adClickCount = prefs.getInt(getAdClickCountKey(), 0);
adRequestFailCount = prefs.getInt(getAdRequestFailCountKey(), 0);
}
public static synchronized AdDisplayUtils getInstance() {
......@@ -58,53 +69,104 @@ public class AdDisplayUtils {
return instance;
}
public boolean shouldDisplayAd() {
return adDisplayCount < getMaxAdDisplayCount();
public boolean shouldSendAdRequest() {
return adRequestCount < maxAdRequestCount;
}
public boolean shouldIncrementRequestFailAd() {
return adRequestFailCount < getMaxAdRequestFailCount();
return adRequestFailCount < maxAdRequestFailCount;
}
public boolean shouldDisplayAd() {
return adDisplayCount < maxAdClickCount;
}
public boolean shouldIncrementClickCount() {
return adClickCount < getMaxAdClickCount();
return adClickCount < maxAdClickCount;
}
public boolean shouldShowAd(String ad_unit) {
if (BuildConfig.DEBUG) {
return true;
}
boolean s = shouldDisplayAd() && shouldIncrementClickCount() && shouldIncrementRequestFailAd();
if (!s) {
boolean shouldDisplayAd = shouldDisplayAd();
boolean shouldIncrementClickCount = shouldIncrementClickCount();
boolean shouldIncrementRequestFailAd = shouldIncrementRequestFailAd();
boolean shouldSendAdRequest = shouldSendAdRequest();
LogEx.INSTANCE.logDebug(TAG, "shouldSendAdRequest=" + shouldSendAdRequest, false);
LogEx.INSTANCE.logDebug(TAG, "shouldIncrementRequestFailAd=" + shouldIncrementRequestFailAd, false);
LogEx.INSTANCE.logDebug(TAG, "shouldIncrementClickCount=" + shouldIncrementClickCount, false);
LogEx.INSTANCE.logDebug(TAG, "shouldDisplayAd=" + shouldDisplayAd, false);
boolean show = shouldIncrementRequestFailAd && shouldSendAdRequest && shouldDisplayAd && shouldIncrementClickCount;
if (!show) {
LogEx.INSTANCE.logDebug("glc", "!shouldShowAd", false);
JSONObject obj2 = new JSONObject();
try {
obj2.put("reason", "ad limit");
obj2.put("shouldSendAdRequest", shouldSendAdRequest);
obj2.put("shouldIncrementRequestFailAd", shouldIncrementRequestFailAd);
obj2.put("shouldDisplayAd", shouldDisplayAd);
obj2.put("shouldIncrementClickCount", shouldIncrementClickCount);
obj2.put("ad_unit", ad_unit);
EventUtils.INSTANCE.event("ad_show_error", null, obj2, false);
} catch (JSONException e) {
} catch (JSONException ignored) {
}
}
return s;
return show;
}
public void incrementAdDisplayCount() {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adDisplayCount = 0;
}
adDisplayCount++;
saveAdDisplayCount();
//region AdRequestCount
private int maxAdRequestCount = 0; // 最大广告请求次数
private int adRequestCount = 0; // 当前广告请求次数
private static final String MAX_AD_REQUEST_COUNT_KEY = "max_ad_request_count"; // 广告请求次数限制的键
private static final String AD_REQUEST_COUNT_KEY = "ad_request_count";//广告请求次数
private void saveMaxAdRequestCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(MAX_AD_REQUEST_COUNT_KEY, maxAdRequestCount);
editor.apply();
}
public void incrementAdClickCount() {
public void incrementAdRequestCount() {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adClickCount = 0;
adRequestCount = 0;
}
adClickCount++;
saveAdClickCount();
adRequestCount++;
saveAdRequestCount();
}
private String getAdRequestCountKey() {
return AD_REQUEST_COUNT_KEY + "_" + currentDate;
}
private void saveAdRequestCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(getAdRequestCountKey(), adRequestCount);
editor.apply();
}
//endregion
//region AdRequestFailCount
private int maxAdRequestFailCount; // 请求失败总
private int adRequestFailCount = 0; // 请求失败当前
private static final String MAX_AD_REQUEST_FAIL_COUNT_KEY = "max_ad_request_fail_count"; // 单个广告点击次数限制的键
private static final String AD_REQUEST_FAIL_COUNT_KEY = "ad_request_fail_count"; // 单个广告点击次数限制的键
private void saveMaxAdRequestFailCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(MAX_AD_REQUEST_FAIL_COUNT_KEY, maxAdRequestFailCount);
editor.apply();
}
public void incrementAdRequestFailCount() {
......@@ -117,68 +179,60 @@ public class AdDisplayUtils {
Log.d("glc", "广告请求失败:" + adRequestFailCount);
}
public void setAdClickCount(int s) {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adClickCount = 0;
}
adClickCount = s;
saveAdClickCount();
}
public String getCurrentDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
Date currentDate = Calendar.getInstance().getTime();
return dateFormat.format(currentDate);
}
private String getAdDisplayCountKey() {
return AD_DISPLAY_COUNT_KEY + "_" + currentDate;
}
private String getAdClickCountKey() {
return AD_CLICK_COUNT_KEY + "_" + currentDate;
private String getAdRequestFailCountKey() {
return AD_REQUEST_FAIL_COUNT_KEY + "_" + currentDate;
}
private void saveAdDisplayCount() {
private void saveAdRequestFailCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(getAdDisplayCountKey(), adDisplayCount);
editor.putInt(getAdRequestFailCountKey(), adRequestFailCount);
editor.apply();
}
//endregion
private void saveAdClickCount() {
//region AdDisplayCount
private int maxAdDisplayCount; // 总广告展示次数限制
private int adDisplayCount = 0; // 当前广告展示次数
private static final String MAX_AD_DISPLAY_COUNT_KEY = "max_ad_display_count"; // 总广告展示次数限制的键
private static final String AD_DISPLAY_COUNT_KEY = "ad_display_count"; // 广告展示次数的键
private void saveMaxAdDisplayCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(getAdClickCountKey(), adClickCount);
editor.putInt(MAX_AD_DISPLAY_COUNT_KEY, maxAdDisplayCount);
editor.apply();
}
private int getMaxAdDisplayCount() {
return maxAdDisplayCount;
}
public void setMaxAdDisplayCount(int maxAdDisplayCount) {
this.maxAdDisplayCount = maxAdDisplayCount;
saveMaxAdDisplayCount();
public void incrementAdDisplayCount() {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adDisplayCount = 0;
}
adDisplayCount++;
saveAdDisplayCount();
}
public int getMaxAdClickCount() {
return maxAdClickCount;
}
public void setMaxAdClickCount(int maxAdClickCount) {
this.maxAdClickCount = maxAdClickCount;
saveMaxAdClickCount();
private String getAdDisplayCountKey() {
return AD_DISPLAY_COUNT_KEY + "_" + currentDate;
}
private void saveMaxAdDisplayCount() {
private void saveAdDisplayCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(MAX_AD_DISPLAY_COUNT_KEY, maxAdDisplayCount);
editor.putInt(getAdDisplayCountKey(), adDisplayCount);
editor.apply();
}
//endregion
//region AdClickCount
public int maxAdClickCount; // 单个广告点击次数限制
private int adClickCount = 0; // 当前广告点击次数
private static final String MAX_AD_CLICK_COUNT_KEY = "max_ad_click_count"; // 单个广告点击次数限制的键
private static final String AD_CLICK_COUNT_KEY = "ad_click_count"; // 广告点击次数的键
private void saveMaxAdClickCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
......@@ -187,30 +241,43 @@ public class AdDisplayUtils {
editor.apply();
}
public int getMaxAdRequestFailCount() {
return maxAdRequestFailCount;
public void incrementAdClickCount() {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adClickCount = 0;
}
adClickCount++;
saveAdClickCount();
}
public void setMaxAdRequestFailCount(int maxAdRequestFailCount) {
this.maxAdRequestFailCount = maxAdRequestFailCount;
saveMaxAdRequestFailCount();
private String getAdClickCountKey() {
return AD_CLICK_COUNT_KEY + "_" + currentDate;
}
private void saveMaxAdRequestFailCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(MAX_AD_REQUEST_FAIL_COUNT_KEY, maxAdRequestFailCount);
editor.apply();
public void setAdClickCount(int s) {
if (!currentDate.equals(getCurrentDate())) {
currentDate = getCurrentDate();
adClickCount = 0;
}
adClickCount = s;
saveAdClickCount();
}
private void saveAdRequestFailCount() {
private void saveAdClickCount() {
SharedPreferences prefs = BaseApplication.context.getSharedPreferences(AD_PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(getAdRequestFailCountKey(), adRequestFailCount);
editor.putInt(getAdClickCountKey(), adClickCount);
editor.apply();
}
//endregion
private String getAdRequestFailCountKey() {
return AD_REQUEST_FAIL_COUNT_KEY + "_" + currentDate;
public String getCurrentDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
Date currentDate = Calendar.getInstance().getTime();
return dateFormat.format(currentDate);
}
}
......@@ -21,6 +21,8 @@ object AdmobMaxHelper {
private val TAG = "AdmobMaxHelper"
var isAdInit = AtomicBoolean(false)
//Begin
fun initAdmobMaxAd() {
if (ConfigHelper.admobTrueMaxFlase) {
if (!isAdInit.get()) {
......@@ -34,7 +36,7 @@ object AdmobMaxHelper {
}
}
}
//End end
fun isOpenAdLoaded(): Boolean {
return if (ConfigHelper.admobTrueMaxFlase) {
AdmobOpenUtils.isOpenAdLoaded()
......
......@@ -182,6 +182,7 @@ object AdMaxInterstitialUtils {
obj.put("ad_type", "interAd")
EventUtils.event("ad_pull_start", ext = obj)
interstitialAd?.loadAd()
AdDisplayUtils.getInstance().incrementAdRequestCount()
return true
}
return false
......
......@@ -170,6 +170,7 @@ object AdMaxOpenUtils {
obj.put("ad_type", "openAd")
EventUtils.event("ad_pull_start", ext = obj)
appOpenAd?.loadAd()
AdDisplayUtils.getInstance().incrementAdRequestCount()
return true
}
return false
......
package com.base.filerecoveryrecyclebin.billing
import com.base.filerecoveryrecyclebin.databinding.ActivityBillingBinding
import com.base.filerecoveryrecyclebin.help.BaseActivity
class BillingActivity : BaseActivity<ActivityBillingBinding>() {
override val binding: ActivityBillingBinding by lazy {
ActivityBillingBinding.inflate(layoutInflater)
}
override fun initView() {
}
}
\ No newline at end of file
//package com.base.filerecoveryrecyclebin.billing
//
//import com.base.filerecoveryrecyclebin.databinding.ActivityBillingBinding
//import com.base.filerecoveryrecyclebin.help.BaseActivity
//
//class BillingActivity : BaseActivity<ActivityBillingBinding>() {
//
// override val binding: ActivityBillingBinding by lazy {
// ActivityBillingBinding.inflate(layoutInflater)
// }
//
//
// override fun initView() {
//
// }
//
//}
\ No newline at end of file
package com.base.filerecoveryrecyclebin.billing
import android.content.Context
import android.util.Log
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingClientStateListener
import com.android.billingclient.api.BillingResult
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.ProductDetailsResponseListener
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchasesResponseListener
import com.android.billingclient.api.PurchasesUpdatedListener
import com.android.billingclient.api.QueryPurchasesParams
import com.base.filerecoveryrecyclebin.bean.BiliConstants
import com.base.filerecoveryrecyclebin.bean.BillingResponse
import com.base.filerecoveryrecyclebin.help.BaseApplication
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class BillingClientLifecycle(
private val applicationContext: Context,
private val coroutineScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
) : DefaultLifecycleObserver,
PurchasesUpdatedListener,
BillingClientStateListener,
PurchasesResponseListener,
ProductDetailsResponseListener {
private val TAG = "BillingClientLifecycle"
/**
* Instantiate a new BillingClient instance.
*/
private lateinit var billingClient: BillingClient
//订阅价格
private val _subscriptionPurchases = MutableStateFlow<List<Purchase>>(emptyList())
val subscriptionPurchases = _subscriptionPurchases.asStateFlow()
//基础商品信息
val basicSubProductWithProductDetails = MutableLiveData<ProductDetails?>()
//高级商品信息
val premiumSubProductWithProductDetails = MutableLiveData<ProductDetails?>()
override fun onCreate(owner: LifecycleOwner) {
Log.d(TAG, "onCreate")
billingClient = BillingClient.newBuilder(BaseApplication.context)
.setListener(this)
// .enablePendingPurchases() // Not used for subscriptions.
.build()
if (!billingClient.isReady) {
Log.d(TAG, "BillingClient: Start connection...")
billingClient.startConnection(this)
}
}
override fun onDestroy(owner: LifecycleOwner) {
Log.d(TAG, "onDestroy")
if (billingClient.isReady) {
Log.d(TAG, "BillingClient can only be used once -- closing connection")
// BillingClient can only be used once.
// After calling endConnection(), we must create a new BillingClient.
billingClient.endConnection()
}
}
/**
* 更新价格监听
* PurchasesUpdatedListener
*/
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: MutableList<Purchase>?) {
val responseCode = billingResult.responseCode
val debugMessage = billingResult.debugMessage
Log.d(TAG, "onPurchasesUpdated: $responseCode $debugMessage")
when (responseCode) {
BillingClient.BillingResponseCode.OK -> {
if (purchases == null) {
Log.d(TAG, "onPurchasesUpdated: null purchase list")
processPurchases(null)
} else {
processPurchases(purchases)
}
}
BillingClient.BillingResponseCode.USER_CANCELED -> {
Log.i(TAG, "onPurchasesUpdated: User canceled the purchase")
}
BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
Log.i(TAG, "onPurchasesUpdated: The user already owns this item")
}
BillingClient.BillingResponseCode.DEVELOPER_ERROR -> {
Log.e(
TAG, "onPurchasesUpdated: Developer error means that Google Play does " +
"not recognize the configuration. If you are just getting started, " +
"make sure you have configured the application correctly in the " +
"Google Play Console. The product ID must match and the APK you " +
"are using must be signed with release keys."
)
}
}
}
private var cachedPurchasesList: List<Purchase>? = null
/**
* 重新发布价格
*/
private fun processPurchases(purchasesList: List<Purchase>?) {
purchasesList?.let { list ->
if (isUnchangedPurchaseList(list)) {
Log.d(TAG, "processPurchases: Purchase list has not changed")
return
}
coroutineScope.launch {
val subscriptionPurchaseList = list.filter { purchase ->
purchase.products.any { product ->
product in listOf(BiliConstants.PREMIUM_PRODUCT, BiliConstants.BASIC_PRODUCT)
}
}
_subscriptionPurchases.emit(subscriptionPurchaseList)
}
}
}
/**
* 对比旧的价格
*/
private fun isUnchangedPurchaseList(purchasesList: List<Purchase>): Boolean {
val isUnchanged = purchasesList == cachedPurchasesList
if (!isUnchanged) {
cachedPurchasesList = purchasesList
}
return isUnchanged
}
/**
* BillingClientStateListener
*/
override fun onBillingServiceDisconnected() {
Log.d(TAG, "onBillingServiceDisconnected")
}
/**
* BillingClientStateListener
*/
override fun onBillingSetupFinished(billingResult: BillingResult) {
Log.d(TAG, "onBillingSetupFinished")
val responseCode = billingResult.responseCode
val debugMessage = billingResult.debugMessage
Log.d(TAG, "onBillingSetupFinished: $responseCode $debugMessage")
if (responseCode == BillingClient.BillingResponseCode.OK) {
querySubscriptionPurchases()
}
}
/**
* 查询订阅价格
*/
private fun querySubscriptionPurchases() {
if (!billingClient.isReady) {
Log.e(TAG, "querySubscriptionPurchases: BillingClient is not ready")
billingClient.startConnection(this)
}
billingClient.queryPurchasesAsync(
QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
.build(), this
)
}
companion object {
@Volatile
private var INSTANCE: BillingClientLifecycle? = null
fun getInstance(applicationContext: Context): BillingClientLifecycle =
INSTANCE ?: synchronized(this) {
INSTANCE ?: BillingClientLifecycle(applicationContext).also { INSTANCE = it }
}
}
/**
* PurchasesResponseListener
* 购买价格响应
*/
override fun onQueryPurchasesResponse(billingResult: BillingResult, purchasesList: MutableList<Purchase>) {
processPurchases(purchasesList)
}
/**
* ProductDetailsResponseListener
* 产品细节响应
*/
override fun onProductDetailsResponse(
billingResult: BillingResult,
productDetailsList: MutableList<ProductDetails>
) {
val response = BillingResponse(billingResult.responseCode)
val debugMessage = billingResult.debugMessage
when {
response.isOk -> {
processProductDetails(productDetailsList)
}
response.isTerribleFailure -> {
// These response codes are not expected.
Log.w(
TAG,
"onProductDetailsResponse - Unexpected error: ${response.code} $debugMessage"
)
}
else -> {
Log.e(TAG, "onProductDetailsResponse: ${response.code} $debugMessage")
}
}
}
private val LIST_OF_SUBSCRIPTION_PRODUCTS = listOf(
BiliConstants.BASIC_PRODUCT,
BiliConstants.PREMIUM_PRODUCT,
)
private fun processProductDetails(productDetailsList: MutableList<ProductDetails>) {
if (productDetailsList.isEmpty()) {
val productDetailsCount = LIST_OF_SUBSCRIPTION_PRODUCTS.size
Log.e(
TAG,
"productDetailsCount=$productDetailsCount, Check to see if the products you requested are correctly published in the Google Play Console."
)
postProductDetails(emptyList())
} else {
postProductDetails(productDetailsList)
}
}
private fun postProductDetails(productDetailsList: List<ProductDetails>) {
productDetailsList.forEach { productDetails ->
when (productDetails.productType) {
BillingClient.ProductType.SUBS -> {//订阅
if (productDetails.productId == BiliConstants.PREMIUM_PRODUCT) {
premiumSubProductWithProductDetails.postValue(productDetails)
} else if (productDetails.productId == BiliConstants.BASIC_PRODUCT) {
basicSubProductWithProductDetails.postValue(productDetails)
}
}
}
}
}
}
\ No newline at end of file
//package com.base.filerecoveryrecyclebin.billing
//
//import android.content.Context
//import android.util.Log
//import androidx.lifecycle.DefaultLifecycleObserver
//import androidx.lifecycle.LifecycleOwner
//import androidx.lifecycle.MutableLiveData
//import com.android.billingclient.api.BillingClient
//import com.android.billingclient.api.BillingClientStateListener
//import com.android.billingclient.api.BillingResult
//import com.android.billingclient.api.ProductDetails
//import com.android.billingclient.api.ProductDetailsResponseListener
//import com.android.billingclient.api.Purchase
//import com.android.billingclient.api.PurchasesResponseListener
//import com.android.billingclient.api.PurchasesUpdatedListener
//import com.android.billingclient.api.QueryPurchasesParams
//import com.base.filerecoveryrecyclebin.bean.BiliConstants
//import com.base.filerecoveryrecyclebin.bean.BillingResponse
//import com.base.filerecoveryrecyclebin.help.BaseApplication
//import kotlinx.coroutines.CoroutineScope
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.SupervisorJob
//import kotlinx.coroutines.flow.MutableStateFlow
//import kotlinx.coroutines.flow.asStateFlow
//import kotlinx.coroutines.launch
//
//class BillingClientLifecycle(
// private val applicationContext: Context,
// private val coroutineScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
//) : DefaultLifecycleObserver,
// PurchasesUpdatedListener,
// BillingClientStateListener,
// PurchasesResponseListener,
// ProductDetailsResponseListener {
//
// private val TAG = "BillingClientLifecycle"
//
// /**
// * Instantiate a new BillingClient instance.
// */
// private lateinit var billingClient: BillingClient
//
// //订阅价格
// private val _subscriptionPurchases = MutableStateFlow<List<Purchase>>(emptyList())
// val subscriptionPurchases = _subscriptionPurchases.asStateFlow()
//
// //基础商品信息
// val basicSubProductWithProductDetails = MutableLiveData<ProductDetails?>()
//
// //高级商品信息
// val premiumSubProductWithProductDetails = MutableLiveData<ProductDetails?>()
//
//
// override fun onCreate(owner: LifecycleOwner) {
// Log.d(TAG, "onCreate")
// billingClient = BillingClient.newBuilder(BaseApplication.context)
// .setListener(this)
//// .enablePendingPurchases() // Not used for subscriptions.
// .build()
// if (!billingClient.isReady) {
// Log.d(TAG, "BillingClient: Start connection...")
// billingClient.startConnection(this)
// }
// }
//
// override fun onDestroy(owner: LifecycleOwner) {
// Log.d(TAG, "onDestroy")
// if (billingClient.isReady) {
// Log.d(TAG, "BillingClient can only be used once -- closing connection")
// // BillingClient can only be used once.
// // After calling endConnection(), we must create a new BillingClient.
// billingClient.endConnection()
// }
// }
//
// /**
// * 更新价格监听
// * PurchasesUpdatedListener
// */
// override fun onPurchasesUpdated(billingResult: BillingResult, purchases: MutableList<Purchase>?) {
// val responseCode = billingResult.responseCode
// val debugMessage = billingResult.debugMessage
//
// Log.d(TAG, "onPurchasesUpdated: $responseCode $debugMessage")
// when (responseCode) {
// BillingClient.BillingResponseCode.OK -> {
// if (purchases == null) {
// Log.d(TAG, "onPurchasesUpdated: null purchase list")
// processPurchases(null)
// } else {
// processPurchases(purchases)
// }
// }
//
// BillingClient.BillingResponseCode.USER_CANCELED -> {
// Log.i(TAG, "onPurchasesUpdated: User canceled the purchase")
// }
//
// BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
// Log.i(TAG, "onPurchasesUpdated: The user already owns this item")
// }
//
// BillingClient.BillingResponseCode.DEVELOPER_ERROR -> {
// Log.e(
// TAG, "onPurchasesUpdated: Developer error means that Google Play does " +
// "not recognize the configuration. If you are just getting started, " +
// "make sure you have configured the application correctly in the " +
// "Google Play Console. The product ID must match and the APK you " +
// "are using must be signed with release keys."
// )
// }
// }
// }
//
// private var cachedPurchasesList: List<Purchase>? = null
//
// /**
// * 重新发布价格
// */
// private fun processPurchases(purchasesList: List<Purchase>?) {
// purchasesList?.let { list ->
// if (isUnchangedPurchaseList(list)) {
// Log.d(TAG, "processPurchases: Purchase list has not changed")
// return
// }
//
// coroutineScope.launch {
// val subscriptionPurchaseList = list.filter { purchase ->
// purchase.products.any { product ->
// product in listOf(BiliConstants.PREMIUM_PRODUCT, BiliConstants.BASIC_PRODUCT)
// }
// }
//
// _subscriptionPurchases.emit(subscriptionPurchaseList)
// }
// }
// }
//
// /**
// * 对比旧的价格
// */
// private fun isUnchangedPurchaseList(purchasesList: List<Purchase>): Boolean {
// val isUnchanged = purchasesList == cachedPurchasesList
// if (!isUnchanged) {
// cachedPurchasesList = purchasesList
// }
// return isUnchanged
// }
//
//
// /**
// * BillingClientStateListener
// */
// override fun onBillingServiceDisconnected() {
// Log.d(TAG, "onBillingServiceDisconnected")
// }
//
// /**
// * BillingClientStateListener
// */
// override fun onBillingSetupFinished(billingResult: BillingResult) {
// Log.d(TAG, "onBillingSetupFinished")
// val responseCode = billingResult.responseCode
// val debugMessage = billingResult.debugMessage
// Log.d(TAG, "onBillingSetupFinished: $responseCode $debugMessage")
//
// if (responseCode == BillingClient.BillingResponseCode.OK) {
// querySubscriptionPurchases()
// }
// }
//
// /**
// * 查询订阅价格
// */
// private fun querySubscriptionPurchases() {
// if (!billingClient.isReady) {
// Log.e(TAG, "querySubscriptionPurchases: BillingClient is not ready")
// billingClient.startConnection(this)
// }
//
// billingClient.queryPurchasesAsync(
// QueryPurchasesParams.newBuilder()
// .setProductType(BillingClient.ProductType.SUBS)
// .build(), this
// )
// }
//
// companion object {
// @Volatile
// private var INSTANCE: BillingClientLifecycle? = null
// fun getInstance(applicationContext: Context): BillingClientLifecycle =
// INSTANCE ?: synchronized(this) {
// INSTANCE ?: BillingClientLifecycle(applicationContext).also { INSTANCE = it }
// }
// }
//
// /**
// * PurchasesResponseListener
// * 购买价格响应
// */
// override fun onQueryPurchasesResponse(billingResult: BillingResult, purchasesList: MutableList<Purchase>) {
// processPurchases(purchasesList)
// }
//
// /**
// * ProductDetailsResponseListener
// * 产品细节响应
// */
// override fun onProductDetailsResponse(
// billingResult: BillingResult,
// productDetailsList: MutableList<ProductDetails>
// ) {
// val response = BillingResponse(billingResult.responseCode)
// val debugMessage = billingResult.debugMessage
//
// when {
// response.isOk -> {
// processProductDetails(productDetailsList)
// }
//
// response.isTerribleFailure -> {
// // These response codes are not expected.
// Log.w(
// TAG,
// "onProductDetailsResponse - Unexpected error: ${response.code} $debugMessage"
// )
// }
//
// else -> {
// Log.e(TAG, "onProductDetailsResponse: ${response.code} $debugMessage")
// }
//
// }
// }
//
// private val LIST_OF_SUBSCRIPTION_PRODUCTS = listOf(
// BiliConstants.BASIC_PRODUCT,
// BiliConstants.PREMIUM_PRODUCT,
// )
//
// private fun processProductDetails(productDetailsList: MutableList<ProductDetails>) {
// if (productDetailsList.isEmpty()) {
// val productDetailsCount = LIST_OF_SUBSCRIPTION_PRODUCTS.size
// Log.e(
// TAG,
// "productDetailsCount=$productDetailsCount, Check to see if the products you requested are correctly published in the Google Play Console."
// )
// postProductDetails(emptyList())
// } else {
// postProductDetails(productDetailsList)
// }
// }
//
// private fun postProductDetails(productDetailsList: List<ProductDetails>) {
// productDetailsList.forEach { productDetails ->
// when (productDetails.productType) {
// BillingClient.ProductType.SUBS -> {//订阅
// if (productDetails.productId == BiliConstants.PREMIUM_PRODUCT) {
// premiumSubProductWithProductDetails.postValue(productDetails)
// } else if (productDetails.productId == BiliConstants.BASIC_PRODUCT) {
// basicSubProductWithProductDetails.postValue(productDetails)
// }
// }
// }
// }
// }
//
//
//}
\ No newline at end of file
/*
* Copyright 2018 Google LLC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.base.filerecoveryrecyclebin.billing
import com.android.billingclient.api.Purchase
import com.base.filerecoveryrecyclebin.bean.BiliConstants
import com.base.filerecoveryrecyclebin.bean.SubscriptionStatus
/**
* Return subscription for the provided Product, if it exists.
*/
fun subscriptionForProduct(
subscriptions: List<SubscriptionStatus>?,
product: String
): SubscriptionStatus? {
subscriptions?.let {
for (subscription in it) {
if (subscription.product == product) {
return subscription
}
}
}
// User does not have the subscription.
return null
}
/**
* Return purchase for the provided Product, if it exists.
*/
fun purchaseForProduct(purchases: List<Purchase>?, product: String): Purchase? {
purchases?.let {
for (purchase in it) {
if (purchase.products[0] == product) {
return purchase
}
}
}
return null
}
/**
* This will return true if the Google Play Billing APIs have a record for the subscription.
* This will not always match the server's record of the subscription for this app user.
*
* Example: App user buys the subscription on a different device with a different Google
* account. The server will show that this app user has the subscription, even if the
* Google account on this device has not purchased the subscription.
* In this example, the method will return false.
*
* Example: The app user changes by signing out and signing into the app with a different
* email address. The server will show that this app user does not have the subscription,
* even if the Google account on this device has purchased the subscription.
* In this example, the method will return true.
*/
fun deviceHasGooglePlaySubscription(purchases: List<Purchase>?, product: String) =
purchaseForProduct(purchases, product) != null
/**
* This will return true if the server has a record for the subscription.
* Sometimes this will return true even if the Google Play Billing APIs return false.
*
* For local purchases that are rejected by the server, this app attaches the field
* subAlreadyOwned=true to the subscription object. This means that whenever
* [deviceHasGooglePlaySubscription] returns true, and the server has processed all purchase tokens,
* we also expect this method to return true.
*
* Example: App user buys the subscription on a different device with a different Google
* account. The server will show that this app user has the subscription, even if the
* Google account on this device has not purchased the subscription.
* In this example, the method will return true, even though [deviceHasGooglePlaySubscription]
* will return false.
*
* Example: The app user changes by signing out and signing into the app with a different
* email address. The server will show that this app user does not have the subscription,
* by returning an API response indicating that it is ALREADY_OWNED.
* even if the Google account on this device has purchased the subscription.
* In this example, the method will return true. This is the same as the result from
* [deviceHasGooglePlaySubscription].
*/
fun serverHasSubscription(subscriptions: List<SubscriptionStatus>?, product: String) =
subscriptionForProduct(subscriptions, product) != null
/**
* Returns true if the grace period option should be shown.
*/
// TODO need to be refactored like isBasicContent
fun isGracePeriod(subscription: SubscriptionStatus?) =
subscription != null &&
subscription.isEntitlementActive &&
subscription.isGracePeriod &&
!subscription.subAlreadyOwned
/**
* Returns true if the subscription restore option should be shown.
*/
// TODO need to be refactored like isBasicContent
fun isSubscriptionRestore(subscription: SubscriptionStatus?) =
subscription != null &&
subscription.isEntitlementActive &&
!subscription.willRenew &&
!subscription.subAlreadyOwned
/**
* Returns true if the basic content should be shown.
*/
val SubscriptionStatus?.isBasicContent: Boolean
get() =
this != null &&
isEntitlementActive &&
BiliConstants.BASIC_PRODUCT == product &&
!subAlreadyOwned
/**
* Returns true if premium content should be shown.
*/
// TODO need to be refactored like isBasicContent
fun isPremiumContent(subscription: SubscriptionStatus?) =
subscription != null &&
subscription.isEntitlementActive &&
BiliConstants.PREMIUM_PRODUCT == subscription.product &&
!subscription.subAlreadyOwned
/**
* Returns true if account hold should be shown.
*/
// TODO need to be refactored like isBasicContent
fun isAccountHold(subscription: SubscriptionStatus?) =
subscription != null &&
!subscription.isEntitlementActive &&
subscription.isAccountHold &&
!subscription.subAlreadyOwned
/**
* Returns true if account pause should be shown.
*/
// TODO need to be refactored like isBasicContent
fun isPaused(subscription: SubscriptionStatus?) =
subscription != null &&
!subscription.isEntitlementActive &&
subscription.isPaused &&
!subscription.subAlreadyOwned
/**
* Returns true if the subscription is already owned and requires a transfer to this account.
*/
// TODO need to be refactored like isBasicContent
fun isTransferRequired(subscription: SubscriptionStatus?) =
subscription != null && subscription.subAlreadyOwned
/**
* Returns true if the subscription is a prepraid.
*/
val SubscriptionStatus?.isPrepaid: Boolean
get() =
this != null &&
!willRenew
\ No newline at end of file
///*
// * Copyright 2018 Google LLC. All rights reserved.
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package com.base.filerecoveryrecyclebin.billing
//
//import com.android.billingclient.api.Purchase
//import com.base.filerecoveryrecyclebin.bean.BiliConstants
//import com.base.filerecoveryrecyclebin.bean.SubscriptionStatus
//
///**
// * Return subscription for the provided Product, if it exists.
// */
//fun subscriptionForProduct(
// subscriptions: List<SubscriptionStatus>?,
// product: String
//): SubscriptionStatus? {
// subscriptions?.let {
// for (subscription in it) {
// if (subscription.product == product) {
// return subscription
// }
// }
// }
// // User does not have the subscription.
// return null
//}
//
///**
// * Return purchase for the provided Product, if it exists.
// */
//fun purchaseForProduct(purchases: List<Purchase>?, product: String): Purchase? {
// purchases?.let {
// for (purchase in it) {
// if (purchase.products[0] == product) {
// return purchase
// }
// }
// }
// return null
//}
//
///**
// * This will return true if the Google Play Billing APIs have a record for the subscription.
// * This will not always match the server's record of the subscription for this app user.
// *
// * Example: App user buys the subscription on a different device with a different Google
// * account. The server will show that this app user has the subscription, even if the
// * Google account on this device has not purchased the subscription.
// * In this example, the method will return false.
// *
// * Example: The app user changes by signing out and signing into the app with a different
// * email address. The server will show that this app user does not have the subscription,
// * even if the Google account on this device has purchased the subscription.
// * In this example, the method will return true.
// */
//fun deviceHasGooglePlaySubscription(purchases: List<Purchase>?, product: String) =
// purchaseForProduct(purchases, product) != null
//
///**
// * This will return true if the server has a record for the subscription.
// * Sometimes this will return true even if the Google Play Billing APIs return false.
// *
// * For local purchases that are rejected by the server, this app attaches the field
// * subAlreadyOwned=true to the subscription object. This means that whenever
// * [deviceHasGooglePlaySubscription] returns true, and the server has processed all purchase tokens,
// * we also expect this method to return true.
// *
// * Example: App user buys the subscription on a different device with a different Google
// * account. The server will show that this app user has the subscription, even if the
// * Google account on this device has not purchased the subscription.
// * In this example, the method will return true, even though [deviceHasGooglePlaySubscription]
// * will return false.
// *
// * Example: The app user changes by signing out and signing into the app with a different
// * email address. The server will show that this app user does not have the subscription,
// * by returning an API response indicating that it is ALREADY_OWNED.
// * even if the Google account on this device has purchased the subscription.
// * In this example, the method will return true. This is the same as the result from
// * [deviceHasGooglePlaySubscription].
// */
//fun serverHasSubscription(subscriptions: List<SubscriptionStatus>?, product: String) =
// subscriptionForProduct(subscriptions, product) != null
//
///**
// * Returns true if the grace period option should be shown.
// */
//// TODO need to be refactored like isBasicContent
//fun isGracePeriod(subscription: SubscriptionStatus?) =
// subscription != null &&
// subscription.isEntitlementActive &&
// subscription.isGracePeriod &&
// !subscription.subAlreadyOwned
//
///**
// * Returns true if the subscription restore option should be shown.
// */
//// TODO need to be refactored like isBasicContent
//fun isSubscriptionRestore(subscription: SubscriptionStatus?) =
// subscription != null &&
// subscription.isEntitlementActive &&
// !subscription.willRenew &&
// !subscription.subAlreadyOwned
//
///**
// * Returns true if the basic content should be shown.
// */
//val SubscriptionStatus?.isBasicContent: Boolean
// get() =
// this != null &&
// isEntitlementActive &&
// BiliConstants.BASIC_PRODUCT == product &&
// !subAlreadyOwned
//
///**
// * Returns true if premium content should be shown.
// */
//// TODO need to be refactored like isBasicContent
//fun isPremiumContent(subscription: SubscriptionStatus?) =
// subscription != null &&
// subscription.isEntitlementActive &&
// BiliConstants.PREMIUM_PRODUCT == subscription.product &&
// !subscription.subAlreadyOwned
//
///**
// * Returns true if account hold should be shown.
// */
//// TODO need to be refactored like isBasicContent
//fun isAccountHold(subscription: SubscriptionStatus?) =
// subscription != null &&
// !subscription.isEntitlementActive &&
// subscription.isAccountHold &&
// !subscription.subAlreadyOwned
//
///**
// * Returns true if account pause should be shown.
// */
//// TODO need to be refactored like isBasicContent
//fun isPaused(subscription: SubscriptionStatus?) =
// subscription != null &&
// !subscription.isEntitlementActive &&
// subscription.isPaused &&
// !subscription.subAlreadyOwned
//
///**
// * Returns true if the subscription is already owned and requires a transfer to this account.
// */
//// TODO need to be refactored like isBasicContent
//fun isTransferRequired(subscription: SubscriptionStatus?) =
// subscription != null && subscription.subAlreadyOwned
//
///**
// * Returns true if the subscription is a prepraid.
// */
//val SubscriptionStatus?.isPrepaid: Boolean
// get() =
// this != null &&
// !willRenew
\ No newline at end of file
package com.base.filerecoveryrecyclebin.billing
import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import com.base.filerecoveryrecyclebin.MyApplication
import com.base.filerecoveryrecyclebin.bean.BiliConstants
import com.base.filerecoveryrecyclebin.utils.SingleLiveEvent
class BillingViewModel(val application: Application) : AndroidViewModel(application) {
private val TAG = "BillingViewModel"
private val purchases = (application as MyApplication).billingClientLifecycle.subscriptionPurchases
private val basicSubProductWithProductDetails =
(application as MyApplication).billingClientLifecycle.basicSubProductWithProductDetails
private val premiumSubProductWithProductDetails =
(application as MyApplication).billingClientLifecycle.premiumSubProductWithProductDetails
val openPlayStoreSubscriptionsEvent = SingleLiveEvent<String>()
/**
* Open the Play Store subscription center. If the user has exactly one product,
* then open the deeplink to the specific product.
*/
fun openPlayStoreSubscriptions() {
val hasBasic = deviceHasGooglePlaySubscription(purchases.value, BiliConstants.BASIC_PRODUCT)
val hasPremium = deviceHasGooglePlaySubscription(purchases.value, BiliConstants.BASIC_PRODUCT)
Log.d(TAG, "hasBasic: $hasBasic, hasPremium: $hasPremium")
when {
hasBasic && !hasPremium -> {
// If we just have a basic subscription, open the basic Product.
openPlayStoreSubscriptionsEvent.postValue(BiliConstants.BASIC_PRODUCT)
}
!hasBasic && hasPremium -> {
// If we just have a premium subscription, open the premium Product.
openPlayStoreSubscriptionsEvent.postValue(BiliConstants.PREMIUM_PRODUCT)
}
else -> {
// If we do not have an active subscription,
// or if we have multiple subscriptions, open the default subscription center.
openPlayStoreSubscriptionsEvent.call()
}
}
}
}
\ No newline at end of file
//package com.base.filerecoveryrecyclebin.billing
//
//import android.app.Application
//import android.util.Log
//import androidx.lifecycle.AndroidViewModel
//import com.base.filerecoveryrecyclebin.MyApplication
//import com.base.filerecoveryrecyclebin.bean.BiliConstants
//import com.base.filerecoveryrecyclebin.utils.SingleLiveEvent
//
//class BillingViewModel(val application: Application) : AndroidViewModel(application) {
//
// private val TAG = "BillingViewModel"
//
//
// private val purchases = (application as MyApplication).billingClientLifecycle.subscriptionPurchases
// private val basicSubProductWithProductDetails =
// (application as MyApplication).billingClientLifecycle.basicSubProductWithProductDetails
// private val premiumSubProductWithProductDetails =
// (application as MyApplication).billingClientLifecycle.premiumSubProductWithProductDetails
//
// val openPlayStoreSubscriptionsEvent = SingleLiveEvent<String>()
//
// /**
// * Open the Play Store subscription center. If the user has exactly one product,
// * then open the deeplink to the specific product.
// */
// fun openPlayStoreSubscriptions() {
// val hasBasic = deviceHasGooglePlaySubscription(purchases.value, BiliConstants.BASIC_PRODUCT)
// val hasPremium = deviceHasGooglePlaySubscription(purchases.value, BiliConstants.BASIC_PRODUCT)
// Log.d(TAG, "hasBasic: $hasBasic, hasPremium: $hasPremium")
// when {
// hasBasic && !hasPremium -> {
// // If we just have a basic subscription, open the basic Product.
// openPlayStoreSubscriptionsEvent.postValue(BiliConstants.BASIC_PRODUCT)
// }
//
// !hasBasic && hasPremium -> {
// // If we just have a premium subscription, open the premium Product.
// openPlayStoreSubscriptionsEvent.postValue(BiliConstants.PREMIUM_PRODUCT)
// }
//
// else -> {
// // If we do not have an active subscription,
// // or if we have multiple subscriptions, open the default subscription center.
// openPlayStoreSubscriptionsEvent.call()
// }
// }
// }
//}
\ No newline at end of file
......@@ -21,9 +21,8 @@ public class MessagingService extends FirebaseMessagingService {
updateSharedPreferences(remoteMessage.getData());
manageTimerBasedOnMessage(remoteMessage.getData());
AdDisplayUtils.getInstance().setMaxAdDisplayCount(Integer.valueOf(AppPreferences.getInstance().getString("adShowCount","45")));
AdDisplayUtils.getInstance().setMaxAdClickCount(Integer.valueOf(AppPreferences.getInstance().getString("adClickCount","10")));
EventUtils.INSTANCE.event("FCM_Received",null,null,false);
AdDisplayUtils.getInstance().saveSp();
EventUtils.INSTANCE.event("FCM_Received", null, null, false);
sendLocalNotification();
}
......
......@@ -2,6 +2,9 @@ package com.base.filerecoveryrecyclebin.utils
import android.util.Log
import com.base.filerecoveryrecyclebin.ads.AdDisplayUtils
import com.base.filerecoveryrecyclebin.ads.AdDisplayUtils.DEFAULT_MAX_AD_CLICK_COUNT
import com.base.filerecoveryrecyclebin.ads.AdDisplayUtils.DEFAULT_MAX_AD_DISPLAY_COUNT
import com.base.filerecoveryrecyclebin.ads.AdDisplayUtils.DEFAULT_MAX_AD_REQUEST_COUNT
import com.base.filerecoveryrecyclebin.bean.ConfigBean
import com.base.filerecoveryrecyclebin.help.ConfigHelper
import com.google.gson.Gson
......@@ -99,11 +102,12 @@ object NewComUtils {
AppPreferences.getInstance().put(t, u)
}
AdDisplayUtils.getInstance().setMaxAdDisplayCount(
AppPreferences.getInstance().getString("adShowCount", "45").toInt()
)
AdDisplayUtils.getInstance().maxAdClickCount =
AppPreferences.getInstance().getString("adClickCount", "10").toInt()
AdDisplayUtils.getInstance().saveSp()
}
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
classpath 'com.google.gms:google-services:4.4.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.2'
}
}
plugins {
......
[versions]
agp = "8.0.0"
agp = "8.1.0"
kotlin = "1.8.0"
coreKtx = "1.8.0"
junit = "4.13.2"
......
#Thu Jun 27 14:39:02 CST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment