This commit is contained in:
ocean 2024-05-29 14:36:27 +08:00
parent 40fb451fa4
commit 6565860cbb
13 changed files with 308 additions and 23 deletions

View File

@ -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")
}

29
app/google-services.json Normal file
View File

@ -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"
}

View File

@ -41,6 +41,7 @@
</activity>
<activity
android:name=".activity.MainActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<activity
android:name=".activity.PrimaryActivity"

View File

@ -20,10 +20,13 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import relax.offline.music.database.AppFavoriteDBManager
import relax.offline.music.firebase.RemoteConfig
import relax.offline.music.http.CommonIpInfoUtil
import relax.offline.music.http.UploadEventName
import relax.offline.music.util.AppLifecycleHandler
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.concurrent.atomic.AtomicBoolean
@OptIn(UnstableApi::class)
class App : Application() {
@ -107,9 +110,15 @@ class App : Application() {
}
}
var isAdShowing: AtomicBoolean = AtomicBoolean(false)//是否正在广告界面
override fun onCreate() {
super.onCreate()
app = this
AppLifecycleHandler(this)
CommonIpInfoUtil.shared.initIPInfo()
UploadEventName.shared.init(this)
RemoteConfig.instance.init(this)
initialize(this)
MediaControllerManager.init(this)
LocalMediaControllerManager.init(this)
@ -121,8 +130,5 @@ class App : Application() {
initImportAudio()
CacheManager.initializeCaches(this)
DownloadUtil.getDownloadManager(this)
CommonIpInfoUtil.shared.initIPInfo()
UploadEventName.shared.init(this)
}
}

View File

@ -8,7 +8,7 @@ import relax.offline.music.databinding.ActivityLaunchBinding
class LaunchActivity : MoBaseActivity() {
private lateinit var binding: ActivityLaunchBinding
private val totalTime = 3000L // 5秒
private val totalTime = 5000L // 5秒
private val interval = 50L // 更新间隔,毫秒
private val steps = totalTime / interval
private val progressPerStep = 100f / steps.toFloat()

View File

@ -1,7 +1,6 @@
package relax.offline.music.activity
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
@ -32,7 +31,6 @@ import relax.offline.music.App
import relax.offline.music.R
import relax.offline.music.bean.FavoriteBean
import relax.offline.music.bean.OfflineBean
import relax.offline.music.innertube.Innertube
import relax.offline.music.media.MediaControllerManager
import relax.offline.music.sp.AppStore
import relax.offline.music.util.LogTag
@ -235,7 +233,7 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
thumbnail = mediaItem.mediaMetadata.artworkUri.toString(),
isOffline = true
)
LogTag.LogD(Innertube.TAG, "insertOfflineBean bean->${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
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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<String, FirebaseRemoteConfigValue>) {
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<RemoteConfig>
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()
}
}
}
}
}

View File

@ -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
@ -46,6 +47,12 @@ class AppStore(context: Context) {
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"

View File

@ -0,0 +1,9 @@
package relax.offline.music.util
import com.google.firebase.analytics.FirebaseAnalytics
object AnalysisUtil {
private lateinit var firebaseAnalytics: FirebaseAnalytics
}

View File

@ -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) {}
}

View File

@ -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
}