文章目录
说明
以下功能的实现是在有系统签名和设置了 android:sharedUserId=“android.uid.system” 的情况下实现的。如果你的应用没有系统签名,可能无法实现以下功能。
1. 修改系统时间
设置系统时间,首先需要保证app的uid为system,关于如何设置uid,可以看看这篇 App设置系统签名。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxx.xxx"
android:sharedUserId="android.uid.system">
并给予相应的权限:
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
<uses-permission android:name="android.permission.SET_TIME_ZONE" />
设置系统时间,主要是通过 Calendar 进行日期的设定,通过 Calendar.getTimeInMillis() 获取毫秒数,再通过 SystemClock.setCurrentTimeMillis() 来进行设置。
1. 设置系统时间(仅设置xx:xx)
/** 设置系统时间*/
@Throws(IOException::class)
fun setTime(hour: Int, minute: Int) {
val calendar = Calendar.getInstance()
calendar.set(Calendar.HOUR_OF_DAY, hour)
calendar.set(Calendar.MINUTE, minute)
val timeMills: Long = calendar.timeInMillis
SystemClock.setCurrentTimeMillis(timeMills)
}
2. 设置系统日期(仅设置到xxxx年xx月xx日)
/** 设置系统日期*/
@Throws(IOException::class)
fun setDate(year: Int, month: Int, day: Int) {
val calendar = Calendar.getInstance()
calendar.set(Calendar.YEAR, year)
calendar.set(Calendar.MONTH, month)
calendar.set(Calendar.DAY_OF_MONTH, day)
val timeMills: Long = calendar.timeInMillis
SystemClock.setCurrentTimeMillis(timeMills)
}
3.设置系统是否自动获取时间
/**
* 设置系统是否自动获取时间
* @param context Activity's context.
* @param checked If checked > 0, it will auto set date.
*/
fun setAutoDateTime(context: Context, checked: Int) {
Settings.Global.putInt(
context.contentResolver,
Settings.Global.AUTO_TIME, checked
)
}
4.判断系统是否自动获取时间
/**
* 判断系统是否自动获取时间
* @param context Activity's context.
* @return If date is auto setting.
*/
fun checkDateAutoSet(context: Context): Boolean {
return try {
Settings.Global.getInt(
context.contentResolver,
Settings.Global.AUTO_TIME
) > 0
} catch (exception: SettingNotFoundException) {
exception.printStackTrace()
false
}
}
2. 修改系统时区
系统是通过 AlarmManager 来修改 TimeZone 的。也需要权限:
<uses-permission android:name="android.permission.SET_TIME_ZONE" />
1. 设置系统时区
/** 设置系统时区*/
fun setTimeZone(context: Context, timeZone: String?) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setTimeZone(timeZone)
}
2. 设置系统是否自动获取时区
/**
* 设置系统是否自动获取时区
* @param context Activity's context.
* @param checked If checked > 0, it will auto set timezone.
*/
fun setAutoTimeZone(context: Context, checked: Int) {
Settings.Global.putInt(
context.contentResolver,
Settings.Global.AUTO_TIME_ZONE, checked
)
}
3. 判断系统是否自动获取时区
/**
* 判断系统是否自动获取时区
* @param context Activity's context.
* @return If timezone is auto setting.
*/
fun checkTimeZoneAutoSet(context: Context): Boolean {
return try {
Settings.Global.getInt(
context.contentResolver,
Settings.Global.AUTO_TIME_ZONE
) > 0
} catch (exception: SettingNotFoundException) {
exception.printStackTrace()
false
}
}
3. 修改系统语言
我们要实现在 APP 内修改系统语言,并且系统语言更改后,APP 的语言会随之改变,且无需重启 APP。OK,要实现这个功能,我用的是 Java 反射。通过查资料,发现 6.0 以前和 6.0 以后的反射代码会略有不同,下面的代码对切换系统语言做了一个简单的封装,可以实现在 APP 内修改系统语言,即使系统重启也会生效。
package com.xzy.syssettings.utils
import android.app.Activity
import android.app.backup.BackupManager
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.LocaleList
import android.widget.Toast
import com.xzy.syssettings.MainActivity2
import com.xzy.syssettings.language.LanguageUtil
import com.xzy.syssettings.language.Sp
import java.lang.Exception
import java.lang.reflect.Method
import java.util.Locale
/**
*
* @author :created by xzy.
* @date :2021/10/28
*/
object LanguageUtil {
/**
* 这个方法不需要系统签名
* 经过测试:android 8.0 以下的版本需要更新 configuration 和 resources,
* android 8.0 以上只需要将当前的语言环境写入 Sp 文件即可。
* 测试机型 android4.4、android6.0、android7.0、android7.1、android8.1
* 然后,重新创建当前页面。
* @param language
*/
fun changeAppLanguage(language: String?, activity: Activity) {
// 版本低于 android 8.0 不执行该方法
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// 注意,这里的 context 不能传 Application 的 context
LanguageUtil.changeAppLanguage(activity, language!!)
}
Sp.put("language", language!!)
// 不同的版本,使用不同的重启方式,达到最好的效果
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
// 6.0 以及以下版本,使用这种方式,并给 activity 添加启动动画效果,可以规避黑屏和闪烁问题
val intent = Intent(activity, MainActivity2::class.java)
// intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
activity.startActivity(intent)
activity.finish()
} else {
// 6.0 以上系统直接调用重新创建函数,可以达到无缝切换的效果
activity.recreate()
}
}
/**
* 这个方法需要系统签名
* */
fun changeSystemLanguage(locale: Locale?, context: Context) {
if (locale != null) {
try {
val classActivityManagerNative = Class.forName("android.app.ActivityManagerNative")
val getDefault: Method = classActivityManagerNative.getDeclaredMethod("getDefault")
val objIActivityManager: Any = getDefault.invoke(classActivityManagerNative)
val classIActivityManager = Class.forName("android.app.IActivityManager")
val getConfiguration: Method =
classIActivityManager.getDeclaredMethod("getConfiguration")
val config: Configuration =
getConfiguration.invoke(objIActivityManager) as Configuration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.locales = LocaleList(locale)
} else {
config.setLocale(locale)
}
val clzConfig = Class
.forName("android.content.res.Configuration")
val userSetLocale = clzConfig
.getField("userSetLocale")
userSetLocale[config] = true
val clzParams = arrayOf<Class<*>>(
Configuration::class.java
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 该代码决定是否修改系统语言设置(需要有系统签名和sharedUserId)
val updatePersistentConfiguration: Method =
classIActivityManager.getDeclaredMethod(
"updatePersistentConfiguration",
*clzParams
)
updatePersistentConfiguration.invoke(objIActivityManager, config)
BackupManager.dataChanged("com.android.providers.settings")
} else {
// updateConfiguration
val configuration = context.resources.configuration
// 获取想要切换的语言类型
configuration.setLocale(locale)
// updateConfiguration
val dm = context.resources.displayMetrics
context.resources.updateConfiguration(configuration, dm)
// 下面的代码决定是否修改系统语言设置(需要有系统签名和sharedUserId)
val updateConfiguration: Method =
classIActivityManager.getDeclaredMethod(
"updateConfiguration",
*clzParams
)
updateConfiguration.invoke(objIActivityManager, config)
BackupManager.dataChanged("com.android.providers.settings")
}
Toast.makeText(
context,
"language:" + locale.language + "--country:" + locale.country,
Toast.LENGTH_SHORT
).show()
} catch (exception: Exception) {
exception.printStackTrace()
}
}
}
}
如果只需要修改 APP 语言,则只需要调用 changeAppLanguage(LanguageType.ENGLISH.language, this) 即可。
如果同时需要修改系统语言,调用方式为:
changeSystemLanguage(Locale.ENGLISH, this)
changeAppLanguage(LanguageType.ENGLISH.language, this)
4. 系统重启
由于 App 有系统签名,所以实现系统重启功能比较简单。其中的一种方式是使用 PowerManager 实现。代码如下:
// 重新启动到 fastboot模式
val pManager = getSystemService(Context.POWER_SERVICE) as PowerManager
pManager.reboot("")
5. 源码
以上功能的实现源码点击这里:源码