在Android 9及以下版别,后台发动Activity相对自在,可是假如在Activity上下文之外发动Activity会有约束。
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag
所以此时需要给intent添加flag:FLAG_ACTIVITY_NEW_TASK。
在Android版别10及今后版别, 引入了后台履行约束,约束了使用在后台履行操作的才能。非中心使命的后台发动 Activity 可能会受到约束。详情可参见官方文档:从后台发动 Activity 的约束。
依据文档可知,大致有两种计划可实现从后台发动Activity。
计划一:设置全屏Notification
设置Notification时经过setFullScreenIntent添加一个全屏Intent目标,能够在Android 10上从后台发动一个Activity界面,需要在Manifest.xml清单文件中加上:
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
示例代码如下:
private fun getChannelNotificationQ(context: Context, title: String?, content: String?): Notification {
val fullScreenPendingIntent = PendingIntent.getActivity(
context,
0,
DemoActivity.genIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
val notificationBuilder = NotificationCompat.Builder(context, ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(title)
.setContentText(content)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(Notification.CATEGORY_CALL)
.setOngoing(true)
.setFullScreenIntent(fullScreenPendingIntent, true)
return notificationBuilder.build()
}
计划二:获取SYSTEM_ALERT_WINDOW权限
假如用户已向使用授予SYSTEM_ALERT_WINDOW权限,则能够在后台发动Activity。在 Android 10 Go 版别中,使用现已无法直接获得SYSTEM_ALERT_WINDOW权限。不过Android引入了一种称为”Display over other apps”(在其他使用上层显现)的新权限系统。这种新的权限系统答应使用请求”TYPE_APPLICATION_OVERLAY”类型的窗口权限。请求过程如下:
在Manifest.xml清单文件中加上:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
代码中发起请求权限请求:
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 0);
}
定制化ROM新权限
有些机型增加了一项权限——后台弹出界面,比如在华为、 小米等设备上便新增了这项权限,且默认是封闭的,除非加入了它们的白名单。而且假如权限是封闭的,那么前面所说的两种计划将无效。所以在这些机型上,有必要获取后台弹出界面权限,才能够从后台发动Activity。
判别是否获取弹出界面权限:
object PopBackgroundPermissionUtil {
private const val TAG = "PopPermissionUtil"
private const val HW_OP_CODE_POPUP_BACKGROUND_WINDOW = 100000
private const val XM_OP_CODE_POPUP_BACKGROUND_WINDOW = 10021
/**
* 是否有后台弹出页面权限
*/
fun hasPopupBackgroundPermission(): Boolean {
if (isHuawei()) {
return checkHwPermission()
}
if (isXiaoMi()) {
return checkXmPermission()
}
if (isVivo()) {
checkVivoPermission()
}
if (isOppo() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Settings.canDrawOverlays(sContext)
}
return true
}
fun isHuawei(): Boolean {
return checkManufacturer("huawei")
}
fun isXiaoMi(): Boolean {
return checkManufacturer("xiaomi")
}
fun isOppo(): Boolean {
return checkManufacturer("oppo")
}
fun isVivo(): Boolean {
return checkManufacturer("vivo")
}
private fun checkManufacturer(manufacturer: String): Boolean {
return manufacturer.equals(Build.MANUFACTURER, true)
}
private fun checkHwPermission(): Boolean {
val context = sContext
try {
val c = Class.forName("com.huawei.android.app.AppOpsManagerEx")
val m = c.getDeclaredMethod(
"checkHwOpNoThrow",
AppOpsManager::class.java,
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType,
String::class.java
)
val result = m.invoke(
c.newInstance(),
*arrayOf(
context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager,
HW_OP_CODE_POPUP_BACKGROUND_WINDOW,
Binder.getCallingUid(),
context.packageName
)
) as Int
Log.d(
TAG,
"PopBackgroundPermissionUtil checkHwPermission result:" + (AppOpsManager.MODE_ALLOWED == result)
)
return AppOpsManager.MODE_ALLOWED == result
} catch (e: Exception) {
//ignore
}
return false
}
private fun checkXmPermission(): Boolean {
val context = sContext
val ops = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
try {
val method = ops.javaClass.getMethod(
"checkOpNoThrow", *arrayOf<Class<*>?>(
Int::class.javaPrimitiveType, Int::class.javaPrimitiveType, String::class.java
)
)
val result = method.invoke(
ops,
XM_OP_CODE_POPUP_BACKGROUND_WINDOW,
Process.myUid(),
context.packageName
) as Int
Log.d(
TAG,
"PopBackgroundPermissionUtil checkXmPermission result:" + (AppOpsManager.MODE_ALLOWED == result)
)
return result == AppOpsManager.MODE_ALLOWED
} catch (e: Exception) {
//ignore
}
return false
}
private fun checkVivoPermission(): Boolean {
val context = sContext
val uri =
Uri.parse("content://com.vivo.permissionmanager.provider.permission/start_bg_activity")
val selection = "pkgname = ?"
val selectionArgs = arrayOf(context.packageName)
var result = 1
val contentResolver = context.contentResolver
try {
contentResolver.query(uri, null, selection, selectionArgs, null).use { cursor ->
if (cursor!!.moveToFirst()) {
result = cursor.getInt(cursor.getColumnIndex("currentstate"))
}
}
} catch (exception: Exception) {
//ignore
}
Log.d(
TAG,
"PopBackgroundPermissionUtil checkVivoPermission result:" + (AppOpsManager.MODE_ALLOWED == result)
)
return result == AppOpsManager.MODE_ALLOWED
}
}
跳转弹出界面权限界面:
class SystemAlertWindow(private val mSource: Activity) {
fun start(requestCode: Int) {
var intent: Intent?
intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (MARK.contains("meizu")) {
meiZuApi(mSource)
} else {
MdefaultApi(mSource)
}
} else {
if (MARK.contains("huawei")) {
huaweiApi(mSource)
} else if (MARK.contains("xiaomi")) {
xiaomiApi(mSource)
} else if (MARK.contains("oppo")) {
oppoApi(mSource)
} else if (MARK.contains("vivo")) {
vivoApi(mSource)
} else if (MARK.contains("meizu")) {
meizuApi(mSource)
} else {
LdefaultApi(mSource)
}
}
try {
mSource.startActivityForResult(intent, requestCode)
} catch (e: Exception) {
intent = appDetailsApi(mSource)
mSource.startActivityForResult(intent, requestCode)
}
}
private fun huaweiApi(context: Context): Intent? {
val intent = Intent()
intent.setClassName(
"com.huawei.systemmanager",
"com.huawei.permissionmanager.ui.MainActivity"
)
if (hasActivity(context, intent)) {
return intent
}
intent.setClassName(
"com.huawei.systemmanager",
"com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity"
)
if (hasActivity(context, intent)) {
return intent
}
intent.setClassName(
"com.huawei.systemmanager",
"com.huawei.notificationmanager.ui.NotificationManagmentActivity"
)
return if (hasActivity(context, intent)) {
intent
} else MdefaultApi(context)
}
private fun xiaomiApi(context: Context): Intent? {
val intent = Intent("miui.intent.action.APP_PERM_EDITOR")
intent.putExtra("extra_pkgname", context.packageName)
if (hasActivity(context, intent)) {
return intent
}
intent.setClassName(
"com.miui.securitycenter",
"com.miui.permcenter.permissions.AppPermissionsEditorActivity"
)
return if (hasActivity(context, intent)) {
intent
} else MdefaultApi(context)
}
private fun oppoApi(context: Context): Intent? {
val intent = Intent()
intent.putExtra("packageName", context.packageName)
intent.setClassName(
"com.color.safecenter",
"com.color.safecenter.permission.floatwindow.FloatWindowListActivity"
)
if (hasActivity(context, intent)) {
return intent
}
intent.setClassName(
"com.coloros.safecenter",
"com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity"
)
if (hasActivity(context, intent)) {
return intent
}
intent.setClassName("com.oppo.safe", "com.oppo.safe.permission.PermissionAppListActivity")
return if (hasActivity(context, intent)) {
intent
} else MdefaultApi(context)
}
private fun vivoApi(context: Context): Intent? {
val intent = Intent()
intent.setClassName(
"com.iqoo.secure",
"com.iqoo.secure.ui.phoneoptimize.FloatWindowManager"
)
intent.putExtra("packagename", context.packageName)
if (hasActivity(context, intent)) {
return intent
}
intent.setClassName(
"com.iqoo.secure",
"com.iqoo.secure.safeguard.SoftPermissionDetailActivity"
)
return if (hasActivity(context, intent)) {
intent
} else MdefaultApi(context)
}
private fun meizuApi(context: Context): Intent? {
val intent = Intent("com.meizu.safe.security.SHOW_APPSEC")
intent.putExtra("packageName", context.packageName)
intent.component = ComponentName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity")
return if (hasActivity(context, intent)) {
intent
} else MdefaultApi(context)
}
companion object {
private val MARK = Build.MANUFACTURER.lowercase(Locale.getDefault())
const val REQUEST_OVERLY = 7562
private fun LdefaultApi(context: Context): Intent {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.fromParts("package", context.packageName, null)
return intent
}
private fun appDetailsApi(context: Context): Intent {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.fromParts("package", context.packageName, null)
return intent
}
private fun MdefaultApi(context: Context): Intent? {
var intent: Intent? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
}
intent!!.data = Uri.fromParts("package", context.packageName, null)
return if (hasActivity(context, intent)) {
intent
} else appDetailsApi(context)
}
private fun meiZuApi(context: Context): Intent? {
val intent = Intent("com.meizu.safe.security.SHOW_APPSEC")
intent.putExtra("packageName", context.packageName)
intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity")
return if (hasActivity(context, intent)) {
intent
} else MdefaultApi(context)
}
private fun hasActivity(context: Context, intent: Intent?): Boolean {
val packageManager = context.packageManager
return packageManager.queryIntentActivities(
intent!!,
PackageManager.MATCH_DEFAULT_ONLY
).size > 0
}
}
}
权限说明
“Draw Over Other Apps”(在其他使用上层制作)权限:
这个权限答应使用在其他使用的上层制作悬浮窗口,例如悬浮告诉、悬浮工具栏、聊天头像等。经过这个权限,使用能够在其他使用的界面上显现自己的内容,可是这些窗口通常会有必定的约束,不会掩盖系统等级的UI元素(如状态栏、导航栏等)。
“Background Pop-ups”(后台弹窗)权限:
这个权限控制使用在后台是否答应弹出窗口,即便使用处于后台运转状态。这意味着即便使用不在前台,它依然能够显现一些弹窗、告诉或许提示。这能够让使用在后台运转时持续向用户展现重要的信息。
总结
- 先判别是否是特殊机型,假如是则需要请求后台弹出界面权限
- 假如不是特殊机型,则有两种计划,一是全屏告诉,二是请求在其他使用上层制作权限