混合开发Android FlutterActivity 的透明状态栏
前言
我们知道,在纯flutter 的项目中,设置透明状态栏只需要在flutter 的入口处添加下面这段代码就可以,
if (Platform.isAndroid) {
//设置Android头部的导航栏透明
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(
statusBarColor: Colors.transparent, //全局设置透明
statusBarIconBrightness: Brightness.light
//light:黑色图标 dark:白色图标
//在此处设置statusBarIconBrightness为全局设置
);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
对于电量、信号等设置,只需要全局设置 statusBarIconBrightness 就可以.
单页面设置:
appBar: AppBar(
title: new Text(''),
elevation: 0,
brightness: Brightness.dark, //设置为白色字体
),
问题?
那么对于混合开发的项目呢?实测发现,对于Android混合开发的项目,我们打开一个flutter 自带的FlutterActivity ,可以发现,第一次进入页面状态栏很正常,但是第二次打开的时候,状态栏变全白了。单个页面的设置也不生效了,这是怎么回事呢?
源码分析
1、太长不看版
1、继承 FlutterActivity
2、覆写 onPostResume
3、在 onPostResume 中重新设置状态栏
/**
* @description: flutter 页面容器
*/
public class FlutterContainerActivity extends FlutterActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置透明状态栏
StatusBarHelper.translucent(this);
}
@Override
public void onPostResume() {
super.onPostResume();
// 重新设置 状态栏 图标 mode
StatusBarHelper.setStatusBarDarkMode(this);
}
}
2、源码原理版
档FlutterActivity 创建 onCreate 的时候,
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
switchLaunchThemeForNormalTheme();
super.onCreate(savedInstanceState);
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this);
delegate.onActivityCreated(savedInstanceState);
configureWindowForTransparency();
setContentView(createFlutterView());
configureStatusBarForFullscreenFlutterExperience();
}
会实例化创建 FlutterActivityAndFragmentDelegate 代理类,用来绑定host , 绑定FlutterEngine .
当FlutterActivity 走完 onResume 后,会进入 onPostResume 这个方法,
@Override
protected void onResume() {
super.onResume();
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
delegate.onResume();
}
@Override
public void onPostResume() {
super.onPostResume();
delegate.onPostResume();
}
这个方法很简单,主要的逻辑都是通过 delegate 实现,我们进去看下,
void onPostResume() {
Log.v(TAG, "onPostResume()");
ensureAlive();
if (flutterEngine != null) {
if (platformPlugin != null) {
// TODO(mattcarroll): find a better way to handle the update of UI overlays than calling
// through
// to platformPlugin. We're implicitly entangling the Window, Activity,
// Fragment,
// and engine all with this one call.
platformPlugin.updateSystemUiOverlays();
}
} else {
Log.w(TAG, "onPostResume() invoked before FlutterFragment was attached to an Activity.");
}
}
在这里,主要是判断 flutterEngine 是否存在,不存在输出log,存在的话,通过调用 platformPlugin.updateSystemUiOverlays() 来更新状态栏。而 platformPlugin 则是在FlutterActivity onCreate 的时候,通过
platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
获取的。
我们进入 platformPlugin 来看看具体如何实现的。
在 PlatformPlugin 中,
public void updateSystemUiOverlays() {
activity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays);
if (currentTheme != null) {
setSystemChromeSystemUIOverlayStyle(currentTheme);
}
}
主要是通过全局变量 currentTheme 来设置的。那这个全局变量 currentTheme 如何赋值的呢?
通过 debug 发现,在我们第一次打开FlutterActivity 的时候,我们在dart 层设置的透明状态栏以及图标,dart 通过 PlatformChannel 的方式传递到了 PlatformPlugin 中进行实现的。当我们在dart 层设置完成后,通过发送 “ SystemChrome.setSystemUIOverlayStyle ” 来通知原生端进行更新,
//PlatformChannel 类 MethodChannel.MethodCallHandler 实现
case "SystemChrome.setSystemUIOverlayStyle":
try {
SystemChromeStyle systemChromeStyle =
decodeSystemChromeStyle((JSONObject) arguments);
platformMessageHandler.setSystemUiOverlayStyle(systemChromeStyle);
result.success(null);
} catch (JSONException | NoSuchFieldException exception) {
// JSONException: One or more expected fields were either omitted or referenced an
// invalid type.
// NoSuchFieldException: One or more of the brightness names are invalid.
result.error("error", exception.getMessage(), null);
}
break;
然后在PlatformPlugin 中调用 “ setSystemChromeSystemUIOverlayStyle ” 方法,设置状态栏并给 currentTheme 赋值的。
结论
到这里我们可以知道,为啥第一次打开FlutterActivity 状态栏有效,第二次打开无效了,因为在 dart 层,main.dart 中
runApp(MyApp());
只会走一次,第二次打开就不会走了。
PS:
StatusBarHelper 源码
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class StatusBarHelper {
private final static int STATUSBAR_TYPE_DEFAULT = 0;
private final static int STATUSBAR_TYPE_MIUI = 1;
private final static int STATUSBAR_TYPE_FLYME = 2;
private final static int STATUSBAR_TYPE_ANDROID6 = 3; // Android 6.0
private final static int STATUS_BAR_DEFAULT_HEIGHT_DP = 25; // 大部分状态栏都是25dp
// 在某些机子上存在不同的density值,所以增加两个虚拟值
public static float sVirtualDensity = -1;
public static float sVirtualDensityDpi = -1;
private static int sStatusBarHeight = -1;
private static @StatusBarType
int mStatusBarType = STATUSBAR_TYPE_DEFAULT;
private static Integer sTransparentValue;
public static void translucent(Activity activity) {
translucent(activity.getWindow());
}
public static void translucent(Window window) {
translucent(window, 0x40000000);
}
private static boolean supportTranslucent() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
// Essential Phone 在 Android 8 之前沉浸式做得不全,系统不从状态栏顶部开始布局却会下发 WindowInsets
&& !(DeviceHelper.isEssentialPhone() && Build.VERSION.SDK_INT < 26);
}
@TargetApi(19)
public static void translucent(Window window, @ColorInt int colorOn5x) {
if (!supportTranslucent()) {
// 版本小于4.4,绝对不考虑沉浸式
return;
}
// if (QMUINotchHelper.isNotchOfficialSupport()) {
// handleDisplayCutoutMode(window);
// }
// 小米和魅族4.4 以上版本支持沉浸式
// 小米 Android 6.0 ,开发版 7.7.13 及以后版本设置黑色字体又需要 clear FLAG_TRANSLUCENT_STATUS, 因此还原为官方模式
if (DeviceHelper.isFlymeLowerThan(8) || (DeviceHelper.isMIUI() && Build.VERSION.SDK_INT < Build.VERSION_CODES.M)) {
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
window.getDecorView().setSystemUiVisibility(systemUiVisibility);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && supportTransclentStatusBar6()) {
// android 6以后可以改状态栏字体颜色,因此可以自行设置为透明
// ZUK Z1是个另类,自家应用可以实现字体颜色变色,但没开放接口
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
} else {
// android 5不能修改状态栏字体颜色,因此直接用FLAG_TRANSLUCENT_STATUS,nexus表现为半透明
// 魅族和小米的表现如何?
// update: 部分手机运用FLAG_TRANSLUCENT_STATUS时背景不是半透明而是没有背景了。。。。。
// window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 采取setStatusBarColor的方式,部分机型不支持,那就纯黑了,保证状态栏图标可见
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(colorOn5x);
}
// } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// // android4.4的默认是从上到下黑到透明,我们的背景是白色,很难看,因此只做魅族和小米的
// } else if(Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1){
// // 如果app 为白色,需要更改状态栏颜色,因此不能让19一下支持透明状态栏
// Window window = activity.getWindow();
// Integer transparentValue = getStatusBarAPITransparentValue(activity);
// if(transparentValue != null) {
// window.getDecorView().setSystemUiVisibility(transparentValue);
// }
}
}
/**
* 设置状态栏黑色字体图标,
* 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
*
* @param activity 需要被处理的 Activity
*/
public static boolean setStatusBarLightMode(Activity activity) {
if (activity == null) return false;
// 无语系列:ZTK C2016只能时间和电池图标变色。。。。
if (DeviceHelper.isZTKC2016()) {
return false;
}
if (mStatusBarType != STATUSBAR_TYPE_DEFAULT) {
return setStatusBarLightMode(activity, mStatusBarType);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (isMIUICustomStatusBarLightModeImpl() && MIUISetStatusBarLightMode(activity.getWindow(), true)) {
mStatusBarType = STATUSBAR_TYPE_MIUI;
return true;
} else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) {
mStatusBarType = STATUSBAR_TYPE_FLYME;
return true;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Android6SetStatusBarLightMode(activity.getWindow(), true);
mStatusBarType = STATUSBAR_TYPE_ANDROID6;
return true;
}
}
return false;
}
/**
* 已知系统类型时,设置状态栏黑色字体图标。
* 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
*
* @param activity 需要被处理的 Activity
* @param type StatusBar 类型,对应不同的系统
*/
private static boolean setStatusBarLightMode(Activity activity, @StatusBarType int type) {
if (type == STATUSBAR_TYPE_MIUI) {
return MIUISetStatusBarLightMode(activity.getWindow(), true);
} else if (type == STATUSBAR_TYPE_FLYME) {
return FlymeSetStatusBarLightMode(activity.getWindow(), true);
} else if (type == STATUSBAR_TYPE_ANDROID6) {
return Android6SetStatusBarLightMode(activity.getWindow(), true);
}
return false;
}
/**
* 设置状态栏白色字体图标
* 支持 4.4 以上版本 MIUI 和 Flyme,以及 6.0 以上版本的其他 Android
*/
public static boolean setStatusBarDarkMode(Activity activity) {
if (activity == null) return false;
if (mStatusBarType == STATUSBAR_TYPE_DEFAULT) {
// 默认状态,不需要处理
return true;
}
if (mStatusBarType == STATUSBAR_TYPE_MIUI) {
return MIUISetStatusBarLightMode(activity.getWindow(), false);
} else if (mStatusBarType == STATUSBAR_TYPE_FLYME) {
return FlymeSetStatusBarLightMode(activity.getWindow(), false);
} else if (mStatusBarType == STATUSBAR_TYPE_ANDROID6) {
return Android6SetStatusBarLightMode(activity.getWindow(), false);
}
return true;
}
/**
* 设置状态栏字体图标为深色,Android 6
*
* @param window 需要设置的窗口
* @param light 是否把状态栏字体及图标颜色设置为深色
* @return boolean 成功执行返回true
*/
@TargetApi(23)
private static boolean Android6SetStatusBarLightMode(Window window, boolean light) {
View decorView = window.getDecorView();
int systemUi = decorView.getSystemUiVisibility();
if (light) {
systemUi |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else {
systemUi ^= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
}
decorView.setSystemUiVisibility(systemUi);
if (DeviceHelper.isMIUIV9()) {
// MIUI 9 低于 6.0 版本依旧只能回退到以前的方案
// https://github.com/Tencent/QMUI_Android/issues/160
MIUISetStatusBarLightMode(window, light);
}
return true;
}
/**
* 设置状态栏字体图标为深色,需要 MIUIV6 以上
*
* @param window 需要设置的窗口
* @param light 是否把状态栏字体及图标颜色设置为深色
* @return boolean 成功执行返回 true
*/
@SuppressWarnings("unchecked")
public static boolean MIUISetStatusBarLightMode(Window window, boolean light) {
boolean result = false;
if (window != null) {
Class clazz = window.getClass();
try {
int darkModeFlag;
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (light) {
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体
} else {
extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
}
result = true;
} catch (Exception ignored) {
}
}
return result;
}
/**
* 更改状态栏图标、文字颜色的方案是否是MIUI自家的, MIUI9 && Android 6 之后用回Android原生实现
* 见小米开发文档说明:https://dev.mi.com/console/doc/detail?pId=1159
*/
private static boolean isMIUICustomStatusBarLightModeImpl() {
if (DeviceHelper.isMIUIV9() && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
return DeviceHelper.isMIUIV5() || DeviceHelper.isMIUIV6() ||
DeviceHelper.isMIUIV7() || DeviceHelper.isMIUIV8();
}
/**
* 设置状态栏图标为深色和魅族特定的文字风格
* 可以用来判断是否为 Flyme 用户
*
* @param window 需要设置的窗口
* @param light 是否把状态栏字体及图标颜色设置为深色
* @return boolean 成功执行返回true
*/
public static boolean FlymeSetStatusBarLightMode(Window window, boolean light) {
boolean result = false;
if (window != null) {
Android6SetStatusBarLightMode(window, light);
// flyme 在 6.2.0.0A 支持了 Android 官方的实现方案,旧的方案失效
// 高版本调用这个出现不可预期的 Bug,官方文档也没有给出完整的高低版本兼容方案
if (DeviceHelper.isFlymeLowerThan(7)) {
try {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class
.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class
.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (light) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
result = true;
} catch (Exception ignored) {
}
} else if (DeviceHelper.isFlyme()) {
result = true;
}
}
return result;
}
/**
* 检测 Android 6.0 是否可以启用 window.setStatusBarColor(Color.TRANSPARENT)。
*/
public static boolean supportTransclentStatusBar6() {
return !(DeviceHelper.isZUKZ1() || DeviceHelper.isZTKC2016());
}
@IntDef({STATUSBAR_TYPE_DEFAULT, STATUSBAR_TYPE_MIUI, STATUSBAR_TYPE_FLYME, STATUSBAR_TYPE_ANDROID6})
@Retention(RetentionPolicy.SOURCE)
private @interface StatusBarType {
}
}
DeviceHelper:
import android.annotation.TargetApi;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DeviceHelper {
private final static String TAG = "QMUIDeviceHelper";
private final static String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
private static final String KEY_FLYME_VERSION_NAME = "ro.build.display.id";
private final static String FLYME = "flyme";
private final static String ZTEC2016 = "zte c2016";
private final static String ZUKZ1 = "zuk z1";
private final static String ESSENTIAL = "essential";
private final static String MEIZUBOARD[] = {"m9", "M9", "mx", "MX"};
private static String sMiuiVersionName;
private static String sFlymeVersionName;
private static boolean sIsTabletChecked = false;
private static boolean sIsTabletValue = false;
private static final String BRAND = Build.BRAND.toLowerCase();
static {
Properties properties = new Properties();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// android 8.0,读取 /system/uild.prop 会报 permission denied
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(new File(Environment.getRootDirectory(), "build.prop"));
properties.load(fileInputStream);
} catch (Exception e) {
// QMUILog.printErrStackTrace(TAG, e, "read file error");
} finally {
// QMUILangHelper.close(fileInputStream);
}
}
Class<?> clzSystemProperties = null;
try {
clzSystemProperties = Class.forName("android.os.SystemProperties");
Method getMethod = clzSystemProperties.getDeclaredMethod("get", String.class);
// miui
sMiuiVersionName = getLowerCaseName(properties, getMethod, KEY_MIUI_VERSION_NAME);
//flyme
sFlymeVersionName = getLowerCaseName(properties, getMethod, KEY_FLYME_VERSION_NAME);
} catch (Exception e) {
// QMUILog.printErrStackTrace(TAG, e, "read SystemProperties error");
}
}
private static boolean _isTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >=
Configuration.SCREENLAYOUT_SIZE_LARGE;
}
/**
* 判断是否为平板设备
*/
public static boolean isTablet(Context context) {
if (sIsTabletChecked) {
return sIsTabletValue;
}
sIsTabletValue = _isTablet(context);
sIsTabletChecked = true;
return sIsTabletValue;
}
/**
* 判断是否是flyme系统
*/
public static boolean isFlyme() {
return !TextUtils.isEmpty(sFlymeVersionName) && sFlymeVersionName.contains(FLYME);
}
/**
* 判断是否是MIUI系统
*/
public static boolean isMIUI() {
return !TextUtils.isEmpty(sMiuiVersionName);
}
public static boolean isMIUIV5() {
return "v5".equals(sMiuiVersionName);
}
public static boolean isMIUIV6() {
return "v6".equals(sMiuiVersionName);
}
public static boolean isMIUIV7() {
return "v7".equals(sMiuiVersionName);
}
public static boolean isMIUIV8() {
return "v8".equals(sMiuiVersionName);
}
public static boolean isMIUIV9() {
return "v9".equals(sMiuiVersionName);
}
public static boolean isFlymeLowerThan(int majorVersion){
return isFlymeLowerThan(majorVersion, 0, 0);
}
public static boolean isFlymeLowerThan(int majorVersion, int minorVersion, int patchVersion) {
boolean isLower = false;
if (sFlymeVersionName != null && !sFlymeVersionName.equals("")) {
try{
Pattern pattern = Pattern.compile("(\\d+\\.){2}\\d");
Matcher matcher = pattern.matcher(sFlymeVersionName);
if (matcher.find()) {
String versionString = matcher.group();
if (versionString.length() > 0) {
String[] version = versionString.split("\\.");
if (version.length >= 1) {
if (Integer.parseInt(version[0]) < majorVersion) {
isLower = true;
}
}
if(version.length >= 2 && minorVersion > 0){
if (Integer.parseInt(version[1]) < majorVersion) {
isLower = true;
}
}
if(version.length >= 3 && patchVersion > 0){
if (Integer.parseInt(version[2]) < majorVersion) {
isLower = true;
}
}
}
}
}catch (Throwable ignore){
}
}
return isMeizu() && isLower;
}
public static boolean isMeizu() {
return isPhone(MEIZUBOARD) || isFlyme();
}
/**
* 判断是否为小米
* https://dev.mi.com/doc/?p=254
*/
public static boolean isXiaomi() {
return Build.MANUFACTURER.toLowerCase().equals("xiaomi");
}
public static boolean isVivo() {
return BRAND.contains("vivo") || BRAND.contains("bbk");
}
public static boolean isOppo() {
return BRAND.contains("oppo");
}
public static boolean isHuawei() {
return BRAND.contains("huawei") || BRAND.contains("honor");
}
public static boolean isEssentialPhone(){
return BRAND.contains("essential");
}
/**
* 判断是否为 ZUK Z1 和 ZTK C2016。
* 两台设备的系统虽然为 android 6.0,但不支持状态栏icon颜色改变,因此经常需要对它们进行额外判断。
*/
public static boolean isZUKZ1() {
final String board = android.os.Build.MODEL;
return board != null && board.toLowerCase().contains(ZUKZ1);
}
public static boolean isZTKC2016() {
final String board = android.os.Build.MODEL;
return board != null && board.toLowerCase().contains(ZTEC2016);
}
private static boolean isPhone(String[] boards) {
final String board = android.os.Build.BOARD;
if (board == null) {
return false;
}
for (String board1 : boards) {
if (board.equals(board1)) {
return true;
}
}
return false;
}
/**
* 判断悬浮窗权限(目前主要用户魅族与小米的检测)。
*/
public static boolean isFloatWindowOpAllowed(Context context) {
final int version = Build.VERSION.SDK_INT;
if (version >= 19) {
return checkOp(context, 24); // 24 是AppOpsManager.OP_SYSTEM_ALERT_WINDOW 的值,该值无法直接访问
} else {
try {
return (context.getApplicationInfo().flags & 1 << 27) == 1 << 27;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
@TargetApi(19)
private static boolean checkOp(Context context, int op) {
final int version = Build.VERSION.SDK_INT;
if (version >= Build.VERSION_CODES.KITKAT) {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
try {
Method method = manager.getClass().getDeclaredMethod("checkOp", int.class, int.class, String.class);
int property = (Integer) method.invoke(manager, op,
Binder.getCallingUid(), context.getPackageName());
return AppOpsManager.MODE_ALLOWED == property;
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
@Nullable
private static String getLowerCaseName(Properties p, Method get, String key) {
String name = p.getProperty(key);
if (name == null) {
try {
name = (String) get.invoke(null, key);
} catch (Exception ignored) {
}
}
if (name != null) name = name.toLowerCase();
return name;
}
}
本文地址:https://blog.csdn.net/weixin_44157988/article/details/108994977
上一篇: 快播5以上新版本不能点播怎么办?
下一篇: 怎么利用QQ影音从视频中截取gif?