diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 12d9347..d459699 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,8 @@ plugins { id("kotlin-kapt") id("org.jetbrains.kotlin.plugin.serialization") id("kotlin-android") + id("com.google.gms.google-services") + id("com.google.firebase.crashlytics") } android { @@ -30,13 +32,13 @@ android { "proguard-rules.pro" ) } - debug { - isMinifyEnabled = true - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } +// debug { +// isMinifyEnabled = true +// proguardFiles( +// getDefaultProguardFile("proguard-android-optimize.txt"), +// "proguard-rules.pro" +// ) +// } } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -97,4 +99,10 @@ dependencies { //google // implementation("com.google.android.gms:play-services-ads-identifier:18.0.1") + + // Import the Firebase BoM + implementation(platform("com.google.firebase:firebase-bom:32.3.1")) + implementation("com.google.firebase:firebase-analytics-ktx") + implementation("com.google.firebase:firebase-crashlytics-ktx") + implementation("com.google.firebase:firebase-config") } \ No newline at end of file diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..51665d3 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "320083292372", + "project_id": "musiclax-and", + "storage_bucket": "musiclax-and.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:320083292372:android:047dd51c4c373acf9a8b41", + "android_client_info": { + "package_name": "relax.offline.mp3.music" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyCs8V_b7UYuUfcs_mAWIAbr06VZKBM-680" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4dc58cb..f7e56fd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,6 +41,7 @@ ${bean}") + LogTag.LogD(TAG, "insertOfflineBean bean->${bean}") App.appOfflineDBManager.insertOfflineBean(bean) } @@ -247,14 +245,30 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope thumbnail = mediaItem.mediaMetadata.artworkUri.toString(), isFavorite = true ) - LogTag.LogD(Innertube.TAG, "insertFavoriteBean bean->${bean}") + LogTag.LogD(TAG, "insertFavoriteBean bean->${bean}") App.appFavoriteDBManager.insertFavoriteBean(bean) } fun withPermission(): Boolean { + //先判断当前配置的开关是否为true,为false的话就直接进入A + LogTag.LogD(TAG, "withPermission shouldEnterMusicPage->${appStore.shouldEnterMusicPage}") + if (!appStore.shouldEnterMusicPage) { + return false + } // 不允许的国家代码 - val restrictedCountries = setOf("CN", "HK", "TW", "JP", "KR", "GB", "CH", "BE", "MO", "SG") + val restrictedCountries = setOf( +// "CN", +// "HK", + "TW", + "JP", + "KR", + "GB", + "CH", + "BE", + "MO", + "SG") // 检查是否包含当前的国家代码 + LogTag.LogD(TAG, "withPermission ipCountryCode->${appStore.ipCountryCode}") if (appStore.ipCountryCode in restrictedCountries) { return false } @@ -275,9 +289,9 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope //525 Singapore (Republic of) val restrictedCountryCodes = setOf( - "460", - "461", - "454", +// "460", +// "461", +// "454", "466", "440", "441", @@ -290,6 +304,7 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope "525" ) val currentCountryCode = getCountryCode(this) + LogTag.LogD(TAG, "withPermission currentCountryCode->${currentCountryCode}") return currentCountryCode !in restrictedCountryCodes } } \ No newline at end of file diff --git a/app/src/main/java/relax/offline/music/database/AppOfflineDBManager.kt b/app/src/main/java/relax/offline/music/database/AppOfflineDBManager.kt index 89faaa0..00d571a 100644 --- a/app/src/main/java/relax/offline/music/database/AppOfflineDBManager.kt +++ b/app/src/main/java/relax/offline/music/database/AppOfflineDBManager.kt @@ -31,10 +31,8 @@ class AppOfflineDBManager private constructor(context: Context) { withContext(Dispatchers.IO) { val offlineBean = getOfflineBeanByID(bean.videoId) if (offlineBean == null) { - LogTag.LogD(LogTag.VO_TEST_ONLY,"insertOfflineBean") dao.insertOfflineBean(bean) } else { - LogTag.LogD(LogTag.VO_TEST_ONLY,"updateOfflineBean") dao.updateOfflineBean(bean) } } diff --git a/app/src/main/java/relax/offline/music/firebase/Constants.kt b/app/src/main/java/relax/offline/music/firebase/Constants.kt new file mode 100644 index 0000000..8fb8267 --- /dev/null +++ b/app/src/main/java/relax/offline/music/firebase/Constants.kt @@ -0,0 +1,7 @@ +package relax.offline.music.firebase + +object Constants { + const val KEY_SHOULD_ENTER_MUSIC_PAGE = "key_should_enter_music_page" + const val DEFAULT_SHOULD_ENTER_MUSIC_PAGE = false +} + diff --git a/app/src/main/java/relax/offline/music/firebase/RemoteConfig.kt b/app/src/main/java/relax/offline/music/firebase/RemoteConfig.kt new file mode 100644 index 0000000..7946181 --- /dev/null +++ b/app/src/main/java/relax/offline/music/firebase/RemoteConfig.kt @@ -0,0 +1,146 @@ +package relax.offline.music.firebase + +import android.annotation.SuppressLint +import android.app.Application +import android.content.Context +import android.os.Handler +import android.os.Message +import android.text.TextUtils +import com.google.firebase.remoteconfig.ConfigUpdate +import com.google.firebase.remoteconfig.ConfigUpdateListener +import com.google.firebase.remoteconfig.FirebaseRemoteConfig +import com.google.firebase.remoteconfig.FirebaseRemoteConfigException +import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings +import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue +import relax.offline.music.App +import relax.offline.music.BuildConfig +import relax.offline.music.sp.AppStore +import relax.offline.music.util.LogTag +import java.lang.ref.WeakReference + +class RemoteConfig { + + private val TAG = LogTag.VO_TEST_ONLY + private var ctx: Context? = null + private var mFirebaseRemoteConfig: FirebaseRemoteConfig? = null + + //配置是否初始化成功 + private var isInit = false + + //上次获取数据的时间 + private var lastFetchTime: Long = 0 + private val handler = MHandler(this) + + companion object { + const val MSG_REFRESH_CONFIG = 1 + val instance: RemoteConfig by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { + RemoteConfig() + } + } + + fun init(ctx: Application) { + this.ctx = ctx + initConfig() + fetchConfig() + onConfigUpdate() + } + + private fun initConfig() { + var intervalTime = (60 * 10).toLong() + //如果是开发状态,则将提取时间缩短 + if (BuildConfig.DEBUG) { + intervalTime = (60 * 5).toLong() + } + mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance() + val configSettings = + FirebaseRemoteConfigSettings.Builder() //默认值12小时的最短提取间隔,如果在间隔内取值,则优先取上次的结果 + .setMinimumFetchIntervalInSeconds(intervalTime) + .build() + mFirebaseRemoteConfig!!.setConfigSettingsAsync(configSettings) + } + + private fun onConfigUpdate() { + mFirebaseRemoteConfig!!.addOnConfigUpdateListener(object : ConfigUpdateListener { + override fun onUpdate(configUpdate: ConfigUpdate) { + LogTag.LogD(TAG, "Updated keys: " + configUpdate.updatedKeys) + + if (configUpdate.updatedKeys.contains(Constants.KEY_SHOULD_ENTER_MUSIC_PAGE)) { + mFirebaseRemoteConfig!!.activate().addOnCompleteListener { task -> + if (task.isSuccessful) { + updateData("onConfigUpdate", mFirebaseRemoteConfig!!.all) + } + } + } + } + + override fun onError(error: FirebaseRemoteConfigException) { + LogTag.LogD(TAG, "Config update error with code: " + error.code) + } + }) + } + + @SuppressLint("LongLogTag") + private fun fetchConfig() { + //这里可能会抛出异常 FirebaseRemoteConfigFetchThrottledException + try { + mFirebaseRemoteConfig!!.fetchAndActivate().addOnCompleteListener { task -> + if (task.isSuccessful) { + isInit = true + lastFetchTime = System.currentTimeMillis() + updateData("fetchAndActivate", mFirebaseRemoteConfig!!.all) + //24小时后,重新再去获取 + handler.removeMessages(MSG_REFRESH_CONFIG) + handler.sendEmptyMessageDelayed( + MSG_REFRESH_CONFIG, + (1000 * 60 * 60 * 24).toLong() + ) + } else { + //这里需要重新再去获取 + handler.removeMessages(MSG_REFRESH_CONFIG) + handler.sendEmptyMessageDelayed(MSG_REFRESH_CONFIG, (1000 * 60 * 15).toLong()) + } + } + } catch (ignore: Exception) { + } + } + + private fun updateData(from: String, all: Map) { + val appStore = AppStore(App.app) + for ((key, value) in all) { + try { + LogTag.LogD( + TAG, + "from = " + from + "Key = " + key + " Value = " + value.asString() + ) + if (TextUtils.equals( + Constants.KEY_SHOULD_ENTER_MUSIC_PAGE, key + ) + ) { + val shouldEnterMusicPage = value.asBoolean() + appStore.shouldEnterMusicPage = shouldEnterMusicPage + } + } catch (ignore: Exception) { + + } + } + } + + private class MHandler(remoteConfig: RemoteConfig) : Handler() { + private val weakReference: WeakReference + + init { + weakReference = WeakReference(remoteConfig) + } + + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + val remoteConfig = weakReference.get() + if (remoteConfig?.ctx != null) { + if (msg.what == MSG_REFRESH_CONFIG) { + remoteConfig.fetchConfig() + } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/relax/offline/music/sp/AppStore.kt b/app/src/main/java/relax/offline/music/sp/AppStore.kt index c51e26c..b3fc26a 100644 --- a/app/src/main/java/relax/offline/music/sp/AppStore.kt +++ b/app/src/main/java/relax/offline/music/sp/AppStore.kt @@ -1,6 +1,7 @@ package relax.offline.music.sp import android.content.Context +import relax.offline.music.firebase.Constants import relax.offline.music.sp.store.Store import relax.offline.music.sp.store.asStoreProvider import relax.offline.music.util.PlayMode @@ -32,7 +33,7 @@ class AppStore(context: Context) { defaultValue = "" ) - var appUUID : String by store.string( + var appUUID: String by store.string( key = APP_UUID, defaultValue = "" ) @@ -42,10 +43,16 @@ class AppStore(context: Context) { defaultValue = "" ) - var firstOpenIsSucceed : Boolean by store.boolean( + var firstOpenIsSucceed: Boolean by store.boolean( key = FIRST_OPEN_IS_SUCCEED, defaultValue = false ) + + var shouldEnterMusicPage: Boolean by store.boolean( + key = Constants.KEY_SHOULD_ENTER_MUSIC_PAGE, + defaultValue = Constants.DEFAULT_SHOULD_ENTER_MUSIC_PAGE + ) + companion object { private const val FILE_NAME = "music_oo_app" const val SEARCH_HISTORY = "search_history" diff --git a/app/src/main/java/relax/offline/music/util/AnalysisUtil.kt b/app/src/main/java/relax/offline/music/util/AnalysisUtil.kt new file mode 100644 index 0000000..415a8e4 --- /dev/null +++ b/app/src/main/java/relax/offline/music/util/AnalysisUtil.kt @@ -0,0 +1,9 @@ +package relax.offline.music.util + +import com.google.firebase.analytics.FirebaseAnalytics + +object AnalysisUtil { + private lateinit var firebaseAnalytics: FirebaseAnalytics + + +} \ No newline at end of file diff --git a/app/src/main/java/relax/offline/music/util/AppLifecycleHandler.kt b/app/src/main/java/relax/offline/music/util/AppLifecycleHandler.kt new file mode 100644 index 0000000..883de0a --- /dev/null +++ b/app/src/main/java/relax/offline/music/util/AppLifecycleHandler.kt @@ -0,0 +1,57 @@ +package relax.offline.music.util + +import android.app.Activity +import android.app.Application +import android.content.Intent +import android.os.Bundle +import android.os.SystemClock +import relax.offline.music.App +import relax.offline.music.activity.LaunchActivity + +/** + * 一个处理应用程序生命周期事件并管理启动页显示的类。 + * + * @param application 应用程序实例。 + */ +class AppLifecycleHandler(private val application: Application) : Application.ActivityLifecycleCallbacks { + + private var activityReferences = 0 + private var isActivityChangingConfigurations = false + private var lastPausedTime: Long = 0 + private val intervalTime = 5000L + + init { + application.registerActivityLifecycleCallbacks(this) + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} + + override fun onActivityStarted(activity: Activity) { + if (++activityReferences == 1 && !isActivityChangingConfigurations) { + // 应用进入前台 + val currentTime = SystemClock.elapsedRealtime() + val isAdShowing = App.app.isAdShowing.get() + //间隔时间是否满足,当前不是启动页,当前不是广告show + if (currentTime - lastPausedTime > intervalTime && activity !is LaunchActivity && !isAdShowing) { + val intent = Intent(activity, LaunchActivity::class.java) + activity.startActivity(intent) + } + } + } + + override fun onActivityResumed(activity: Activity) {} + + override fun onActivityPaused(activity: Activity) {} + + override fun onActivityStopped(activity: Activity) { + isActivityChangingConfigurations = activity.isChangingConfigurations + if (--activityReferences == 0 && !isActivityChangingConfigurations) { + // 应用进入后台 + lastPausedTime = SystemClock.elapsedRealtime() + } + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + + override fun onActivityDestroyed(activity: Activity) {} +} diff --git a/build.gradle.kts b/build.gradle.kts index 3b28948..7edc5a0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,4 +3,6 @@ plugins { id("com.android.application") version "8.2.1" apply false id("org.jetbrains.kotlin.android") version "1.9.22" apply false id("org.jetbrains.kotlin.plugin.serialization") version "1.7.20" apply false + id("com.google.gms.google-services") version "4.3.15" apply false + id("com.google.firebase.crashlytics") version "2.9.5" apply false } \ No newline at end of file