Android 冷门知识
启动图标替换
主要是利用 ActivityAlias 进行设置,在 manifest 文件提前配置好,需要用到的时候动态设置为 true。
- 动态替换会导致杀死 App,所以需要 App 在后台时进行切换
- 在 manifest 配置的 icon 需要预先放在 apk 中。 如果非要动态加载,可以考虑动态加载资源。参考链接:https://www.jianshu.com/p/e6125ddfaea7,备用链接:https://app.yinxiang.com/shard/s69/nl/17150910/fb6c13b0-0ec9-4918-a5ea-1a8d61c374bc/
<activity-alias
android:name=".SplashAliasActivity"
android:enabled="false"
android:icon="@mipmap/ic_launcher_1111"
android:targetActivity=".pages.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
object ActivityAliasUtils {
private val taskMap = mutableMapOf<String, AliasTask>()
fun register(app: Application) {
app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
var startActivityCount = 0
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityStarted(activity: Activity) {
if (startActivityCount == 0) {
appStart()
}
startActivityCount += 1
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
startActivityCount -= 1
if (startActivityCount == 0) {
appBackground(activity.application)
}
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
}
})
}
private fun appBackground(application: Application) {
processTask(application)
}
private fun appStart() {
}
const val tag = "ActivityAliasUtils"
private fun processTask(application: Application) {
for (value in taskMap.values) {
if (isTimeValid(value)) {
disableComponent(application, getLauncherActivityName(application))
enableComponent(application, value.launcherComponentClassName)
} else {
disableComponent(application, getLauncherActivityName(application))
enableComponent(application, value.aliasComponentClassName)
break
}
}
}
/**
* 超出范围
*/
private fun isTimeValid(value: AliasTask): Boolean {
return System.currentTimeMillis() > value.endTime || System.currentTimeMillis() < value.startTime
}
private fun isTimeValid(value: AliasTask, application: Application): Boolean {
return when {
// 超过时间
isPassedOutDateTime(value) -> {
Log.d(tag, "OutDateTime ")
disableComponent(application, getLauncherActivityName(application))
enableComponent(application, value.launcherComponentClassName)
false
}
// 在活动时间范围内
isPassedPresetTime(value) -> {
Log.d(tag, "PresetTime ")
disableComponent(application, getLauncherActivityName(application))
enableComponent(application, value.aliasComponentClassName)
true
}
else -> false
}
}
/**
* 是否已超过预设时间
* @param task 任务
*/
private fun isPassedPresetTime(task: AliasTask) =
System.currentTimeMillis() > task.startTime
/**
* 是否已超过过期时间
* @param task 任务
*
*/
private fun isPassedOutDateTime(task: AliasTask) =
System.currentTimeMillis() > task.endTime
/**
* 添加图标切换任务
* @param newTasks 新任务,可以传多个
*/
fun addNewTask(vararg newTasks: AliasTask) {
for (newTask in newTasks) {
// 防止重复添加任务
if (taskMap.containsKey(newTask.aliasComponentClassName)) return
// 校验任务的预设时间和过期时间
for (queuedTask in taskMap.values) {
if (newTask.startTime > newTask.endTime) throw IllegalArgumentException("非法的任务预设时间${newTask.startTime}, 不能晚于过期时间")
if (newTask.startTime <= queuedTask.endTime) throw IllegalArgumentException("非法的任务预设时间${newTask.startTime}, 不能早于已添加任务的过期时间")
}
taskMap[newTask.aliasComponentClassName] = newTask
}
}
/**
* 启用组件
* @param context 上下文
* @param className 组件类名
*/
private fun enableComponent(context: Context, className: String) {
Log.d(tag, "enableComponent $className")
val componentName = ComponentName(context, className)
if (isComponentEnabled(context, componentName)) return //已经启用
context.packageManager.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP
)
}
/**
* 禁用组件
* @param context 上下文
* @param className 组件类名
*/
private fun disableComponent(context: Context, className: String) {
Log.d(tag, "disableComponent $className")
if (className.isNullOrEmpty()) {
return
}
val componentName = ComponentName(context, className)
if (isComponentDisabled(context, componentName)) return // 已经禁用
context.packageManager.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
}
/**
* 组件是否处于可用状态
* @param context 上下文
* @param componentName 组件名
*/
private fun isComponentEnabled(context: Context, componentName: ComponentName): Boolean {
val state: Int = context.packageManager.getComponentEnabledSetting(componentName)
return PackageManager.COMPONENT_ENABLED_STATE_ENABLED == state
}
/**
* 组件是否处于禁用状态
* @param context 上下文
* @param componentName 组件名
*/
private fun isComponentDisabled(context: Context, componentName: ComponentName): Boolean {
val state: Int = context.packageManager.getComponentEnabledSetting(componentName)
return PackageManager.COMPONENT_ENABLED_STATE_DISABLED == state
}
private fun getLauncherActivityName(context: Context): String {
val intent = Intent(Intent.ACTION_MAIN, null).apply {
addCategory(Intent.CATEGORY_LAUNCHER)
setPackage(context.packageName)
}
val resolveInfoList = context.packageManager.queryIntentActivities(intent, 0)
return if (resolveInfoList.isNotEmpty() && resolveInfoList.isNotEmpty()) resolveInfoList[0].activityInfo.name else ""
}
}