feat(network): 新增应用管控接口与广播兼容适配
- 新增 getIconControl 接口,按禁用/显示清单管理桌面图标 - 应用管控数据与在线禁用数据分开存储 - 新增 ReceiverCompat 统一处理 Android13+ 广播注册导出标志 - 异常捕获交回系统默认处理,移除主线程异常吞没逻辑 - 定位数据存储由 SPUtils 迁移至 MMKV - 修复百度定位 SDK 构造异常及隐私合规声明
This commit is contained in:
@@ -13,24 +13,25 @@ static def releaseTime() {
|
||||
android {
|
||||
namespace "com.aoleyun.sn"
|
||||
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 36
|
||||
buildToolsVersion "36.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.aoleyun.sn"
|
||||
versionCode 246
|
||||
versionName "1.6.1013"
|
||||
versionCode 250
|
||||
versionName "1.6.1016"
|
||||
|
||||
//There are no CERT files because If the mini sdk version is 23+, the AGP will ignore the V1 scheme signature.
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 36
|
||||
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
ndk {
|
||||
//选择要添加的对应 cpu 类型的 .so 库。
|
||||
abiFilters 'arm64-v8a', 'armeabi-v7a'
|
||||
// 还可以添加 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
|
||||
// 还可以添加 'armeabi', 'x86', 'mips', 'mips64'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
@@ -322,9 +323,31 @@ android {
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
|
||||
crosshatch {
|
||||
storeFile file("keystore/AospCrosshatch.keystore")
|
||||
storePassword "Fan1996"
|
||||
keyAlias "AospCrosshatch"
|
||||
keyPassword "Fan1996"
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
CrosshatchDebug.initWith(debug)
|
||||
CrosshatchDebug {
|
||||
buildConfigField "String", "platform", '"AOSP"'
|
||||
debuggable true
|
||||
versionNameSuffix "_debug"
|
||||
signingConfig signingConfigs.crosshatch
|
||||
}
|
||||
|
||||
CrosshatchRelease.initWith(release)
|
||||
CrosshatchRelease {
|
||||
buildConfigField "String", "platform", '"AOSP"'
|
||||
signingConfig signingConfigs.crosshatch
|
||||
}
|
||||
|
||||
G122JDebug.initWith(debug)
|
||||
G122JDebug {
|
||||
@@ -884,7 +907,7 @@ dependencies {
|
||||
//阿里云推送
|
||||
implementation 'com.aliyun.ams:alicloud-android-push:3.8.0'
|
||||
//百度地图
|
||||
implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.1.8'
|
||||
implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.7.0'
|
||||
//MMKV
|
||||
implementation 'com.tencent:mmkv-static:2.4.0'
|
||||
//bugly
|
||||
|
||||
BIN
app/keystore/AospCrosshatch.keystore
Normal file
BIN
app/keystore/AospCrosshatch.keystore
Normal file
Binary file not shown.
@@ -107,6 +107,14 @@
|
||||
<!-- 如果您的应用需要后台定位权限,且有可能运行在Android Q设备上,并且设置了target>28,必须增加这个权限声明 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
|
||||
<!-- ============ 适配 Android 13 / 14+(targetSdk 33+)新增要求 ============ -->
|
||||
<!-- 前台服务:Android 14 起必须声明具体类型并申请对应权限 -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<!-- 通知权限:前台服务通知展示(系统应用默认已授予) -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<!-- 精确闹钟:Android 14 起 SCHEDULE_EXACT_ALARM 默认被拒绝,需声明 USE_EXACT_ALARM -->
|
||||
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
|
||||
|
||||
<application
|
||||
android:name=".base.BaseApplication"
|
||||
android:allowBackup="true"
|
||||
@@ -205,10 +213,16 @@
|
||||
android:name=".service.ShakeDetectionService"
|
||||
android:enabled="true"
|
||||
android:exported="true" />
|
||||
<!-- Android 14 起前台服务必须声明 foregroundServiceType,否则 startForeground 会抛异常;
|
||||
specialUse 不受 Android 15 的 6 小时限制与 BOOT_COMPLETED 启动限制影响 -->
|
||||
<service
|
||||
android:name=".service.main.MainService"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="specialUse">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="设备管控常驻服务,用于策略同步与设备状态维护" />
|
||||
<intent-filter android:priority="1000">
|
||||
<action android:name="android.intent.action.USER_PRESENT" />
|
||||
<action android:name="android.intent.action.SCREEN_ON" />
|
||||
|
||||
@@ -34,11 +34,9 @@ import com.aoleyun.sn.utils.FlowInfo;
|
||||
import com.aoleyun.sn.utils.GetFlowUtil;
|
||||
import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.NetworkUtils;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.utils.ServiceAliveUtils;
|
||||
import com.aoleyun.sn.utils.Utils;
|
||||
import com.blankj.utilcode.util.PathUtils;
|
||||
import com.google.gson.Gson;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
|
||||
|
||||
@@ -182,7 +180,6 @@ public class SplashActivity extends AppCompatActivity {
|
||||
// }
|
||||
// ActivityManager mAm = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
// mAm.killBackgroundProcesses("com.android.documentsui");
|
||||
Log.e(TAG, "debugTest: SPUtils all " + new Gson().toJson(SPUtils.getAll(this)));
|
||||
|
||||
// Utils.getPublicIP(this);
|
||||
Utils.obtainWifiInfo(this);
|
||||
|
||||
@@ -120,7 +120,9 @@ public class DebugActivity extends BaseMvvmActivity<DebugViewModel, ActivityDebu
|
||||
.setIntent(shortcutInfoIntent)
|
||||
.build();
|
||||
//当添加快捷方式的确认弹框弹出来时,将被回调,自定义Receiver
|
||||
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, ShortcutReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0,
|
||||
new Intent(context, ShortcutReceiver.class),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
ShortcutManagerCompat.requestPinShortcut(context, info,
|
||||
shortcutCallbackIntent.getIntentSender());
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import com.aoleyun.sn.service.main.MainService;
|
||||
import com.aoleyun.sn.utils.ApkUtils;
|
||||
import com.aoleyun.sn.utils.GlideLoadUtils;
|
||||
import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.utils.ReceiverCompat;
|
||||
import com.aoleyun.sn.utils.SysSettingUtils;
|
||||
import com.aoleyun.sn.utils.TimeUtils;
|
||||
import com.aoleyun.sn.utils.Utils;
|
||||
@@ -445,7 +445,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
} else {
|
||||
setIv_head(head_img);
|
||||
}
|
||||
SPUtils.put(MainActivity.this, CommonConfig.ADMIN_ID, studentsInfo.getAdmin_id());
|
||||
mMMKV.encode(CommonConfig.ADMIN_ID, studentsInfo.getAdmin_id());
|
||||
}
|
||||
});
|
||||
mViewModel.mEBagCodeData.observe(this, new Observer<Boolean>() {
|
||||
@@ -589,7 +589,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
super.onPause();
|
||||
NetworkUtils.unregisterNetworkStatusChangedListener(this);
|
||||
if (mUpdateReceiver != null) {
|
||||
unregisterReceiver(mUpdateReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mUpdateReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,7 +637,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
|
||||
private void unregister() {
|
||||
if (mPanelReceiver != null) {
|
||||
unregisterReceiver(mPanelReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mPanelReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,7 +648,8 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(PANEL_SHOW);
|
||||
filter.addAction(PANEL_HIDE);
|
||||
registerReceiver(mPanelReceiver, filter);
|
||||
//面板显隐广播由兄弟管控应用下发,需保持可被外部发送
|
||||
ReceiverCompat.registerReceiver(this, mPanelReceiver, filter, ReceiverCompat.RECEIVER_EXPORTED);
|
||||
}
|
||||
|
||||
public static final String PANEL_SHOW = "panel_show_key";
|
||||
@@ -680,7 +681,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
}
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(UPDATE_LOCKED_STATUS);
|
||||
registerReceiver(mUpdateReceiver, filter);
|
||||
ReceiverCompat.registerReceiver(this, mUpdateReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
private class UpdateReceiver extends BroadcastReceiver {
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.aoleyun.sn.adapter.AppUpdateAdapter;
|
||||
import com.aoleyun.sn.base.mvvm.BaseMvvmActivity;
|
||||
import com.aoleyun.sn.bean.AppUpdateInfo;
|
||||
import com.aoleyun.sn.databinding.ActivityUpdateBinding;
|
||||
import com.aoleyun.sn.utils.ReceiverCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -71,7 +72,7 @@ public class UpdateActivity extends BaseMvvmActivity<UpdateViewModel, ActivityUp
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mNewAppReceiver != null) {
|
||||
unregisterReceiver(mNewAppReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mNewAppReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +85,8 @@ public class UpdateActivity extends BaseMvvmActivity<UpdateViewModel, ActivityUp
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
registerReceiver(mNewAppReceiver, filter);
|
||||
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
|
||||
ReceiverCompat.registerReceiver(this, mNewAppReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
private NewAppReceiver mNewAppReceiver;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.aoleyun.sn.base;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
@@ -94,30 +92,27 @@ public class BaseApplication extends MultiDexApplication {
|
||||
}
|
||||
|
||||
private void catchException() {
|
||||
final Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(
|
||||
new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
Log.e("捕获异常子线程:", Thread.currentThread().getName() +
|
||||
"在:" + e.getStackTrace()[0].getClassName());
|
||||
e.printStackTrace();
|
||||
try {
|
||||
Log.e("捕获异常:", Thread.currentThread().getName() +
|
||||
"在:" + e.getStackTrace()[0].getClassName());
|
||||
e.printStackTrace();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
//交回系统默认处理(弹崩溃提示并结束进程,xcrash/Bugly 仍能正常抓取堆栈)。
|
||||
//原实现在主线程用 while (true) { Looper.loop(); } 吞掉异常,
|
||||
//会使进程处于"半死"状态:Activity/Service 实际未创建,界面点开即闪退,
|
||||
//并最终演变为 ANR,故移除该写法。
|
||||
if (defaultHandler != null) {
|
||||
defaultHandler.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
//下面是新增方法!
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Looper.loop(); //会先执行这个方法,然后在执行下面的异常捕获方法!
|
||||
} catch (Exception e) {
|
||||
Log.e("捕获异常主线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface OnRegisterResult {
|
||||
|
||||
28
app/src/main/java/com/aoleyun/sn/bean/IconControlInfo.java
Normal file
28
app/src/main/java/com/aoleyun/sn/bean/IconControlInfo.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.aoleyun.sn.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class IconControlInfo implements Serializable {
|
||||
private static final long serialVersionUID = 7845568828765632321L;
|
||||
|
||||
String app_package;
|
||||
|
||||
// 1 展示 0 隐藏
|
||||
int control_type;
|
||||
|
||||
public int getControl_type() {
|
||||
return control_type;
|
||||
}
|
||||
|
||||
public void setControl_type(int control_type) {
|
||||
this.control_type = control_type;
|
||||
}
|
||||
|
||||
public String getApp_package() {
|
||||
return app_package;
|
||||
}
|
||||
|
||||
public void setApp_package(String app_package) {
|
||||
this.app_package = app_package;
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,10 @@ public class CommonConfig {
|
||||
public final static String AOLE_ACTION_RESTORE_FORBID_ON = "aole_restore_forbid_on";
|
||||
|
||||
public final static String AOLE_ACTION_DISABLE_APP = "aole_disable_app_list";
|
||||
/*应用管控禁用列表(getIconControl 接口下发,与 AOLE_ACTION_DISABLE_APP 是两套独立数据)*/
|
||||
public final static String AOLE_ACTION_HIDE_DESKTOP_ICON = "aole_hide_desktop_icon_list";
|
||||
/*应用管控要求显示列表(getIconControl 接口下发,优先级高于本地默认禁用)*/
|
||||
public final static String AOLE_ACTION_SHOW_DESKTOP_ICON = "aole_show_desktop_icon_list";
|
||||
/*公共应用白名单*/
|
||||
public final static String AOLE_PUBLIC_APP_INSTALL_WHITELIST = "aole_public_app_install_whitelist_set";
|
||||
/*分组应用白名单*/
|
||||
|
||||
@@ -54,9 +54,6 @@ public class ReceiverHandler extends Handler {
|
||||
return sDefaultInstance;
|
||||
}
|
||||
|
||||
// private SharedPreferences mDefaultSharedPreferences =
|
||||
// MyApplication.getInstance().getDefaultSharedPreferences();
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
int what = msg.what;
|
||||
@@ -143,28 +140,6 @@ public class ReceiverHandler extends Handler {
|
||||
// DebugLoggerUIServiceManager.getInstance().getService().dealWithBypassAction(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Judge whether need to start up DebugLoggerUI service at boot time. If none log instance was
|
||||
* set to start automatically when boot up, just remove this process to avoid confuse user
|
||||
*/
|
||||
// private boolean needStartLogAtBootTime() {
|
||||
// boolean needStart = false;
|
||||
// for (Integer logType : Utils.LOG_TYPE_SET) {
|
||||
// if (logType == Utils.LOG_TYPE_MET) {
|
||||
// continue;
|
||||
// }
|
||||
// if (Utils.VALUE_START_AUTOMATIC_ON == mDefaultSharedPreferences.getBoolean(
|
||||
// Utils.KEY_START_AUTOMATIC_MAP.get(logType),
|
||||
// Utils.DEFAULT_CONFIG_LOG_AUTO_START_MAP.get(logType))) {
|
||||
// needStart = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// Utils.logd(TAG, "-->needStartLogAtBootTime(), needStart=" + needStart);
|
||||
// return needStart;
|
||||
// }
|
||||
|
||||
|
||||
public class ServiceNullException extends Exception {
|
||||
private static final long serialVersionUID = -430298482097527072L;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class ConnectManager {
|
||||
}
|
||||
long nowTime = System.currentTimeMillis();
|
||||
long lastTime = mMMKV.decodeLong(key + "_time");
|
||||
// long lastTime = (long) SPUtils.get(mContext, key, 0L);
|
||||
// long lastTime = mMMKV.decodeLong(key, 0L);
|
||||
Log.e(TAG, "isNeedConnect: key = " + key + "\ttime = " + lastTime);
|
||||
long intervalTime = getConnectModeTime(connectMode);
|
||||
//防止一分钟内重复请求
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.aoleyun.sn.bean.ElectronicFenceInfo;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.comm.CommonConfig;
|
||||
import com.baidu.location.BDAbstractLocationListener;
|
||||
import com.baidu.location.BDLocation;
|
||||
import com.baidu.location.LocationClient;
|
||||
@@ -29,10 +29,13 @@ import com.baidu.trace.model.CoordType;
|
||||
import com.baidu.trace.model.LatLng;
|
||||
import com.baidu.trace.model.OnTraceListener;
|
||||
import com.baidu.trace.model.PushMessage;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
public class MapManager {
|
||||
private String TAG = "MapManager";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static MapManager sInstance;
|
||||
private Context mContext;
|
||||
@@ -65,14 +68,21 @@ public class MapManager {
|
||||
}
|
||||
|
||||
public void initMap() {
|
||||
LocationClient.setAgreePrivacy(true);
|
||||
|
||||
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ASSISTED_GPS_ENABLED, 1);
|
||||
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+gps");
|
||||
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+network");
|
||||
mLocationClient = new LocationClient(mContext);
|
||||
mLocationClient.setLocOption(getDefaultLocationClientOption());
|
||||
mLocationClient.registerLocationListener(mListener);
|
||||
mLocationClient.stop();
|
||||
mLocationClient.start();
|
||||
//百度定位 SDK 9.7.0 起 LocationClient 构造方法声明了 throws Exception
|
||||
try {
|
||||
mLocationClient = new LocationClient(mContext);
|
||||
mLocationClient.setLocOption(getDefaultLocationClientOption());
|
||||
mLocationClient.registerLocationListener(mListener);
|
||||
mLocationClient.stop();
|
||||
mLocationClient.start();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initMap: LocationClient 创建失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public LocationClient getLocationClient() {
|
||||
@@ -131,28 +141,28 @@ public class MapManager {
|
||||
case BDLocation.TypeOffLineLocation:// 离线定位结果
|
||||
Log.e(TAG, "onLocationChanged: " + "定位成功");
|
||||
Log.e(TAG, "onLocationChanged: " + location.getAddrStr() + location.getLocationDescribe());
|
||||
SPUtils.put(mContext, "MapAddress", location.getAddrStr() + location.getLocationDescribe());
|
||||
SPUtils.put(mContext, "longitude", location.getLongitude());
|
||||
SPUtils.put(mContext, "latitude", location.getLatitude());
|
||||
SPUtils.put(mContext, "MapError", "-");
|
||||
mMMKV.encode("MapAddress", location.getAddrStr() + location.getLocationDescribe());
|
||||
mMMKV.encode("longitude", String.valueOf(location.getLongitude()));
|
||||
mMMKV.encode("latitude", String.valueOf(location.getLatitude()));
|
||||
mMMKV.encode("MapError", "-");
|
||||
break;
|
||||
case BDLocation.TypeServerError:
|
||||
Log.e(TAG, "onReceiveLocation: " + "服务端网络定位失败");
|
||||
SPUtils.put(mContext, "MapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");
|
||||
mMMKV.encode("MapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");
|
||||
break;
|
||||
case BDLocation.TypeNetWorkException:
|
||||
Log.e(TAG, "onReceiveLocation: " + "网络不同导致定位失败,请检查网络是否通畅");
|
||||
SPUtils.put(mContext, "MapError", "网络不同导致定位失败,请检查网络是否通畅");
|
||||
mMMKV.encode("MapError", "网络不同导致定位失败,请检查网络是否通畅");
|
||||
break;
|
||||
case BDLocation.TypeCriteriaException:
|
||||
Log.e(TAG, "onReceiveLocation: " + "无法获取有效定位依据导致定位失败");
|
||||
SPUtils.put(mContext, "MapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
|
||||
mMMKV.encode("MapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "MapAddress: " + (String) SPUtils.get(mContext, "MapAddress", "-"));
|
||||
Log.e(TAG, "MapError: " + (String) SPUtils.get(mContext, "MapError", "-"));
|
||||
Log.e(TAG, "MapAddress: " + mMMKV.decodeString("MapAddress", "-"));
|
||||
Log.e(TAG, "MapError: " + mMMKV.decodeString("MapError", "-"));
|
||||
// Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-network");
|
||||
// Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-gps");
|
||||
// Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "");
|
||||
@@ -214,7 +224,7 @@ public class MapManager {
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "onLocationChanged: " + "定位失败");
|
||||
SPUtils.put(mContext, "MapError", sb);
|
||||
mMMKV.encode("MapError", sb.toString());
|
||||
Log.e(TAG, "onLocDiagnosticMessage: " + sb);
|
||||
// logMsg(sb.toString(), tag);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.aoleyun.sn.bean.DefaultApp;
|
||||
import com.aoleyun.sn.bean.DeveloperBean;
|
||||
import com.aoleyun.sn.bean.EBagCode;
|
||||
import com.aoleyun.sn.bean.ForceDownloadData;
|
||||
import com.aoleyun.sn.bean.IconControlInfo;
|
||||
import com.aoleyun.sn.bean.LessonApp;
|
||||
import com.aoleyun.sn.bean.LessonJson;
|
||||
import com.aoleyun.sn.bean.LessonSetting;
|
||||
@@ -99,7 +100,6 @@ import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.MD5Util;
|
||||
import com.aoleyun.sn.utils.NetworkUtils;
|
||||
import com.aoleyun.sn.utils.PowerOffAlarmUtils;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.utils.SysSettingUtils;
|
||||
import com.aoleyun.sn.utils.TimeUtils;
|
||||
import com.aoleyun.sn.utils.URLUtils;
|
||||
@@ -132,6 +132,7 @@ import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
@@ -717,10 +718,15 @@ public class NetInterfaceManager {
|
||||
return schedule(getSystemSettingApi().getBrowserBookmarks(HTTP_KEY, Utils.getSerial(mContext)));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Observable<BaseResponse> getDesktopIconObservable() {
|
||||
return schedule(getSystemSettingApi().getDesktopIcon(HTTP_KEY, Utils.getSerial(mContext)));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<List<IconControlInfo>>> getIconControlObservable() {
|
||||
return schedule(getSystemSettingApi().getIconControl(HTTP_KEY, Utils.getSerial(mContext)));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getSystemSettingObservable() {
|
||||
return withCache(schedule(getSystemSettingApi().getSystemSettings(HTTP_KEY, Utils.getSerial(mContext))),
|
||||
UrlAddress.GET_FIRMWARE,
|
||||
@@ -1428,57 +1434,165 @@ public class NetInterfaceManager {
|
||||
});
|
||||
}
|
||||
|
||||
public void getDesktopIcon(BehaviorSubject<ActivityEvent> lifecycle, onCompleteCallback callback) {
|
||||
getDesktopIconObservable()
|
||||
/**
|
||||
* 应用 getIconControl 接口下发的应用管控清单。
|
||||
* <p>
|
||||
* 清单为空(接口失败或没有数据)表示接口没有任何管控:上次下发的禁用清单会被清空(应用恢复显示),
|
||||
* 未被接口提及的应用恢复为本地默认禁用。
|
||||
* <p>
|
||||
* 注意:应用图标禁用(AOLE_ACTION_HIDE_DESKTOP_ICON)与 getSnAppAttr 的在线禁用
|
||||
* (AOLE_ACTION_DISABLE_APP)是两套独立数据,分开存放。
|
||||
*
|
||||
* @param disablePkgs 需要禁用的应用(control_type = 0)
|
||||
* @param showPkgs 需要显示的应用(control_type = 1)
|
||||
*/
|
||||
private void applyIconControl(List<String> disablePkgs, List<String> showPkgs) {
|
||||
//旧的禁用清单
|
||||
Set<String> oldHidePkgs = ApkUtils.getDesktopHidePkgs(mContext);
|
||||
//本次要禁用的包名(含不同机型同功能应用的包名映射),持久化后 showAllApp 会跳过这些应用,避免图标被重新显示
|
||||
Set<String> hidePkgs = expandAlias(disablePkgs);
|
||||
//1、先保存接口数据:后续放开判断(isKeepDisableByOtherSource)要读到最新的显示清单,
|
||||
// 否则接口要求显示的应用会被"本地默认禁用"挡住
|
||||
String hideString = TextUtils.join(",", hidePkgs);
|
||||
String showString = TextUtils.join(",", showPkgs);
|
||||
Log.e("getIconControl", "applyIconControl: disable = " + hideString + ", show = " + showString);
|
||||
Settings.Global.putString(mContext.getContentResolver(),
|
||||
CommonConfig.AOLE_ACTION_HIDE_DESKTOP_ICON,
|
||||
TextUtils.isEmpty(hideString) ? "null" : hideString);
|
||||
Settings.Global.putString(mContext.getContentResolver(),
|
||||
CommonConfig.AOLE_ACTION_SHOW_DESKTOP_ICON,
|
||||
TextUtils.isEmpty(showString) ? "null" : showString);
|
||||
//2、按清单禁用
|
||||
for (String pkg : hidePkgs) {
|
||||
JgyUtils.getInstance().hideApp(pkg);
|
||||
}
|
||||
//3、按清单显示(被其他管控来源要求禁用的除外)
|
||||
for (String pack : showPkgs) {
|
||||
showAppIfAllowed(pack);
|
||||
}
|
||||
//4、上次禁用、本次既没要求禁用也没要求显示的应用,恢复显示
|
||||
for (String pkg : oldHidePkgs) {
|
||||
if (hidePkgs.contains(pkg) || showPkgs.contains(pkg)) {
|
||||
continue;
|
||||
}
|
||||
showAppIfAllowed(pkg);
|
||||
}
|
||||
//5、接口清单本次既没要求禁用也没要求显示的应用,恢复本地默认禁用
|
||||
// (例如后台删除了某应用的管控记录,它应回到机型的默认禁用状态)
|
||||
for (String pkg : ApkUtils.getEffectiveDefaultDisableApp(mContext)) {
|
||||
if (hidePkgs.contains(pkg) || showPkgs.contains(pkg) || ApkUtils.isDisable(mContext, pkg)) {
|
||||
continue;
|
||||
}
|
||||
Log.e("getIconControl", "applyIconControl: 恢复默认禁用: " + pkg);
|
||||
JgyUtils.getInstance().hideApp(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示应用(被其他管控来源要求禁用的除外)
|
||||
*
|
||||
* @param pkg 包名
|
||||
*/
|
||||
private void showAppIfAllowed(String pkg) {
|
||||
if (ApkUtils.isKeepDisableByOtherSource(mContext, pkg, ApkUtils.SOURCE_DESKTOP_HIDE)) {
|
||||
Log.e("getIconControl", "showAppIfAllowed: 被其他管控来源禁用,保持禁用: " + pkg);
|
||||
return;
|
||||
}
|
||||
Log.e("getIconControl", "showAppIfAllowed: show: " + pkg);
|
||||
JgyUtils.getInstance().showApp(pkg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 展开包名映射:不同机型的同功能应用包名不同,禁用时需一起处理
|
||||
*
|
||||
* @param pkgs 接口下发的包名
|
||||
* @return 实际需要禁用的包名
|
||||
*/
|
||||
private Set<String> expandAlias(List<String> pkgs) {
|
||||
Set<String> hidePkgs = new HashSet<>();
|
||||
for (String pack : pkgs) {
|
||||
if (TextUtils.isEmpty(pack)) {
|
||||
continue;
|
||||
}
|
||||
hidePkgs.add(pack);
|
||||
if ("com.mediatek.camera".equals(pack)) {
|
||||
hidePkgs.add("com.android.camera2");
|
||||
}
|
||||
if ("com.android.mms".equals(pack)) {
|
||||
hidePkgs.add("com.android.messaging");
|
||||
}
|
||||
}
|
||||
return hidePkgs;
|
||||
}
|
||||
|
||||
public void getIconControl(BehaviorSubject<ActivityEvent> lifecycle, onCompleteCallback callback) {
|
||||
getIconControlObservable()
|
||||
.observeOn(Schedulers.io())
|
||||
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
|
||||
.subscribe(getDesktopIconObserver(callback));
|
||||
.subscribe(getIconControlObserver(callback));
|
||||
}
|
||||
|
||||
public void getDesktopIcon(onCompleteCallback callback) {
|
||||
getDesktopIconObservable()
|
||||
public void getIconControl(BehaviorSubject<ActivityEvent> lifecycle) {
|
||||
getIconControlObservable()
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(getDesktopIconObserver(callback));
|
||||
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
|
||||
.subscribe(getIconControlObserver(null));
|
||||
}
|
||||
|
||||
public void getDesktopIcon() {
|
||||
getDesktopIconObservable()
|
||||
public void getIconControl() {
|
||||
getIconControlObservable()
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(getDesktopIconObserver(null));
|
||||
.subscribe(getIconControlObserver(null));
|
||||
}
|
||||
|
||||
public Observer<BaseResponse> getDesktopIconObserver(onCompleteCallback callback) {
|
||||
return new Observer<BaseResponse>() {
|
||||
public Observer<BaseResponse<List<IconControlInfo>>> getIconControlObserver(onCompleteCallback callback) {
|
||||
return new Observer<BaseResponse<List<IconControlInfo>>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getDesktopIcon", "onSubscribe: ");
|
||||
Log.e("getIconControl", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse response) {
|
||||
Log.e("getDesktopIcon", "onNext: " + response);
|
||||
public void onNext(BaseResponse<List<IconControlInfo>> listBaseResponse) {
|
||||
Log.e("getIconControl", "onNext: " + listBaseResponse);
|
||||
//先把不在管控清单里的应用恢复显示
|
||||
ApkUtils.showAllApp(mContext);
|
||||
if (response.isSuccess()) {
|
||||
String data = response.data.toString();
|
||||
if (!TextUtils.isEmpty(data)) {
|
||||
List<String> newList = Arrays.asList(data.split(","));//新的list
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
for (String pack : newList) {
|
||||
try {
|
||||
if ("com.mediatek.camera".equals(pack)) {
|
||||
JgyUtils.getInstance().hideApp("com.android.camera2");
|
||||
List<String> disablePkgs = new ArrayList<>();
|
||||
List<String> showPkgs = new ArrayList<>();
|
||||
if (listBaseResponse.isSuccess() && listBaseResponse.data != null) {
|
||||
List<IconControlInfo> iconControlInfoList = listBaseResponse.data;
|
||||
disablePkgs = iconControlInfoList.stream()
|
||||
.filter(new Predicate<IconControlInfo>() {
|
||||
@Override
|
||||
public boolean test(IconControlInfo iconControlInfo) {
|
||||
return iconControlInfo.getControl_type() == 0;
|
||||
}
|
||||
if ("com.android.mms".equals(pack)) {
|
||||
JgyUtils.getInstance().hideApp("com.android.messaging");
|
||||
})
|
||||
.map(new Function<IconControlInfo, String>() {
|
||||
@Override
|
||||
public String apply(IconControlInfo iconControlInfo) {
|
||||
return iconControlInfo.getApp_package();
|
||||
}
|
||||
JgyUtils.getInstance().hideApp(pack);
|
||||
Log.e("getDesktopIcon", pack);
|
||||
} catch (Exception e) {
|
||||
Log.e("getDesktopIcon", "Exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
showPkgs = iconControlInfoList.stream()
|
||||
.filter(new Predicate<IconControlInfo>() {
|
||||
@Override
|
||||
public boolean test(IconControlInfo iconControlInfo) {
|
||||
return iconControlInfo.getControl_type() == 1;
|
||||
}
|
||||
})
|
||||
.map(new Function<IconControlInfo, String>() {
|
||||
@Override
|
||||
public String apply(IconControlInfo iconControlInfo) {
|
||||
return iconControlInfo.getApp_package();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
Log.e("getIconControl", "onNext: disablePkgs = " + disablePkgs + ", showPkgs = " + showPkgs);
|
||||
//清单为空(接口失败或没有数据)时同样按"接口清单为空"处理:
|
||||
//上次接口禁用的应用恢复显示,未被接口提及的应用恢复为本地默认禁用
|
||||
applyIconControl(disablePkgs, showPkgs);
|
||||
if (JgyUtils.getInstance().isAiuduPlatform()) {
|
||||
ApkUtils.HideAiUDuApp();
|
||||
}
|
||||
@@ -1486,13 +1600,17 @@ public class NetInterfaceManager {
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getDesktopIcon", "onError: " + e.getMessage());
|
||||
onComplete();
|
||||
Log.e("getIconControl", "onError: " + e.getMessage());
|
||||
//接口异常同样视为没有数据,禁用本机默认禁用的应用
|
||||
ApkUtils.hideDefaultDisableApp(mContext);
|
||||
if (callback != null) {
|
||||
callback.onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getDesktopIcon", "onComplete: ");
|
||||
Log.e("getIconControl", "onComplete: ");
|
||||
if (callback != null) {
|
||||
callback.onComplete();
|
||||
}
|
||||
@@ -1618,18 +1736,19 @@ public class NetInterfaceManager {
|
||||
}
|
||||
|
||||
private void setAppDisable(List<AppAttr> appAttrList) {
|
||||
String appString = Settings.Global.getString(mContext.getContentResolver(), CommonConfig.AOLE_ACTION_DISABLE_APP);
|
||||
if (!TextUtils.isEmpty(appString)) {
|
||||
List<String> pkgs = new ArrayList<>(Arrays.asList(appString.split(",")));
|
||||
for (String pkg : pkgs) {
|
||||
JgyUtils.getInstance().showApp(pkg);
|
||||
}
|
||||
}
|
||||
//注意:这里只处理 getSnAppAttr 接口下发的禁用清单(AOLE_ACTION_DISABLE_APP),
|
||||
//与 getIconControl 接口下发的禁用清单(AOLE_ACTION_HIDE_DESKTOP_ICON)是两套独立数据
|
||||
List<String> oldPkgs = ApkUtils.parsePkgList(
|
||||
Settings.Global.getString(mContext.getContentResolver(), CommonConfig.AOLE_ACTION_DISABLE_APP));
|
||||
|
||||
if (appAttrList == null || appAttrList.size() == 0) {
|
||||
//接口没有数据(失败或为空):只放开 AOLE_ACTION_DISABLE_APP 里记录的、
|
||||
//且没有被其他管控来源要求禁用的应用
|
||||
Log.e("getSnAppAttr", "onNext: appAttrList is empty");
|
||||
showAppWithoutOtherSource(oldPkgs);
|
||||
Settings.Global.putString(mContext.getContentResolver(), CommonConfig.AOLE_ACTION_DISABLE_APP, "null");
|
||||
} else {
|
||||
showAppWithoutOtherSource(oldPkgs);
|
||||
for (AppAttr appAttr : appAttrList) {
|
||||
if (appAttr.getIs_open() == 0) {
|
||||
JgyUtils.getInstance().hideApp(appAttr.getApp_package());
|
||||
@@ -1644,6 +1763,25 @@ public class NetInterfaceManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 放开在线禁用应用。
|
||||
* <p>
|
||||
* 只放开在线禁用清单(AOLE_ACTION_DISABLE_APP)中的应用,若该应用同时被其他管控来源
|
||||
* (本地默认禁用、桌面图标隐藏、爱优读隐藏等)要求禁用,则保持禁用不做放开。
|
||||
*
|
||||
* @param pkgs 旧的在线的禁用包名
|
||||
*/
|
||||
private void showAppWithoutOtherSource(List<String> pkgs) {
|
||||
for (String pkg : pkgs) {
|
||||
if (ApkUtils.isKeepDisableByOtherSource(mContext, pkg, ApkUtils.SOURCE_ONLINE_DISABLE)) {
|
||||
Log.e("getSnAppAttr", "showApp: 被其他管控来源禁用,保持禁用: " + pkg);
|
||||
continue;
|
||||
}
|
||||
Log.e("getSnAppAttr", "showApp: " + pkg);
|
||||
JgyUtils.getInstance().showApp(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSystemSetting(BehaviorSubject<ActivityEvent> lifecycle, onCompleteCallback callback) {
|
||||
getSystemSettingObservable()
|
||||
.observeOn(Schedulers.io())
|
||||
@@ -1674,7 +1812,7 @@ public class NetInterfaceManager {
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse baseResponse) {
|
||||
Log.e("setSystemSetting", "onNext: " + baseResponse);
|
||||
SPUtils.put(mContext, CommonConfig.JGY_FIRST_CONNECT, 1);
|
||||
mMMKV.encode(CommonConfig.JGY_FIRST_CONNECT, 1);
|
||||
if (baseResponse.isSuccess()) {
|
||||
String data = GsonUtils.toJSONString(baseResponse.data);
|
||||
Log.e("setSystemSetting", "onNext: " + data);
|
||||
@@ -1723,7 +1861,7 @@ public class NetInterfaceManager {
|
||||
Settings.System.putString(mContext.getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, eBagCode.getEbagCode());
|
||||
}
|
||||
String ebagCode = eBagCode.getEbagCode();
|
||||
SPUtils.put(mContext, "ebagCode", ebagCode);
|
||||
mMMKV.encode("ebagCode", ebagCode);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), CommonConfig.UIUI_ACTIVATION_KEY, 1);
|
||||
JgyUtils.getInstance().checkLocalExternalFiles();
|
||||
} else {
|
||||
@@ -1864,11 +2002,11 @@ public class NetInterfaceManager {
|
||||
private void setDefaultDesktop(String defaultApp) {
|
||||
if (!TextUtils.isEmpty(defaultApp)) {
|
||||
JgyUtils.getInstance().setDefaultDesktop(defaultApp);
|
||||
// SPUtils.put(mContext, "default_launcher", defaultApp);
|
||||
// mMMKV.encode(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, defaultApp);
|
||||
} else {
|
||||
//如果默认桌面不为空则设置为默认桌面
|
||||
String default_launcher = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
|
||||
// String default_launcher = (String) SPUtils.get(mContext, "default_launcher", "");
|
||||
// String default_launcher = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
|
||||
Log.e("getDefaultApp", "onNext: " + default_launcher);
|
||||
if (TextUtils.isEmpty(default_launcher)) {
|
||||
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.YXPD1Platform) {
|
||||
@@ -1963,7 +2101,7 @@ public class NetInterfaceManager {
|
||||
// int oldState = Settings.System.getInt(mContext.getContentResolver(), JGYActions.ACTION_QCH_UNLOCK_IPAD, JGYActions.FRAME_CODE_LOCKED);
|
||||
// Log.e( "getLockedState", "qch_unlock_ipad: " + oldState);
|
||||
//后台1是锁定,底层0是锁定
|
||||
SPUtils.put(mContext, CommonConfig.JGY_FIRST_CONNECT, 1);
|
||||
mMMKV.encode(CommonConfig.JGY_FIRST_CONNECT, 1);
|
||||
if (locked == JGYActions.NET_CODE_LOCKED) {
|
||||
// if (oldState == JGYActions.FRAME_CODE_UNLOCKED) {
|
||||
// Log.e( "getLockedState", "onNext: " + "state changed , reset devices");
|
||||
@@ -2764,21 +2902,21 @@ public class NetInterfaceManager {
|
||||
}
|
||||
String white_ip = browserData.getWhite_ip();
|
||||
if (!TextUtils.isEmpty(white_ip)) {
|
||||
SPUtils.put(mContext, "white_ip", white_ip);
|
||||
mMMKV.encode("white_ip", white_ip);
|
||||
} else {
|
||||
SPUtils.put(mContext, "white_ip", " ");
|
||||
mMMKV.encode("white_ip", " ");
|
||||
}
|
||||
String black_ip = browserData.getBlack_ip();
|
||||
if (!TextUtils.isEmpty(black_ip)) {
|
||||
SPUtils.put(mContext, "black_ip", black_ip);
|
||||
mMMKV.encode("black_ip", black_ip);
|
||||
} else {
|
||||
SPUtils.put(mContext, "black_ip", " ");
|
||||
mMMKV.encode("black_ip", " ");
|
||||
}
|
||||
} else {
|
||||
Settings.System.putString(mContext.getContentResolver(), "DeselectBrowserArray", homePage);
|
||||
Settings.System.putString(mContext.getContentResolver(), "qch_webblack_url", " ");
|
||||
SPUtils.put(mContext, "white_ip", " ");
|
||||
SPUtils.put(mContext, "black_ip", " ");
|
||||
mMMKV.encode("white_ip", " ");
|
||||
mMMKV.encode("black_ip", " ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3714,15 +3852,14 @@ public class NetInterfaceManager {
|
||||
Intent intent = new Intent(LogcatService.LOGCAT_START_ACTION);
|
||||
PendingIntent startPendingIntent = PendingIntent.getBroadcast(mContext,
|
||||
REQUEST_CODE, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
|
||||
timestamp, startPendingIntent);
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
setExactAlarm(alarmManager, timestamp, startPendingIntent);
|
||||
}
|
||||
Intent intent = new Intent(LogcatService.LOGCAT_STOP_ACTION);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
|
||||
REQUEST_CODE, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
setExactAlarm(alarmManager,
|
||||
timestamp + snRunLog.getDuration() * 1000, pendingIntent);
|
||||
}
|
||||
}
|
||||
@@ -3740,6 +3877,23 @@ public class NetInterfaceManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置精确闹钟。
|
||||
* <p>Android 14(targetSdk 33+)起 SCHEDULE_EXACT_ALARM 默认被拒绝,
|
||||
* 权限不可用时会抛 SecurityException,这里降级为非精确闹钟,保证日志上传策略依然生效。
|
||||
*/
|
||||
private static void setExactAlarm(AlarmManager alarmManager, long triggerAtMillis, PendingIntent pendingIntent) {
|
||||
if (alarmManager == null || pendingIntent == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "setExactAlarm: 精确闹钟不可用,降级为非精确闹钟: " + e.getMessage());
|
||||
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String PublicIP = "PublicIP";
|
||||
|
||||
public static final String WHOIS_ADDR = "whois_ip_addr";
|
||||
|
||||
@@ -110,7 +110,10 @@ public class UrlAddress {
|
||||
/*小程序的管控设置*/
|
||||
public static final String GET_SN_SETTING = "And/control/getSnSetting";
|
||||
/*获取隐藏桌面图标*/
|
||||
@Deprecated
|
||||
public static final String GET_HIDE_DESKTOPICON = "Icon";
|
||||
/*获取隐藏桌面图标 新版*/
|
||||
public static final String GET_ICON_CONTROL = "icon/control";
|
||||
/*获取系统设置*/
|
||||
public static final String GET_FIRMWARE = "firmware/get";
|
||||
/*获取时间管控*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.aoleyun.sn.bean.BaseResponse;
|
||||
import com.aoleyun.sn.bean.BlacklistBean;
|
||||
import com.aoleyun.sn.bean.BrowserBookmarks;
|
||||
import com.aoleyun.sn.bean.BrowserData;
|
||||
import com.aoleyun.sn.bean.IconControlInfo;
|
||||
import com.aoleyun.sn.bean.PoweroffBean;
|
||||
import com.aoleyun.sn.bean.SnSetting;
|
||||
import com.aoleyun.sn.bean.SnTimeControl;
|
||||
@@ -42,11 +43,18 @@ public interface SystemSettingApi {
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(UrlAddress.GET_HIDE_DESKTOPICON)
|
||||
@Deprecated
|
||||
Observable<BaseResponse> getDesktopIcon(
|
||||
@Field("key") String key,
|
||||
@Field("sn") String sn
|
||||
);
|
||||
|
||||
@GET(UrlAddress.GET_ICON_CONTROL)
|
||||
Observable<BaseResponse<List<IconControlInfo>>> getIconControl(
|
||||
@Query("key") String key,
|
||||
@Query("sn") String sn
|
||||
);
|
||||
|
||||
@GET(UrlAddress.GET_SN_TIME_CONTROL)
|
||||
@Deprecated
|
||||
Observable<BaseResponse<SnTimeControl>> getSnTimeControl(
|
||||
|
||||
@@ -51,7 +51,7 @@ import com.aoleyun.sn.utils.CmdUtil;
|
||||
import com.aoleyun.sn.utils.ForegroundAppUtil;
|
||||
import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.MySQLData;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.utils.ReceiverCompat;
|
||||
import com.aoleyun.sn.utils.ServiceAliveUtils;
|
||||
import com.aoleyun.sn.utils.SysSettingUtils;
|
||||
import com.aoleyun.sn.utils.TimeUtils;
|
||||
@@ -336,7 +336,7 @@ public class PushManager {
|
||||
@Override
|
||||
public void run() {
|
||||
NetInterfaceManager.getInstance().getAppLimit();
|
||||
NetInterfaceManager.getInstance().getDesktopIcon();
|
||||
NetInterfaceManager.getInstance().getIconControl();
|
||||
NetInterfaceManager.getInstance().getDefaultDesktop();
|
||||
NetInterfaceManager.getInstance().getAppAutoStartUpdateAndNet();
|
||||
}
|
||||
@@ -378,7 +378,7 @@ public class PushManager {
|
||||
@Override
|
||||
public void run() {
|
||||
NetInterfaceManager.getInstance().getAppLimit();
|
||||
NetInterfaceManager.getInstance().getDesktopIcon();
|
||||
NetInterfaceManager.getInstance().getIconControl();
|
||||
NetInterfaceManager.getInstance().getDefaultDesktop();
|
||||
NetInterfaceManager.getInstance().getAppAutoStartUpdateAndNet();
|
||||
}
|
||||
@@ -421,7 +421,7 @@ public class PushManager {
|
||||
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||
//后台发送时数据库未改变,有时候可能获取到的数据时上一次的
|
||||
Log.e(TAG, "run: HIDE_DESKTOP_ICON ");
|
||||
NetInterfaceManager.getInstance().getDesktopIcon();
|
||||
NetInterfaceManager.getInstance().getIconControl();
|
||||
}, 2000);
|
||||
break;
|
||||
case DISABLE_APP_SLIDE:
|
||||
@@ -457,28 +457,28 @@ public class PushManager {
|
||||
case BDLocation.TypeOffLineLocation:// 离线定位结果
|
||||
Log.e(TAG, "onLocationChanged: " + "定位成功");
|
||||
Log.e(TAG, "onLocationChanged: " + location.getAddrStr() + location.getLocationDescribe());
|
||||
SPUtils.put(mContext, "MapAddress", location.getAddrStr() + location.getLocationDescribe());
|
||||
SPUtils.put(mContext, "longitude", location.getLongitude());
|
||||
SPUtils.put(mContext, "latitude", location.getLatitude());
|
||||
SPUtils.put(mContext, "MapError", "-");
|
||||
mMMKV.encode("MapAddress", location.getAddrStr() + location.getLocationDescribe());
|
||||
mMMKV.encode("longitude", String.valueOf(location.getLongitude()));
|
||||
mMMKV.encode("latitude", String.valueOf(location.getLatitude()));
|
||||
mMMKV.encode("MapError", "-");
|
||||
break;
|
||||
case BDLocation.TypeServerError:
|
||||
Log.e(TAG, "onReceiveLocation: " + "服务端网络定位失败");
|
||||
SPUtils.put(mContext, "MapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");
|
||||
mMMKV.encode("MapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");
|
||||
break;
|
||||
case BDLocation.TypeNetWorkException:
|
||||
Log.e(TAG, "onReceiveLocation: " + "网络不同导致定位失败,请检查网络是否通畅");
|
||||
SPUtils.put(mContext, "MapError", "网络不同导致定位失败,请检查网络是否通畅");
|
||||
mMMKV.encode("MapError", "网络不同导致定位失败,请检查网络是否通畅");
|
||||
break;
|
||||
case BDLocation.TypeCriteriaException:
|
||||
Log.e(TAG, "onReceiveLocation: " + "无法获取有效定位依据导致定位失败");
|
||||
SPUtils.put(mContext, "MapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
|
||||
mMMKV.encode("MapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "MapAddress: " + SPUtils.get(mContext, "MapAddress", "-"));
|
||||
Log.e(TAG, "MapError: " + SPUtils.get(mContext, "MapError", "-"));
|
||||
Log.e(TAG, "MapAddress: " + mMMKV.decodeString("MapAddress", "-"));
|
||||
Log.e(TAG, "MapError: " + mMMKV.decodeString("MapError", "-"));
|
||||
locationClient.stop();
|
||||
}
|
||||
|
||||
@@ -524,7 +524,7 @@ public class PushManager {
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "onLocationChanged: " + "定位失败");
|
||||
SPUtils.put(mContext, "MapError", sb);
|
||||
mMMKV.encode("MapError", sb.toString());
|
||||
Log.e(TAG, "onLocDiagnosticMessage: " + sb);
|
||||
}
|
||||
});
|
||||
@@ -783,7 +783,7 @@ public class PushManager {
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NetInterfaceManager.getInstance().getDesktopIcon();
|
||||
NetInterfaceManager.getInstance().getIconControl();
|
||||
NetInterfaceManager.getInstance().getDefaultDesktop();
|
||||
NetInterfaceManager.getInstance().getAppAutoStartUpdateAndNet();
|
||||
}
|
||||
@@ -890,7 +890,8 @@ public class PushManager {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
return ((BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE)).getIntProperty(4);
|
||||
}
|
||||
Intent intent = (new ContextWrapper(mContext)).registerReceiver(null, new IntentFilter("android.intent.action.BATTERY_CHANGED"));
|
||||
Intent intent = ReceiverCompat.registerReceiver(new ContextWrapper(mContext), null,
|
||||
new IntentFilter("android.intent.action.BATTERY_CHANGED"), ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
return intent.getIntExtra("level", -1) * 100 / intent.getIntExtra("scale", -1);
|
||||
}
|
||||
|
||||
@@ -1390,13 +1391,13 @@ public class PushManager {
|
||||
String ebagCode = jsonObject.get("ebagCode").getAsString();
|
||||
if (TextUtils.isEmpty(ebagCode)) {
|
||||
Log.e(TAG, "setEbagCode: " + "ebagCode is empty");
|
||||
SPUtils.put(mContext, "ebagCode", "");
|
||||
mMMKV.encode("ebagCode", "");
|
||||
if (JgyUtils.getInstance().checkAppPlatform() != JgyUtils.YXPD1Platform) {
|
||||
Settings.System.putString(mContext.getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, "");
|
||||
Settings.Global.putInt(mContext.getContentResolver(), CommonConfig.UIUI_ACTIVATION_KEY, 0);
|
||||
}
|
||||
} else {
|
||||
SPUtils.put(mContext, "ebagCode", ebagCode);
|
||||
mMMKV.encode("ebagCode", ebagCode);
|
||||
if (JgyUtils.getInstance().checkAppPlatform() != JgyUtils.YXPD1Platform) {
|
||||
Settings.System.putString(mContext.getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, ebagCode);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), CommonConfig.UIUI_ACTIVATION_KEY, 1);
|
||||
@@ -1483,13 +1484,13 @@ public class PushManager {
|
||||
if (is_log_type == 0) {
|
||||
NetInterfaceManager.getInstance().uploadLogFile();
|
||||
} else {
|
||||
SPUtils.put(mContext, CommonConfig.UPLOAD_LOG_TYPE, 1);
|
||||
mMMKV.encode(CommonConfig.UPLOAD_LOG_TYPE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void getBatteryInfo(Context context) {
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
||||
Intent receiver = context.registerReceiver(null, filter);
|
||||
Intent receiver = ReceiverCompat.registerReceiver(context, null, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
String jsonString = BatteryUtils.getBatteryJson(context, receiver).toString();
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<BatteryInfo>() {
|
||||
|
||||
@@ -11,7 +11,6 @@ import com.aoleyun.sn.rlog.LogDBManager;
|
||||
import com.aoleyun.sn.utils.BootManager;
|
||||
import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.PowerOffAlarmUtils;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.utils.TimeUtils;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
@@ -80,9 +79,9 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
mMMKV.encode(ConnectManager.REBOOT_LAST_ONNECT_TIME, System.currentTimeMillis());
|
||||
//设备重启后 AlarmManager 中的闹钟会被系统清空,重新安排定时关机
|
||||
PowerOffAlarmUtils.schedule(context);
|
||||
if ((int) SPUtils.get(context, CommonConfig.FIRST_STARTUP, 0) == 0) {
|
||||
if (mMMKV.decodeInt(CommonConfig.FIRST_STARTUP, 0) == 0) {
|
||||
LogDBManager.getInstance().creatRebootLog("首次启动", TimeUtils.transferLongToDate(System.currentTimeMillis()));
|
||||
SPUtils.put(context, CommonConfig.FIRST_STARTUP, 1);
|
||||
mMMKV.encode(CommonConfig.FIRST_STARTUP, 1);
|
||||
} else {
|
||||
LogDBManager.getInstance().creatRebootLog("设备重启", TimeUtils.transferLongToDate(System.currentTimeMillis()));
|
||||
// Intent checkNetintent = new Intent(context, CheckNetActivity.class);
|
||||
|
||||
@@ -119,7 +119,7 @@ public class NewAppReceiver extends BroadcastReceiver {
|
||||
NetInterfaceManager.getInstance().getAppLimit();
|
||||
// NetInterfaceManager.getInstance().getDefaultDesktop();
|
||||
NetInterfaceManager.getInstance().getAppAutoStartUpdateAndNet();
|
||||
NetInterfaceManager.getInstance().getDesktopIcon();
|
||||
NetInterfaceManager.getInstance().getIconControl();
|
||||
NetInterfaceManager.getInstance().sendInstalledAppInfo(new NetInterfaceManager.onCompleteCallback() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
@@ -161,7 +161,7 @@ public class NewAppReceiver extends BroadcastReceiver {
|
||||
Log.e("setLauncher", "onNext: " + pkg);
|
||||
JgyUtils.getInstance().checkDefaultDesktop(pkg);
|
||||
String oldDesktop = JgyUtils.getInstance().getDefaultDesktop();
|
||||
// String oldDesktop = (String) SPUtils.get(mContext, "default_launcher", "");
|
||||
// String oldDesktop = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
|
||||
Log.e("setLauncher", "onNext: oldDesktop = " + oldDesktop);
|
||||
if (pkg.equals(oldDesktop)) {
|
||||
ApkUtils.openPackage(mContext, pkg);
|
||||
|
||||
@@ -59,7 +59,7 @@ import com.aoleyun.sn.service.main.MainService;
|
||||
import com.aoleyun.sn.utils.ApkUtils;
|
||||
import com.aoleyun.sn.utils.CmdUtil;
|
||||
import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.utils.ReceiverCompat;
|
||||
import com.aoleyun.sn.utils.ServiceAliveUtils;
|
||||
import com.aoleyun.sn.utils.TimeUtils;
|
||||
import com.aoleyun.sn.utils.Utils;
|
||||
@@ -72,6 +72,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
@@ -101,6 +102,8 @@ import okhttp3.ResponseBody;
|
||||
public class GuardService extends Service {
|
||||
private final static String TAG = "GuardService";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
||||
@@ -234,13 +237,13 @@ public class GuardService extends Service {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (null != mNewAppReceiver) {
|
||||
unregisterReceiver(mNewAppReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mNewAppReceiver);
|
||||
}
|
||||
if (null != presentReceiver) {
|
||||
unregisterReceiver(presentReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, presentReceiver);
|
||||
}
|
||||
if (null != batteryReceiver) {
|
||||
unregisterReceiver(batteryReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, batteryReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +258,8 @@ public class GuardService extends Service {
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
// filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
filter.addDataScheme("package");
|
||||
registerReceiver(mNewAppReceiver, filter);
|
||||
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
|
||||
ReceiverCompat.registerReceiver(this, mNewAppReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
private PresentReceiver presentReceiver;
|
||||
@@ -275,7 +279,7 @@ public class GuardService extends Service {
|
||||
filter.addAction("android.intent.action.FORCE_FACTORY_RESET");
|
||||
filter.addAction(Intent.ACTION_SHUTDOWN);
|
||||
filter.addAction(Intent.ACTION_REBOOT);
|
||||
registerReceiver(presentReceiver, filter);
|
||||
ReceiverCompat.registerReceiver(this, presentReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
public class PresentReceiver extends BroadcastReceiver {
|
||||
@@ -338,8 +342,8 @@ public class GuardService extends Service {
|
||||
break;
|
||||
case Intent.ACTION_SHUTDOWN:
|
||||
case Intent.ACTION_REBOOT:
|
||||
int restart_count = (int) SPUtils.get(context, "restart_count", 0);
|
||||
SPUtils.put(context, "restart_count", restart_count + 1);
|
||||
int restart_count = mMMKV.decodeInt("restart_count", 0);
|
||||
mMMKV.encode("restart_count", restart_count + 1);
|
||||
// android.os.Process.killProcess(android.os.Process.myPid());
|
||||
break;
|
||||
case Intent.ACTION_FACTORY_RESET:
|
||||
@@ -435,7 +439,7 @@ public class GuardService extends Service {
|
||||
filter.addAction(Intent.ACTION_BATTERY_OKAY);
|
||||
filter.addAction(Intent.ACTION_POWER_CONNECTED);
|
||||
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
|
||||
registerReceiver(batteryReceiver, filter);
|
||||
ReceiverCompat.registerReceiver(this, batteryReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
BatteryReceiver batteryReceiver;
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.util.Log;
|
||||
|
||||
import com.aoleyun.sn.bean.BaseResponse;
|
||||
import com.aoleyun.sn.network.NetInterfaceManager;
|
||||
import com.aoleyun.sn.utils.ReceiverCompat;
|
||||
import com.aoleyun.sn.utils.Utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -106,7 +107,8 @@ public class LogcatService extends Service {
|
||||
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||
filter.addAction(LOGCAT_START_ACTION);
|
||||
filter.addAction(LOGCAT_STOP_ACTION);
|
||||
registerReceiver(receiver, filter);
|
||||
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
|
||||
ReceiverCompat.registerReceiver(this, receiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
Thread thread = null;
|
||||
@@ -334,7 +336,7 @@ public class LogcatService extends Service {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (receiver != null) {
|
||||
unregisterReceiver(receiver);
|
||||
ReceiverCompat.unregisterReceiver(this, receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class RemoteService extends Service {
|
||||
// @Override
|
||||
// public void setDefaultDesktop(String pkg) throws RemoteException {
|
||||
// Log.e(TAG, "setDefaultDesktop: " + "pkg = " + pkg);
|
||||
//// SPUtils.put(RemoteService.this, "default_launcher", pkg);
|
||||
//// mMMKV.encode(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, pkg);
|
||||
// mMMKV.encode(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, pkg);
|
||||
// JgyUtils.getInstance().setDefaultDesktop(pkg);
|
||||
// if (PackageNames.DESKTOP.equals(pkg)) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.annotation.Nullable;
|
||||
import com.aoleyun.sn.BuildConfig;
|
||||
import com.aoleyun.sn.KeepAliveConnection;
|
||||
import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.ReceiverCompat;
|
||||
import com.aoleyun.sn.utils.ServiceAliveUtils;
|
||||
import com.aoleyun.sn.utils.Utils;
|
||||
import com.blankj.utilcode.util.NetworkUtils;
|
||||
@@ -123,7 +124,8 @@ public class StepService extends Service implements NetworkUtils.OnNetworkStatus
|
||||
filter.addAction(Intent.ACTION_REBOOT);
|
||||
filter.addAction(Intent.ACTION_FACTORY_RESET);
|
||||
filter.addAction(Intent.ACTION_MASTER_CLEAR);
|
||||
registerReceiver(screenLockReceiver, filter);
|
||||
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
|
||||
ReceiverCompat.registerReceiver(this, screenLockReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
private class ScreenLockReceiver extends BroadcastReceiver {
|
||||
@@ -163,7 +165,7 @@ public class StepService extends Service implements NetworkUtils.OnNetworkStatus
|
||||
NetworkUtils.unregisterNetworkStatusChangedListener(this);
|
||||
closeConnect();
|
||||
if (screenLockReceiver != null) {
|
||||
unregisterReceiver(screenLockReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, screenLockReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ import com.aoleyun.sn.utils.CacheUtils;
|
||||
import com.aoleyun.sn.utils.ForegroundAppUtil;
|
||||
import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.PowerOffAlarmUtils;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.utils.ReceiverCompat;
|
||||
import com.aoleyun.sn.utils.SysSettingUtils;
|
||||
import com.aoleyun.sn.utils.TimeUtils;
|
||||
import com.aoleyun.sn.utils.Utils;
|
||||
@@ -74,6 +74,8 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
@@ -310,35 +312,22 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
return mMainBinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化任务后台线程(单线程串行,保证初始化顺序)
|
||||
*/
|
||||
private static final ExecutorService sInitExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
//前台服务必须在 startForegroundService() 之后 5 秒内调用 startForeground(),
|
||||
//放在 onCreate 最前面执行,避免后续初始化耗时或抛异常导致前台服务超时(ANR)
|
||||
startForegroundIfNeeded();
|
||||
Log.e(TAG, "onCreate: ");
|
||||
mServiceModel = new MainServiceModel(this);
|
||||
mServiceModel.setLifecycle(getLifecycleSubject());
|
||||
initLiveData();
|
||||
|
||||
if ("YX-T01".equalsIgnoreCase(Build.MODEL)) {
|
||||
JgyUtils.getInstance().setTongyiAppTop();
|
||||
}
|
||||
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.YXPD1Platform) {
|
||||
Settings.System.putString(getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, Utils.getSerial(this));
|
||||
if (ApkUtils.isAvailable(this, JgyUtils.JXW_LAUNCHER_PACKAGE_NAME)) {
|
||||
Intent intent2 = new Intent(Intent.ACTION_VIEW);
|
||||
intent2.setPackage(JgyUtils.JXW_LAUNCHER_PACKAGE_NAME);
|
||||
intent2.setClassName(JgyUtils.JXW_LAUNCHER_PACKAGE_NAME, JgyUtils.JXW_LAUNCHER_CLASS_NAME);
|
||||
try {
|
||||
startService(intent2);//或者bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 都能启动
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onCreate: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String launcherPkg = JgyUtils.getInstance().getDefaultDesktop();
|
||||
Log.e(TAG, "onCreate: " + launcherPkg);
|
||||
JgyUtils.getInstance().checkDefaultDesktop(launcherPkg);
|
||||
|
||||
TimeTask task = new TimeTask();
|
||||
task.execute("ntp.aliyun.com");
|
||||
|
||||
@@ -357,34 +346,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
Log.e(TAG, "onCreate: device not reboot");
|
||||
}
|
||||
|
||||
Settings.Global.putString(getContentResolver(), "AOLE_SERIAL", Utils.getSerial(MainService.this));
|
||||
|
||||
PackageManager pm = getPackageManager();
|
||||
try {
|
||||
Log.e(TAG, "initView: " + pm.getApplicationEnabledSetting("com.teclast.update"));
|
||||
pm.setApplicationEnabledSetting("com.teclast.update", PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0);
|
||||
Log.e(TAG, "onCreate: enable com.teclast.update");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onCreate: setApplicationEnabledSetting = " + e.getMessage());
|
||||
}
|
||||
if ("V1.01_20230519".equalsIgnoreCase(Build.DISPLAY)) {
|
||||
Log.e(TAG, "onCreate: enable");
|
||||
Intent intent = new Intent();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClassName("com.teclast.update", "com.teclast.update.GoogleOtaClient");
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onCreate: startActivity = " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (JgyUtils.getInstance().isAiuduPlatform()) {
|
||||
// if (mMMKV.decodeInt(CommonConfig.DEVICES_FRIST_START, 0) == 0) {
|
||||
// ApkUtils.openPackage(MainService.this, "com.books.wisdom");
|
||||
// }
|
||||
ApkUtils.HideAiUDuApp();
|
||||
}
|
||||
//Settings 写入、包管理查询与启停等耗时操作统一放到后台线程(见 initOnBackground)
|
||||
|
||||
|
||||
// RoleManager mRm = getSystemService(RoleManager.class);
|
||||
@@ -399,11 +361,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
// Log.e("RoleManager", "onRoleHoldersChanged: roleName = " + s + "userHandle = " + userHandle);
|
||||
// }
|
||||
// }, UserHandle.ALL);
|
||||
//根据要求卸载这两个应用
|
||||
if (JgyUtils.getInstance().getDeviceIsLocked()) {
|
||||
ApkUtils.UninstallAPP(this, "com.joytv.live");
|
||||
ApkUtils.UninstallAPP(this, "com.tencent.android.qqdownloader");
|
||||
}
|
||||
//根据要求卸载这两个应用(包管理耗时操作,移到后台线程执行,见 initOnBackground)
|
||||
// Log.e(TAG, "onCreate: " + Build.MODEL);
|
||||
// if ("K106".equals(Build.MODEL)) {
|
||||
// if (ApkUtils.isAvailable(MainService.this, JXW_LAUNCHER)) {
|
||||
@@ -411,12 +369,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
// }
|
||||
// }
|
||||
|
||||
try {
|
||||
JgyUtils.getInstance().hideApp("com.android.email");
|
||||
JgyUtils.getInstance().hideApp("com.android.browser");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onCreate: " + e.getMessage());
|
||||
}
|
||||
//隐藏系统邮件与浏览器(包管理耗时操作,移到后台线程执行,见 initOnBackground)
|
||||
|
||||
Observable.create(topAppSubscribe)
|
||||
.throttleFirst(1, TimeUnit.MINUTES)
|
||||
@@ -425,27 +378,12 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
.throttleFirst(3, TimeUnit.HOURS)
|
||||
.subscribe(timeChangedObserver);
|
||||
|
||||
if ((int) SPUtils.get(this, CommonConfig.JGY_FIRST_CONNECT, 0) == 0) {
|
||||
if (mMMKV.decodeInt(CommonConfig.JGY_FIRST_CONNECT, 0) == 0) {
|
||||
Log.e(TAG, "onCreate: " + "Disable All Settings");
|
||||
SysSettingUtils.setDisableSetting(this);
|
||||
// mPresenter.getLockedState(CommonConfig.JGY_FIRST_CONNECT);
|
||||
}
|
||||
int oldVersionCode = (int) SPUtils.get(this, CommonConfig.SN_VERSIONCODE, 0);
|
||||
if (oldVersionCode < BuildConfig.VERSION_CODE) {
|
||||
Log.e(TAG, "onCreate: " + "upgrade successful");
|
||||
try {
|
||||
// new CacheUtils().cleanApplicationUserData(this, "com.aoleyun.os");
|
||||
new CacheUtils().cleanApplicationUserData(this, "com.aoleyun.audos");
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onReceive: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
// mPresenter.checkAoleyunUpdate();
|
||||
} else {
|
||||
|
||||
}
|
||||
SPUtils.put(this, CommonConfig.SN_VERSIONCODE, BuildConfig.VERSION_CODE);
|
||||
//版本升级后的用户数据清理为耗时 IO,移到后台线程执行(见 initOnBackground)
|
||||
|
||||
//根据本地保存的策略安排定时关机闹钟(服务重启、设备重启后重新计算)
|
||||
PowerOffAlarmUtils.schedule(this);
|
||||
@@ -454,25 +392,10 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
timeChangedStart.onstar(System.currentTimeMillis());
|
||||
setStatusbar();
|
||||
setFloatingWindow();
|
||||
JgyUtils.getInstance().writeAppPackageList();
|
||||
|
||||
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.HL500Platform) {
|
||||
mNotificationManagerCompat = NotificationManagerCompat.from(this);
|
||||
createNotificationChannel();
|
||||
sendSimpleNotification();
|
||||
}
|
||||
|
||||
// aliyunPushInit();
|
||||
|
||||
IActivityManager activityManager = ActivityManagerNative.getDefault();
|
||||
try {
|
||||
activityManager.setActivityController(new AoleyunActivityController(), false);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "setActivityController: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
mMMKV.encode(CommonConfig.DEVICES_FRIST_START, 1);
|
||||
Observable.create(killSubscribe)
|
||||
.throttleLast(30, TimeUnit.SECONDS)
|
||||
// .throttleLast(3, TimeUnit.SECONDS)
|
||||
@@ -482,13 +405,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
.throttleFirst(5, TimeUnit.MINUTES)
|
||||
.subscribe(networkObserver);
|
||||
|
||||
JgyUtils.getInstance().checkAoleyunApp();
|
||||
|
||||
String allowAppList = Settings.System.getString(getContentResolver(), CommonConfig.SEEWO_APP_SOURCE_INSTALL_WHITELIST);
|
||||
if (TextUtils.isEmpty(allowAppList)) {
|
||||
Settings.System.putString(getContentResolver(), CommonConfig.SEEWO_APP_SOURCE_INSTALL_WHITELIST, String.join(",", JgyUtils.DEFAULT_APP_SOURCE_WHITE_LIST));
|
||||
}
|
||||
|
||||
//应用存在性检查与安装白名单初始化(Settings / 包管理读操作)移到后台线程(见 initOnBackground)
|
||||
|
||||
int reminder = mMMKV.decodeInt(CommonConfig.EYE_TO_SCREEN_DISTANCE_REMINDER, 0);
|
||||
Log.e(TAG, "onCreate: reminder = " + reminder);
|
||||
@@ -504,7 +421,156 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
startService(new Intent(this, ShakeDetectionService.class));
|
||||
}
|
||||
|
||||
//以上是必须在主线程完成的工作(startForeground、LiveData、广播注册、悬浮窗等),
|
||||
//其余 Settings、包管理、文件 IO 等耗时操作统一切到后台线程执行,
|
||||
//避免服务创建阶段长时间阻塞主线程造成 Service ANR
|
||||
runOnBackground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
initOnBackground();
|
||||
}
|
||||
});
|
||||
|
||||
mMMKV.encode(CommonConfig.DEVICES_FRIST_START, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台服务声明。
|
||||
* <p>Android 14 起前台服务必须在清单中声明 foregroundServiceType(见 AndroidManifest),
|
||||
* 并且 startForegroundService() 之后 5 秒内必须调用 startForeground(),
|
||||
* 否则系统会抛出 ForegroundServiceDidNotStartInTimeException 并触发 ANR。
|
||||
* 因此该方法在 onCreate 最前面执行,内部并做异常兜底。
|
||||
*/
|
||||
private void startForegroundIfNeeded() {
|
||||
if (JgyUtils.getInstance().checkAppPlatform() != JgyUtils.HL500Platform) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mNotificationManagerCompat = NotificationManagerCompat.from(this);
|
||||
createNotificationChannel();
|
||||
sendSimpleNotification();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "startForegroundIfNeeded: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台线程执行的初始化,只包含 Settings 读写、包管理、文件 IO 等非 UI 操作。
|
||||
*/
|
||||
private void initOnBackground() {
|
||||
try {
|
||||
if (mMMKV.decodeInt(CommonConfig.DEVICES_FRIST_START, 0) == 0) {
|
||||
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.DX11Platform) {
|
||||
Settings.System.putInt(getContentResolver(), CommonConfig.AOLE_ACTION_DEVELOPER_OPTIONS, 1);
|
||||
}
|
||||
if (!checkAoleyunApp()) {
|
||||
Log.e(TAG, "onStartCommand: " + "checkAoleyunApp");
|
||||
// mPresenter.checkAoleyunUpdate();
|
||||
}
|
||||
|
||||
try {
|
||||
//本地默认禁用的应用统一在 ApkUtils.DEFAULT_DISABLE_APP_MAP 中按机型维护,
|
||||
//showAllApp() 会跳过这些应用,避免被重新启用
|
||||
ApkUtils.hideDefaultDisableApp(this);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initOnBackground: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ("YX-T01".equalsIgnoreCase(Build.MODEL)) {
|
||||
JgyUtils.getInstance().setTongyiAppTop();
|
||||
}
|
||||
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.YXPD1Platform) {
|
||||
Settings.System.putString(getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, Utils.getSerial(this));
|
||||
if (ApkUtils.isAvailable(this, JgyUtils.JXW_LAUNCHER_PACKAGE_NAME)) {
|
||||
Intent intent2 = new Intent(Intent.ACTION_VIEW);
|
||||
intent2.setPackage(JgyUtils.JXW_LAUNCHER_PACKAGE_NAME);
|
||||
intent2.setClassName(JgyUtils.JXW_LAUNCHER_PACKAGE_NAME, JgyUtils.JXW_LAUNCHER_CLASS_NAME);
|
||||
try {
|
||||
startService(intent2);//或者bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 都能启动
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initOnBackground: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String launcherPkg = JgyUtils.getInstance().getDefaultDesktop();
|
||||
Log.e(TAG, "initOnBackground: " + launcherPkg);
|
||||
JgyUtils.getInstance().checkDefaultDesktop(launcherPkg);
|
||||
|
||||
Settings.Global.putString(getContentResolver(), "AOLE_SERIAL", Utils.getSerial(MainService.this));
|
||||
|
||||
PackageManager pm = getPackageManager();
|
||||
try {
|
||||
Log.e(TAG, "initOnBackground: " + pm.getApplicationEnabledSetting("com.teclast.update"));
|
||||
pm.setApplicationEnabledSetting("com.teclast.update", PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0);
|
||||
Log.e(TAG, "initOnBackground: enable com.teclast.update");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initOnBackground: setApplicationEnabledSetting = " + e.getMessage());
|
||||
}
|
||||
if ("V1.01_20230519".equalsIgnoreCase(Build.DISPLAY)) {
|
||||
Log.e(TAG, "initOnBackground: enable");
|
||||
Intent intent = new Intent();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClassName("com.teclast.update", "com.teclast.update.GoogleOtaClient");
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initOnBackground: startActivity = " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (JgyUtils.getInstance().isAiuduPlatform()) {
|
||||
ApkUtils.HideAiUDuApp();
|
||||
}
|
||||
|
||||
//根据要求卸载这两个应用
|
||||
if (JgyUtils.getInstance().getDeviceIsLocked()) {
|
||||
ApkUtils.UninstallAPP(this, "com.joytv.live");
|
||||
ApkUtils.UninstallAPP(this, "com.tencent.android.qqdownloader");
|
||||
}
|
||||
|
||||
int oldVersionCode = mMMKV.decodeInt(CommonConfig.SN_VERSIONCODE, 0);
|
||||
if (oldVersionCode < BuildConfig.VERSION_CODE) {
|
||||
Log.e(TAG, "initOnBackground: " + "upgrade successful");
|
||||
try {
|
||||
new CacheUtils().cleanApplicationUserData(this, "com.aoleyun.audos");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initOnBackground: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
mMMKV.encode(CommonConfig.SN_VERSIONCODE, BuildConfig.VERSION_CODE);
|
||||
|
||||
JgyUtils.getInstance().writeAppPackageList();
|
||||
|
||||
IActivityManager activityManager = ActivityManagerNative.getDefault();
|
||||
try {
|
||||
activityManager.setActivityController(new AoleyunActivityController(), false);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "setActivityController: " + e.getMessage());
|
||||
}
|
||||
|
||||
JgyUtils.getInstance().checkAoleyunApp();
|
||||
|
||||
String allowAppList = Settings.System.getString(getContentResolver(), CommonConfig.SEEWO_APP_SOURCE_INSTALL_WHITELIST);
|
||||
if (TextUtils.isEmpty(allowAppList)) {
|
||||
Settings.System.putString(getContentResolver(), CommonConfig.SEEWO_APP_SOURCE_INSTALL_WHITELIST, String.join(",", JgyUtils.DEFAULT_APP_SOURCE_WHITE_LIST));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initOnBackground: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在后台单线程中串行执行初始化任务
|
||||
*/
|
||||
private void runOnBackground(Runnable runnable) {
|
||||
try {
|
||||
sInitExecutor.execute(runnable);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "runOnBackground: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void initLiveData() {
|
||||
@@ -544,7 +610,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
mServiceModel.setLogoImg();
|
||||
mServiceModel.getAllAppList();
|
||||
mServiceModel.getBrowserBookmarks();
|
||||
mServiceModel.getDesktopIcon();
|
||||
mServiceModel.getIconControl();
|
||||
mServiceModel.getAllInfoAndSetting();
|
||||
mServiceModel.getAppAutoStartUpdateAndNet();
|
||||
mServiceModel.setAppinsideWeb();
|
||||
@@ -594,23 +660,23 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
showFloatingWindow(tips);
|
||||
}
|
||||
screenlocked = true;
|
||||
SPUtils.put(MainService.this, "is_screen_lock", true);
|
||||
SPUtils.put(MainService.this, "screen_tips", tips);
|
||||
mMMKV.encode("is_screen_lock", true);
|
||||
mMMKV.encode("screen_tips", tips);
|
||||
} else {
|
||||
if (!timelocked) {
|
||||
hideFloatingWindow();
|
||||
}
|
||||
screenlocked = false;
|
||||
SPUtils.put(MainService.this, "is_screen_lock", false);
|
||||
SPUtils.put(MainService.this, "screen_tips", "");
|
||||
mMMKV.encode("is_screen_lock", false);
|
||||
mMMKV.encode("screen_tips", "");
|
||||
}
|
||||
} else {
|
||||
if (!timelocked) {
|
||||
hideFloatingWindow();
|
||||
}
|
||||
screenlocked = false;
|
||||
SPUtils.put(MainService.this, "is_screen_lock", false);
|
||||
SPUtils.put(MainService.this, "screen_tips", "");
|
||||
mMMKV.encode("is_screen_lock", false);
|
||||
mMMKV.encode("screen_tips", "");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -624,14 +690,20 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.e(TAG, "onStartCommand: ");
|
||||
JgyUtils.getInstance().wakeUpAoleyunAPP();
|
||||
if (mMMKV.decodeInt(CommonConfig.DEVICES_FRIST_START, 0) == 0) {
|
||||
if (!checkAoleyunApp()) {
|
||||
Log.e(TAG, "onStartCommand: " + "checkAoleyunApp");
|
||||
// mPresenter.checkAoleyunUpdate();
|
||||
}
|
||||
try {
|
||||
JgyUtils.getInstance().wakeUpAoleyunAPP();
|
||||
// checkUpdate 内部会做安装包 MD5 校验、文件读写与 APK 安装,属于耗时 IO,
|
||||
// 必须放到子线程执行,否则会阻塞主线程导致 ANR,且安装异常会直接闪退
|
||||
new Thread(() -> {
|
||||
try {
|
||||
NetInterfaceManager.getInstance().checkUpdate();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "onStartCommand: checkUpdate 异常 " + e.getMessage(), e);
|
||||
}
|
||||
}, "check-update").start();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "onStartCommand: 异常 " + e.getMessage(), e);
|
||||
}
|
||||
NetInterfaceManager.getInstance().checkUpdate();
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@@ -687,7 +759,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
private void setStatusbar() {
|
||||
// StatusBarManager mStatusBarManager = (StatusBarManager) getApplicationContext().getSystemService(Context.STATUS_BAR_SERVICE);
|
||||
// mStatusBarManager.disable(StatusBarManager.DISABLE_RECENT | StatusBarManager.DISABLE_HOME);
|
||||
String status = (String) SPUtils.get(this, JGYActions.ACTION_STATUSBAR_STATUS, "");
|
||||
String status = mMMKV.decodeString(JGYActions.ACTION_STATUSBAR_STATUS, "");
|
||||
Log.e(TAG, "setStatusbar: String: " + status);
|
||||
if (TextUtils.isEmpty(status)) {
|
||||
Log.e(TAG, "setStatusbar: " + "status is NULL");
|
||||
@@ -709,25 +781,25 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
|
||||
private void unRegisterReceivers() {
|
||||
if (null != lockScreenReceiver) {
|
||||
unregisterReceiver(lockScreenReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, lockScreenReceiver);
|
||||
}
|
||||
if (null != mTimeChangedReceiver) {
|
||||
unregisterReceiver(mTimeChangedReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mTimeChangedReceiver);
|
||||
}
|
||||
if (null != mInstallReceiver) {
|
||||
unregisterReceiver(mInstallReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mInstallReceiver);
|
||||
}
|
||||
if (null != mWifiReceiver) {
|
||||
unregisterReceiver(mWifiReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mWifiReceiver);
|
||||
}
|
||||
if (null != mRefreshReceiver) {
|
||||
unregisterReceiver(mRefreshReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mRefreshReceiver);
|
||||
}
|
||||
if (null != screenLockReceiver) {
|
||||
unregisterReceiver(screenLockReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, screenLockReceiver);
|
||||
}
|
||||
if (null != mDisplayReceiver) {
|
||||
unregisterReceiver(mDisplayReceiver);
|
||||
ReceiverCompat.unregisterReceiver(this, mDisplayReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -747,7 +819,8 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
|
||||
filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
registerReceiver(mWifiReceiver, filter);
|
||||
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
|
||||
ReceiverCompat.registerReceiver(this, mWifiReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
public class WifiReceiver extends BroadcastReceiver {
|
||||
@@ -806,7 +879,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
}
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(RefreshReceiver.REFRESH_RECEIVER_ACTION);
|
||||
registerReceiver(mRefreshReceiver, filter);
|
||||
ReceiverCompat.registerReceiver(this, mRefreshReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
public class RefreshReceiver extends BroadcastReceiver {
|
||||
@@ -833,7 +906,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
registerReceiver(screenLockReceiver, filter);
|
||||
ReceiverCompat.registerReceiver(this, screenLockReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
private class ScreenLockReceiver extends BroadcastReceiver {
|
||||
@@ -944,7 +1017,8 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||
filter.addAction(LockScreenReceiver.action_lock);
|
||||
filter.addAction(LockScreenReceiver.action_unlock);
|
||||
registerReceiver(lockScreenReceiver, filter);
|
||||
//锁屏管控广播由兄弟管控应用下发,需保持可被外部发送
|
||||
ReceiverCompat.registerReceiver(this, lockScreenReceiver, filter, ReceiverCompat.RECEIVER_EXPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1025,7 +1099,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
|
||||
filter.addAction(Intent.ACTION_TIME_TICK);
|
||||
filter.addAction(TimeChangedReceiver.ACTION_UPDATE);
|
||||
registerReceiver(mTimeChangedReceiver, filter);
|
||||
ReceiverCompat.registerReceiver(this, mTimeChangedReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
public class TimeChangedReceiver extends BroadcastReceiver {
|
||||
@@ -1070,7 +1144,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
filter.addAction(NIGHT_DISPLAY_DISABLED);
|
||||
filter.addAction(READ_MODEL_ENABLED);
|
||||
filter.addAction(READ_MODEL_DISABLED);
|
||||
registerReceiver(mDisplayReceiver, filter);
|
||||
ReceiverCompat.registerReceiver(this, mDisplayReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
public static final String NIGHT_DISPLAY_ENABLED = "NIGHT_DISPLAY_ENABLED";
|
||||
@@ -1108,7 +1182,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
private static final String UPLOAD_LOG_TIME = "23:00:00";
|
||||
|
||||
private void checkUploadLogTime() {
|
||||
int is_log_type = (int) SPUtils.get(this, CommonConfig.UPLOAD_LOG_TYPE, 0);
|
||||
int is_log_type = mMMKV.decodeInt(CommonConfig.UPLOAD_LOG_TYPE, 0);
|
||||
if (is_log_type == 0) {
|
||||
Log.e(TAG, "checkUploadLogTime: " + "is_log_type = " + is_log_type);
|
||||
} else {
|
||||
@@ -1197,7 +1271,9 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
mInstallReceiver = new InstallReceiver();
|
||||
}
|
||||
IntentFilter intentFilter = new IntentFilter(INSTALLAPK_ACTION);
|
||||
registerReceiver(mInstallReceiver, intentFilter, INSTALLAPK_PREMISSIONS, null);
|
||||
//安装广播由兄弟管控应用下发(受签名权限保护),需显式声明导出
|
||||
ReceiverCompat.registerReceiver(this, mInstallReceiver, intentFilter,
|
||||
ReceiverCompat.RECEIVER_EXPORTED, INSTALLAPK_PREMISSIONS);
|
||||
}
|
||||
|
||||
private static final String INSTALLAPK_ACTION = "JGY_INSTALLAPK_ACTION";
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.aoleyun.sn.comm.CommonConfig;
|
||||
import com.aoleyun.sn.comm.JGYActions;
|
||||
import com.aoleyun.sn.network.NetInterfaceManager;
|
||||
import com.aoleyun.sn.utils.JgyUtils;
|
||||
import com.aoleyun.sn.utils.SPUtils;
|
||||
import com.aoleyun.sn.utils.SysSettingUtils;
|
||||
import com.aoleyun.sn.utils.URLUtils;
|
||||
import com.aoleyun.sn.utils.Utils;
|
||||
@@ -131,7 +130,7 @@ public class MainServiceModel extends ViewModel {
|
||||
* 第一次连接
|
||||
*/
|
||||
public void getFirstConnect() {
|
||||
int first = (int) SPUtils.get(mContext, CommonConfig.JGY_FIRST_CONNECT, 0);
|
||||
int first = mMMKV.decodeInt(CommonConfig.JGY_FIRST_CONNECT, 0);
|
||||
Log.e(TAG, "getFirstConnect: " + "first_connect = " + first);
|
||||
if (first == 0) {
|
||||
Log.e(TAG, "setDisableSetting: ");
|
||||
@@ -377,13 +376,12 @@ public class MainServiceModel extends ViewModel {
|
||||
new URLUtils(mContext).setBrowserBlackList();
|
||||
}
|
||||
|
||||
public void getDesktopIcon() {
|
||||
public void getIconControl() {
|
||||
NetInterfaceManager.getInstance()
|
||||
.getDesktopIcon(getLifecycle(), new NetInterfaceManager.onCompleteCallback() {
|
||||
.getIconControl(getLifecycle(), new NetInterfaceManager.onCompleteCallback() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getDesktopIcon", "onComplete: ");
|
||||
// JgyUtils.getInstance().hideSystemAPP();
|
||||
Log.e("getIconControl", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,9 +46,12 @@ import java.io.OutputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
@@ -355,6 +358,49 @@ public class ApkUtils {
|
||||
this.add("com.android.browser");
|
||||
}};
|
||||
|
||||
/**
|
||||
* 所有机型公共的默认禁用应用({@link #DEFAULT_DISABLE_APP_MAP} 的固定 key)
|
||||
*/
|
||||
public static final String DEFAULT_DISABLE_APP_KEY = "default";
|
||||
|
||||
/**
|
||||
* 设备型号前缀({@link #DEFAULT_DISABLE_APP_MAP} 中以该前缀 + Build.MODEL 作为 key)
|
||||
*/
|
||||
public static final String DEFAULT_DISABLE_APP_MODEL_PREFIX = "MODEL:";
|
||||
|
||||
/**
|
||||
* 本地默认禁用的应用(按机型区分,设备启动时由 MainService 禁用)。
|
||||
* <p>
|
||||
* {@link #showAllApp(Context)} 不会再把这些应用重新启用,避免与本地默认禁用、
|
||||
* 后台在线下发的禁用互相覆盖。
|
||||
* <p>
|
||||
* key 支持两种写法,命中后会被叠加:
|
||||
* <p>
|
||||
* 1、平台:"default" 表示所有机型;其它按 build.gradle 中 buildConfigField 配置的 platform
|
||||
* (即 {@link BuildConfig#platform}),如 "QualcommA"、"MT6765"、"MTKG11"、"展锐";
|
||||
* <p>
|
||||
* 2、设备型号:"MODEL:" 前缀 + {@link Build#MODEL},如 "MODEL:G1096JW"。
|
||||
* <p>
|
||||
* 需要给某个机型单独配置时,在这里增加对应 key 与包名即可。
|
||||
*/
|
||||
public static final Map<String, Set<String>> DEFAULT_DISABLE_APP_MAP = new HashMap<String, Set<String>>() {{
|
||||
//所有机型默认禁用
|
||||
put(DEFAULT_DISABLE_APP_KEY, new HashSet<String>() {{
|
||||
this.add("com.android.email");
|
||||
this.add("com.android.browser");
|
||||
}});
|
||||
put(JgyUtils.Qualcomm_TAG, new HashSet<String>() {{
|
||||
this.add("org.chromium.chrome");
|
||||
this.add("com.onyx.appmarket");
|
||||
this.add("com.onyx.aiassistant");
|
||||
this.add("com.onyx.android.production.test");
|
||||
this.add("com.onyx.dict");
|
||||
this.add("com.onyx.mail");
|
||||
this.add("com.onyx.musicplayer");
|
||||
this.add("com.onyx.voicerecorder");
|
||||
}});
|
||||
}};
|
||||
|
||||
/**
|
||||
* 预置的可以卸载的系统应用,判断出来为第三方
|
||||
*/
|
||||
@@ -912,10 +958,15 @@ public class ApkUtils {
|
||||
|
||||
public static void installApp(Context context, String filePath) {
|
||||
Log.e(TAG, "installApp: " + filePath);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
installAppatPie(context, filePath);
|
||||
} else {
|
||||
installApps(filePath);
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
installAppatPie(context, filePath);
|
||||
} else {
|
||||
installApps(filePath);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// 兜底:安装入口不向调用方抛异常,避免主线程/服务线程因安装失败而闪退
|
||||
Log.e(TAG, "installApp: 安装异常 " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -966,35 +1017,87 @@ public class ApkUtils {
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public static void installAppatPie(Context context, String apkFilePath) {
|
||||
File file = new File(apkFilePath);
|
||||
Log.e(TAG, "installAppatPie: 正在安装应用 " + apkFilePath);
|
||||
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
|
||||
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
|
||||
.SessionParams.MODE_FULL_INSTALL);
|
||||
sessionParams.setSize(file.length());
|
||||
int sessionId = createSession(packageInstaller, sessionParams);
|
||||
if (sessionId != -1) {
|
||||
boolean copySuccess = copyApkFile(packageInstaller, sessionId, apkFilePath);
|
||||
if (copySuccess) {
|
||||
Toaster.show("正在安装应用");
|
||||
install(packageInstaller, sessionId, context);
|
||||
try {
|
||||
File file = new File(apkFilePath);
|
||||
Log.e(TAG, "installAppatPie: 正在安装应用 " + apkFilePath);
|
||||
if (!file.exists() || file.length() <= 0) {
|
||||
Log.e(TAG, "installAppatPie: 安装包不存在或为空 " + apkFilePath);
|
||||
return;
|
||||
}
|
||||
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
|
||||
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
|
||||
.SessionParams.MODE_FULL_INSTALL);
|
||||
sessionParams.setSize(file.length());
|
||||
int sessionId = createSession(packageInstaller, sessionParams);
|
||||
if (sessionId != -1) {
|
||||
boolean copySuccess = copyApkFile(packageInstaller, sessionId, apkFilePath);
|
||||
if (copySuccess) {
|
||||
Toaster.show("正在安装应用");
|
||||
install(packageInstaller, sessionId, context);
|
||||
} else {
|
||||
Log.e(TAG, "installAppatPie: 拷贝安装包失败,放弃本次会话");
|
||||
abandonSession(packageInstaller, sessionId);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// 安装失败只记录日志,绝不向外抛出,否则会直接导致调用方(含主线程)进程闪退
|
||||
Log.e(TAG, "installAppatPie: 安装异常 " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
private static void install(PackageInstaller packageInstaller, int sessionId, Context context) {
|
||||
PackageInstaller.Session session = null;
|
||||
try {
|
||||
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
|
||||
Intent intent = new Intent(context, InstallResultReceiver.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
1, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
session.commit(pendingIntent.getIntentSender());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
session = packageInstaller.openSession(sessionId);
|
||||
session.commit(createResultPendingIntent(context).getIntentSender());
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "install: 安装失败 " + e.getMessage(), e);
|
||||
abandonSession(packageInstaller, sessionId);
|
||||
} finally {
|
||||
if (session != null) {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 PackageInstaller 的安装结果回调 PendingIntent。
|
||||
* <p>
|
||||
* Android 14(targetSdk 34+)起,系统会向该 PendingIntent 回填安装结果,
|
||||
* 因此必须使用 FLAG_MUTABLE,否则 commit() 会抛出
|
||||
* IllegalArgumentException: The commit() status receiver should come from a mutable PendingIntent,
|
||||
* 该异常在旧代码中会直接冒泡到主线程造成进程闪退。
|
||||
*/
|
||||
private static PendingIntent createResultPendingIntent(Context context) {
|
||||
Intent intent = new Intent(context, InstallResultReceiver.class);
|
||||
intent.setPackage(context.getPackageName());
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_12) {
|
||||
flags |= FLAG_MUTABLE;
|
||||
}
|
||||
return PendingIntent.getBroadcast(context, 1, intent, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 12(API 31)。编译使用的 framework.jar 为设备提取版,不含 API 31 新增符号,
|
||||
* 因此这里使用字面量常量代替 {@code Build.VERSION_CODES.S} 与 {@code PendingIntent.FLAG_MUTABLE}。
|
||||
*/
|
||||
private static final int ANDROID_12 = 31;
|
||||
/**
|
||||
* PendingIntent.FLAG_MUTABLE(API 31 新增,值为 1 << 25)。
|
||||
*/
|
||||
private static final int FLAG_MUTABLE = 1 << 25;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
private static void abandonSession(PackageInstaller packageInstaller, int sessionId) {
|
||||
try {
|
||||
packageInstaller.abandonSession(sessionId);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "abandonSession: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1150,11 +1253,14 @@ public class ApkUtils {
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public static void uninstall(Context context, String packageName) {
|
||||
Intent broadcastIntent = new Intent(context, InstallResultReceiver.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1,
|
||||
broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
|
||||
packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
|
||||
try {
|
||||
// 卸载结果同样由系统回填到 PendingIntent 中,Android 14+ 必须为可变(Mutable)
|
||||
PendingIntent pendingIntent = createResultPendingIntent(context);
|
||||
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
|
||||
packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "uninstall: 卸载失败 " + packageName + " " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean pmUninstall(String packageName) {
|
||||
@@ -1242,23 +1348,35 @@ public class ApkUtils {
|
||||
|
||||
public static void showAllApp(Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
// 查询所有已经安装的应用程序
|
||||
List<PackageInfo> packages = pm.getInstalledPackages(PackageManager.COMPONENT_ENABLED_STATE_ENABLED | PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
|
||||
//本地默认禁用 + getSnAppAttr 在线禁用 + getIconControl 应用管控禁用,
|
||||
//这三类应用要保持禁用状态,不能被重新启用
|
||||
Set<String> keepDisablePkgs = getKeepDisablePkgs(context);
|
||||
Log.e(TAG, "showAllApp: keepDisablePkgs = " + keepDisablePkgs);
|
||||
// 查询所有已经安装的应用程序(原来传的是 3,等价于 GET_ACTIVITIES|GET_RECEIVERS,会多加载无用元数据)
|
||||
List<PackageInfo> packages = pm.getInstalledPackages(0);
|
||||
boolean isAiuduPlatform = JgyUtils.getInstance().isAiuduPlatform();
|
||||
for (PackageInfo packageInfo : packages) {
|
||||
String pkg = packageInfo.packageName;
|
||||
Log.e(TAG, "showAllApp: " + pkg + " disable = " + (pm.getApplicationEnabledSetting(pkg) == PackageManager.COMPONENT_ENABLED_STATE_DISABLED));
|
||||
if (canremove_systemapp.contains(pkg)
|
||||
&& !show_canremove_systemapp.contains(pkg)) {
|
||||
Log.e(TAG, "showAllApp: continue: " + pkg);
|
||||
continue;
|
||||
}
|
||||
if (JgyUtils.getInstance().isAiuduPlatform()) {
|
||||
if (AiudHideApp.contains(pkg)) {
|
||||
continue;
|
||||
}
|
||||
if (isAiuduPlatform && AiudHideApp.contains(pkg)) {
|
||||
continue;
|
||||
}
|
||||
if (keepDisablePkgs.contains(pkg)) {
|
||||
Log.e(TAG, "showAllApp: keep disable: " + pkg);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Log.i(TAG, "showAllApp: show: " + pkg);
|
||||
int state = pm.getApplicationEnabledSetting(pkg);
|
||||
//已经是启用状态,无需重复设置(避免每次启动都做一遍无用的跨进程调用)
|
||||
if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|
||||
|| state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
|
||||
continue;
|
||||
}
|
||||
Log.e(TAG, "showAllApp: show: " + pkg + ", state = " + state);
|
||||
pm.setApplicationEnabledSetting(pkg, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "showAllApp: " + e.getMessage());
|
||||
@@ -1266,6 +1384,193 @@ public class ApkUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需要保持禁用状态的应用,包含:
|
||||
* <p>
|
||||
* 1、当前生效的本地默认禁用应用({@link #getEffectiveDefaultDisableApp(Context)});
|
||||
* <p>
|
||||
* 2、getSnAppAttr 接口下发的在线禁用应用;
|
||||
* <p>
|
||||
* 3、getIconControl 接口下发的禁用应用。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 不允许被 {@link #showAllApp(Context)} 重新启用的包名集合
|
||||
*/
|
||||
public static Set<String> getKeepDisablePkgs(Context context) {
|
||||
Set<String> pkgs = new HashSet<>(getEffectiveDefaultDisableApp(context));
|
||||
pkgs.addAll(getOnlineDisablePkgs(context));
|
||||
pkgs.addAll(getDesktopHidePkgs(context));
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前机型本地默认禁用的应用。
|
||||
* <p>
|
||||
* 结果由 {@link #DEFAULT_DISABLE_APP_MAP} 决定,按"所有机型 + 平台 + 设备型号"三级叠加,
|
||||
* 见 {@link #DEFAULT_DISABLE_APP_MAP} 的说明。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 当前机型需要默认禁用的包名集合
|
||||
*/
|
||||
public static Set<String> getDefaultDisableApp(Context context) {
|
||||
String platform = BuildConfig.platform;
|
||||
String model = Build.MODEL;
|
||||
Set<String> pkgs = new HashSet<>();
|
||||
//所有机型公共的默认禁用应用
|
||||
pkgs.addAll(getDefaultDisableAppByKey(DEFAULT_DISABLE_APP_KEY));
|
||||
//按平台区分的默认禁用应用
|
||||
pkgs.addAll(getDefaultDisableAppByKey(platform));
|
||||
//按设备型号区分的默认禁用应用
|
||||
pkgs.addAll(getDefaultDisableAppByKey(DEFAULT_DISABLE_APP_MODEL_PREFIX + model));
|
||||
Log.e(TAG, "getDefaultDisableApp: platform = " + platform + ", model = " + model + ", pkgs = " + pkgs);
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 key 取默认禁用应用清单
|
||||
*
|
||||
* @param key {@link #DEFAULT_DISABLE_APP_MAP} 的 key
|
||||
* @return 对应的包名集合,没有配置时返回空集合
|
||||
*/
|
||||
private static Set<String> getDefaultDisableAppByKey(String key) {
|
||||
Set<String> pkgs = DEFAULT_DISABLE_APP_MAP.get(key);
|
||||
return pkgs == null ? Collections.emptySet() : pkgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 getSnAppAttr 接口下发的在线禁用清单
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 在线禁用的包名集合
|
||||
*/
|
||||
public static Set<String> getOnlineDisablePkgs(Context context) {
|
||||
String appString = Settings.Global.getString(context.getContentResolver(), CommonConfig.AOLE_ACTION_DISABLE_APP);
|
||||
Log.e(TAG, "getOnlineDisablePkgs: " + appString);
|
||||
return new HashSet<>(parsePkgList(appString));
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 getIconControl 接口下发的禁用应用清单
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 被禁用的包名集合
|
||||
*/
|
||||
public static Set<String> getDesktopHidePkgs(Context context) {
|
||||
String appString = Settings.Global.getString(context.getContentResolver(), CommonConfig.AOLE_ACTION_HIDE_DESKTOP_ICON);
|
||||
Log.e(TAG, "getDesktopHidePkgs: " + appString);
|
||||
return new HashSet<>(parsePkgList(appString));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线禁用来源(getSnAppAttr 接口下发)
|
||||
*/
|
||||
public static final int SOURCE_ONLINE_DISABLE = 1;
|
||||
/**
|
||||
* 应用管控禁用来源(getIconControl 接口下发)
|
||||
*/
|
||||
public static final int SOURCE_DESKTOP_HIDE = 1 << 1;
|
||||
|
||||
/**
|
||||
* 读取 getIconControl 接口要求显示(control_type = 1)的应用清单
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 接口要求显示的包名集合
|
||||
*/
|
||||
public static Set<String> getShowAppPkgs(Context context) {
|
||||
String appString = Settings.Global.getString(context.getContentResolver(), CommonConfig.AOLE_ACTION_SHOW_DESKTOP_ICON);
|
||||
Log.e(TAG, "getShowAppPkgs: " + appString);
|
||||
return new HashSet<>(parsePkgList(appString));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前生效的默认禁用应用。
|
||||
* <p>
|
||||
* = 机型配置的默认禁用({@link #getDefaultDisableApp(Context)})− getIconControl 接口明确要求显示的应用,
|
||||
* 即后台下发的"显示"优先级高于本地默认禁用。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 当前生效的默认禁用包名集合
|
||||
*/
|
||||
public static Set<String> getEffectiveDefaultDisableApp(Context context) {
|
||||
Set<String> pkgs = new HashSet<>(getDefaultDisableApp(context));
|
||||
pkgs.removeAll(getShowAppPkgs(context));
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用是否被"其他管控来源"要求保持禁用。
|
||||
* <p>
|
||||
* 用于放开某个来源的禁用清单时,避免把其他来源(本地默认禁用、爱优读隐藏、预置可卸载应用,
|
||||
* 以及未排除的在线禁用/应用管控禁用)要求禁用的应用一起放开。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param pkg 包名
|
||||
* @param excludeSources 需要排除的来源({@link #SOURCE_ONLINE_DISABLE}、
|
||||
* {@link #SOURCE_DESKTOP_HIDE} 按位或),本人正在放开的来源需要排除
|
||||
* @return true:该应用还被其他来源管控,不能放开
|
||||
*/
|
||||
public static boolean isKeepDisableByOtherSource(Context context, String pkg, int excludeSources) {
|
||||
if (TextUtils.isEmpty(pkg)) {
|
||||
return false;
|
||||
}
|
||||
if (getEffectiveDefaultDisableApp(context).contains(pkg)) {
|
||||
return true;
|
||||
}
|
||||
if (canremove_systemapp.contains(pkg) && !show_canremove_systemapp.contains(pkg)) {
|
||||
return true;
|
||||
}
|
||||
if (JgyUtils.getInstance().isAiuduPlatform() && AiudHideApp.contains(pkg)) {
|
||||
return true;
|
||||
}
|
||||
if ((excludeSources & SOURCE_ONLINE_DISABLE) == 0 && getOnlineDisablePkgs(context).contains(pkg)) {
|
||||
return true;
|
||||
}
|
||||
return (excludeSources & SOURCE_DESKTOP_HIDE) == 0 && getDesktopHidePkgs(context).contains(pkg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用当前生效的默认禁用应用(已禁用的跳过,被接口要求显示的不禁用)
|
||||
*
|
||||
* @param context 上下文
|
||||
*/
|
||||
public static void hideDefaultDisableApp(Context context) {
|
||||
for (String pkg : getEffectiveDefaultDisableApp(context)) {
|
||||
if (isDisable(context, pkg)) {
|
||||
//已经是禁用状态(或未安装),无需重复禁用
|
||||
continue;
|
||||
}
|
||||
Log.e(TAG, "hideDefaultDisableApp: hide: " + pkg);
|
||||
JgyUtils.getInstance().hideApp(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Settings.Global 中以逗号分隔的包名清单
|
||||
*
|
||||
* @param appString 清单字符串
|
||||
* @return 包名列表(已过滤空值、中括号以及后台无数据时写入的"null")
|
||||
*/
|
||||
public static List<String> parsePkgList(String appString) {
|
||||
List<String> pkgs = new ArrayList<>();
|
||||
if (TextUtils.isEmpty(appString)) {
|
||||
return pkgs;
|
||||
}
|
||||
try {
|
||||
for (String pkg : appString.split(",")) {
|
||||
pkg = pkg.replace("[", "").replace("]", "").trim();
|
||||
if (TextUtils.isEmpty(pkg) || "null".equalsIgnoreCase(pkg)) {
|
||||
continue;
|
||||
}
|
||||
if (!pkgs.contains(pkg)) {
|
||||
pkgs.add(pkg);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "parsePkgList: " + e.getMessage());
|
||||
}
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
public static void hideSystemSettingAPP(Context context, String pkage) {
|
||||
int hide = 0;
|
||||
//后台0显示,1隐藏
|
||||
|
||||
@@ -42,11 +42,13 @@ public class InstallResultReceiver extends BroadcastReceiver {
|
||||
// while (iterator.hasNext()) {
|
||||
// Log.d("KEY", iterator.next());
|
||||
// }
|
||||
String STATUS = intent.getStringExtra(PackageInstaller.EXTRA_STATUS);
|
||||
//STATUS / SESSION_ID / LEGACY_STATUS 都是 int 类型 extra,
|
||||
//使用 getStringExtra 读取会抛 ClassCastException(被 Bundle 吞掉并打印警告)
|
||||
int STATUS = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
|
||||
String PACKAGE_NAME = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME);
|
||||
String SESSION_ID = intent.getStringExtra(PackageInstaller.EXTRA_SESSION_ID);
|
||||
int SESSION_ID = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
|
||||
String STATUS_MESSAGE = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
|
||||
String LEGACY_STATUS = intent.getStringExtra("android.content.pm.extra.LEGACY_STATUS");
|
||||
int LEGACY_STATUS = intent.getIntExtra("android.content.pm.extra.LEGACY_STATUS", -1);
|
||||
// Log.e("fht", STATUS);
|
||||
// Log.e("fht", PACKAGE_NAME);
|
||||
// Log.e("fht", SESSION_ID);
|
||||
|
||||
@@ -30,7 +30,9 @@ import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.os.Debug;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
@@ -515,7 +517,8 @@ public class JgyUtils {
|
||||
BatteryManager batteryManager = (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
|
||||
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
|
||||
} else {
|
||||
Intent intent = new ContextWrapper(mContext).registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
Intent intent = ReceiverCompat.registerReceiver(new ContextWrapper(mContext), null,
|
||||
new IntentFilter(Intent.ACTION_BATTERY_CHANGED), ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
return (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
|
||||
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
|
||||
}
|
||||
@@ -1088,7 +1091,7 @@ public class JgyUtils {
|
||||
|
||||
public void onBootSendNetwork() {
|
||||
if (!MTK11Tag.equals(getAppPlatform())) {
|
||||
String oldBlackListString = (String) SPUtils.get(mContext, CommonConfig.AOLE_ACTION_NETWORK_DISALLOW, "");
|
||||
String oldBlackListString = mMMKV.decodeString(CommonConfig.AOLE_ACTION_NETWORK_DISALLOW, "");
|
||||
HashSet<String> oldBlackListSet = new HashSet<>(Arrays.asList(oldBlackListString.split(",")));
|
||||
Log.e(TAG, "setAppNetwork: oldBlackListSet: " + oldBlackListSet);
|
||||
oldBlackListSet.removeIf(new Predicate<String>() {
|
||||
@@ -1318,7 +1321,7 @@ public class JgyUtils {
|
||||
|
||||
|
||||
private List<TTAppground> getOldWhitelist() {
|
||||
String whiteListString = (String) SPUtils.get(mContext, JGY_APPINSIDE_WHITELIST, "");
|
||||
String whiteListString = mMMKV.decodeString(JGY_APPINSIDE_WHITELIST, "");
|
||||
Log.e(TAG, "getOldWhitelist: " + whiteListString);
|
||||
Gson gson = new Gson();
|
||||
Type listType = new TypeToken<List<TTAppground>>() {
|
||||
@@ -1349,7 +1352,7 @@ public class JgyUtils {
|
||||
}
|
||||
});
|
||||
String data = new Gson().toJson(appgrounds);
|
||||
SPUtils.put(mContext, JGY_APPINSIDE_WHITELIST, data);
|
||||
mMMKV.encode(JGY_APPINSIDE_WHITELIST, data);
|
||||
}
|
||||
|
||||
public void onBootSetAppInsideWeb() {
|
||||
@@ -1507,7 +1510,7 @@ public class JgyUtils {
|
||||
if (locked == JGYActions.FRAME_CODE_LOCKED) {
|
||||
SysSettingUtils.setDisableSetting(mContext);
|
||||
}
|
||||
SPUtils.put(mContext, JGYActions.ACTION_STATUSBAR_STATUS, "");
|
||||
mMMKV.encode(JGYActions.ACTION_STATUSBAR_STATUS, "");
|
||||
} else {
|
||||
if (getAppPlatform().equals(JgyUtils.ZR6016Tag)) {
|
||||
Log.e(TAG, "SettingSysData: ZR6016Tag");
|
||||
@@ -2246,6 +2249,7 @@ public class JgyUtils {
|
||||
|
||||
/**
|
||||
* @param state 1打开 0关闭
|
||||
* dx11 state为1关闭,为0打开
|
||||
*/
|
||||
public void setDeveloperOptions(int state) {
|
||||
Log.e(TAG, "setDeveloperOptions: state = " + state);
|
||||
@@ -2428,11 +2432,11 @@ public class JgyUtils {
|
||||
@Deprecated
|
||||
public void cleanLauncherCache() {
|
||||
Log.e(TAG, "cleanLauncherCache: Start");
|
||||
int cleaned = (int) SPUtils.get(mContext, "fristcleanLauncherCache", 0);
|
||||
int cleaned = mMMKV.decodeInt("fristcleanLauncherCache", 0);
|
||||
if (cleaned == 0) {
|
||||
try {
|
||||
new CacheUtils().cleanApplicationUserData(mContext, "com.android.launcher3");
|
||||
SPUtils.put(mContext, "fristcleanLauncherCache", 1);
|
||||
mMMKV.encode("fristcleanLauncherCache", 1);
|
||||
Log.e(TAG, "cleanLauncherCache: end");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -2677,6 +2681,16 @@ public class JgyUtils {
|
||||
|
||||
private static long lastTime;
|
||||
|
||||
/**
|
||||
* 服务错峰启动间隔(毫秒),避免多个服务同时启动造成主线程串行阻塞
|
||||
*/
|
||||
private static final long SERVICE_START_INTERVAL = 1500L;
|
||||
|
||||
/**
|
||||
* 服务错峰启动用的主线程 Handler
|
||||
*/
|
||||
private static final Handler sStartServiceHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
public static void startServices(Context context) {
|
||||
Log.e(TAG, "startServices: " + context.getClass().getSimpleName());
|
||||
if (System.currentTimeMillis() - lastTime < 10 * 60 * 1000) {
|
||||
@@ -2692,8 +2706,30 @@ public class JgyUtils {
|
||||
} else {
|
||||
context.startService(new Intent(context, MainService.class));
|
||||
}
|
||||
context.startService(new Intent(context, StepService.class));
|
||||
context.startService(new Intent(context, GuardService.class));
|
||||
//三个服务同时启动会在开机阶段造成主线程串行阻塞(曾出现 Service 启动 200s 超时 ANR),
|
||||
//这里按固定间隔错峰启动 StepService / GuardService
|
||||
final Context appContext = context.getApplicationContext() != null
|
||||
? context.getApplicationContext() : context;
|
||||
sStartServiceHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
appContext.startService(new Intent(appContext, StepService.class));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "startServices StepService: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}, SERVICE_START_INTERVAL);
|
||||
sStartServiceHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
appContext.startService(new Intent(appContext, GuardService.class));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "startServices GuardService: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}, SERVICE_START_INTERVAL * 2);
|
||||
// context.startService(new Intent(context, LogcatService.class));
|
||||
}
|
||||
}
|
||||
@@ -2794,7 +2830,7 @@ public class JgyUtils {
|
||||
|
||||
public void checkDefaultDesktop(String pkg) {
|
||||
String desktopPkg = getDefaultDesktop();
|
||||
// String desktopPkg = (String) SPUtils.get(mContext, "default_launcher", "");
|
||||
// String desktopPkg = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
|
||||
Log.e(TAG, "checkDefaultDesktop: pkg = " + pkg);
|
||||
Log.e(TAG, "checkDefaultDesktop: desktopPkg = " + desktopPkg);
|
||||
if (TextUtils.isEmpty(pkg)) {
|
||||
@@ -2988,7 +3024,7 @@ public class JgyUtils {
|
||||
// setDefaultLauncher(mContext, pkg, className);
|
||||
Log.e(TAG, "setDefaultDesktop: setDefaultLauncher: pkg = " + pkg);
|
||||
}
|
||||
// String oldDesktop = (String) SPUtils.get(mContext, "default_launcher", "");
|
||||
// String oldDesktop = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
|
||||
// if (Objects.equals(oldDesktop, pkg)) {
|
||||
// Log.e(TAG, "setDefaultDesktop: " + "数据一致");
|
||||
// return;
|
||||
|
||||
122
app/src/main/java/com/aoleyun/sn/utils/ReceiverCompat.java
Normal file
122
app/src/main/java/com/aoleyun/sn/utils/ReceiverCompat.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package com.aoleyun.sn.utils;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* 动态注册广播兼容工具。
|
||||
*
|
||||
* <p>Android 13(API 33)起,动态注册的接收器若包含非系统保护广播,必须显式声明导出标志;
|
||||
* Android 14+ 的系统(含各厂商定制 ROM,例如本机 Android 16)会强制校验,缺失时直接抛出:
|
||||
* <pre>
|
||||
* java.lang.SecurityException: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED
|
||||
* should be specified when a receiver isn't being registered exclusively for system broadcasts
|
||||
* </pre>
|
||||
* 该异常会导致 Activity / Service 创建失败(启动闪退),并进一步造成前台服务
|
||||
* {@code startForegroundService()} 超时 ANR。
|
||||
*
|
||||
* <p>说明:本工程通过 {@code app/libs/framework.jar} 提供系统隐藏 API(该 jar 为
|
||||
* Android 10 / API 29 的 framework),其中已包含
|
||||
* {@code registerReceiver(BroadcastReceiver, IntentFilter, int)} 等带 flags 的重载,
|
||||
* 因此这里直接调用即可,无需反射;在低版本系统中未知标志位会被忽略,
|
||||
* 行为与改造前的两参注册保持一致。
|
||||
*
|
||||
* <p>标志选择建议:
|
||||
* <ul>
|
||||
* <li>{@link #RECEIVER_NOT_EXPORTED}:只接收系统广播,或本应用
|
||||
* (含共享 {@code android.uid.system} 的兄弟应用)发送的广播;</li>
|
||||
* <li>{@link #RECEIVER_EXPORTED}:需要接收其它(不同 UID 的)应用发送的广播,
|
||||
* 与改造前两参 {@code registerReceiver} 的默认行为一致。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author LiGuangMin
|
||||
*/
|
||||
public final class ReceiverCompat {
|
||||
|
||||
private static final String TAG = "ReceiverCompat";
|
||||
|
||||
/**
|
||||
* 对应 Context.RECEIVER_EXPORTED(API 33)。
|
||||
*/
|
||||
public static final int RECEIVER_EXPORTED = 0x00000002;
|
||||
|
||||
/**
|
||||
* 对应 Context.RECEIVER_NOT_EXPORTED(API 33)。
|
||||
*/
|
||||
public static final int RECEIVER_NOT_EXPORTED = 0x00000004;
|
||||
|
||||
/**
|
||||
* 带导出标志的注册重载从 API 33 开始对应用生效。
|
||||
* 此处不使用 {@code Build.VERSION_CODES.TIRAMISU},因为 framework.jar 只到 API 29。
|
||||
*/
|
||||
private static final int FLAGS_API = 33;
|
||||
|
||||
private ReceiverCompat() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容注册,等价于 {@code registerReceiver(receiver, filter)},
|
||||
* 但在 API 33+ 上显式带上导出标志。
|
||||
*/
|
||||
public static Intent registerReceiver(Context context, BroadcastReceiver receiver,
|
||||
IntentFilter filter, int flags) {
|
||||
return registerReceiver(context, receiver, filter, flags, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全注销广播接收器。
|
||||
*
|
||||
* <p>系统自带的 {@code unregisterReceiver()} 在"接收器未注册过"或"重复注销"时会抛出
|
||||
* {@code IllegalArgumentException: Receiver not registered},直接导致进程崩溃;
|
||||
* 而业务代码通常只能用字段是否为 null 来判断,字段非 null 并不代表注册成功
|
||||
* (例如注册过程中抛异常、Service 被销毁等场景)。这里统一做容错处理。
|
||||
*
|
||||
* @param receiver 允许为 null
|
||||
*/
|
||||
public static void unregisterReceiver(Context context, BroadcastReceiver receiver) {
|
||||
if (context == null || receiver == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
context.unregisterReceiver(receiver);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "unregisterReceiver failed: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容注册,等价于
|
||||
* {@code registerReceiver(receiver, filter, broadcastPermission, scheduler)}。
|
||||
*
|
||||
* @param receiver 可以为 null,此时用于查询粘性广播
|
||||
* @param flags {@link #RECEIVER_EXPORTED} 或 {@link #RECEIVER_NOT_EXPORTED}
|
||||
* @param broadcastPermission 发送方必须持有的权限,可为 null
|
||||
*/
|
||||
public static Intent registerReceiver(Context context, BroadcastReceiver receiver,
|
||||
IntentFilter filter, int flags,
|
||||
String broadcastPermission) {
|
||||
if (context == null || filter == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= FLAGS_API) {
|
||||
return context.registerReceiver(receiver, filter, broadcastPermission, null, flags);
|
||||
}
|
||||
//Android 13 以下不支持导出标志,保持原有注册方式
|
||||
return context.registerReceiver(receiver, filter, broadcastPermission, null);
|
||||
} catch (Exception e) {
|
||||
//兜底:极端情况下(例如厂商裁剪了重载)退回到传统注册,避免直接崩溃
|
||||
Log.e(TAG, "registerReceiver failed, fallback to legacy: " + e);
|
||||
try {
|
||||
return context.registerReceiver(receiver, filter);
|
||||
} catch (Exception inner) {
|
||||
Log.e(TAG, "registerReceiver legacy failed: " + inner);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
package com.aoleyun.sn.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.util.Base64;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.aoleyun.sn.comm.CommonConfig;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
public class SPUtils {
|
||||
/**
|
||||
* 保存在手机里面的文件名
|
||||
*/
|
||||
public static final String FILE_NAME = "share_data";
|
||||
|
||||
/**
|
||||
* 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
|
||||
*/
|
||||
public static void put(Context context, String key, Object object) {
|
||||
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
|
||||
if (object instanceof String) {
|
||||
editor.putString(key, (String) object);
|
||||
} else if (object instanceof Integer) {
|
||||
editor.putInt(key, (Integer) object);
|
||||
} else if (object instanceof Boolean) {
|
||||
editor.putBoolean(key, (Boolean) object);
|
||||
} else if (object instanceof Float) {
|
||||
editor.putFloat(key, (Float) object);
|
||||
} else if (object instanceof Long) {
|
||||
editor.putLong(key, (Long) object);
|
||||
} else {
|
||||
editor.putString(key, object.toString());
|
||||
}
|
||||
|
||||
SharedPreferencesCompat.apply(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
|
||||
*/
|
||||
public static Object get(Context context, String key, Object defaultObject) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
|
||||
if (defaultObject instanceof String) {
|
||||
return sp.getString(key, (String) defaultObject);
|
||||
} else if (defaultObject instanceof Integer) {
|
||||
return sp.getInt(key, (Integer) defaultObject);
|
||||
} else if (defaultObject instanceof Boolean) {
|
||||
return sp.getBoolean(key, (Boolean) defaultObject);
|
||||
} else if (defaultObject instanceof Float) {
|
||||
return sp.getFloat(key, (Float) defaultObject);
|
||||
} else if (defaultObject instanceof Long) {
|
||||
return sp.getLong(key, (Long) defaultObject);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除某个key值已经对应的值
|
||||
*/
|
||||
public static void remove(Context context, String key) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.remove(key);
|
||||
SharedPreferencesCompat.apply(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有数据
|
||||
*/
|
||||
public static void clear(Context context) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.clear();
|
||||
SharedPreferencesCompat.apply(editor);
|
||||
reductFirstEnter(context);
|
||||
}
|
||||
|
||||
//还原状态firstEnter信息
|
||||
private static void reductFirstEnter(Context context) {
|
||||
put(context, CommonConfig.FLAG_FIRST_ENTER, CommonConfig.FLAG_FIRST_ENTER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询某个key是否已经存在
|
||||
*/
|
||||
public static boolean contains(Context context, String key) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
return sp.contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有的键值对
|
||||
*/
|
||||
public static Map<String, ?> getAll(Context context) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
return sp.getAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存图片到SharedPreferences
|
||||
*
|
||||
* @param mContext
|
||||
* @param imageView
|
||||
*/
|
||||
public static void putImage(Context mContext, String key, ImageView imageView) {
|
||||
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
|
||||
Bitmap bitmap = drawable.getBitmap();
|
||||
// 将Bitmap压缩成字节数组输出流
|
||||
ByteArrayOutputStream byStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 80, byStream);
|
||||
// 利用Base64将我们的字节数组输出流转换成String
|
||||
byte[] byteArray = byStream.toByteArray();
|
||||
String imgString = new String(Base64.encodeToString(byteArray, Base64.DEFAULT));
|
||||
// 将String保存shareUtils
|
||||
SPUtils.put(mContext, key, imgString);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从SharedPreferences读取图片
|
||||
*
|
||||
* @param mContext
|
||||
* @param imageView
|
||||
*/
|
||||
public static Bitmap getImage(Context mContext, String key, ImageView imageView) {
|
||||
String imgString = (String) SPUtils.get(mContext, key, "");
|
||||
if (!imgString.equals("")) {
|
||||
// 利用Base64将我们string转换
|
||||
byte[] byteArray = Base64.decode(imgString, Base64.DEFAULT);
|
||||
ByteArrayInputStream byStream = new ByteArrayInputStream(byteArray);
|
||||
// 生成bitmap
|
||||
return BitmapFactory.decodeStream(byStream);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
|
||||
*/
|
||||
private static class SharedPreferencesCompat {
|
||||
private static final Method sApplyMethod = findApplyMethod();
|
||||
|
||||
/**
|
||||
* 反射查找apply的方法
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static Method findApplyMethod() {
|
||||
try {
|
||||
Class clz = SharedPreferences.Editor.class;
|
||||
return clz.getMethod("apply");
|
||||
} catch (NoSuchMethodException e) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果找到则使用apply执行,否则使用commit
|
||||
*/
|
||||
public static void apply(SharedPreferences.Editor editor) {
|
||||
try {
|
||||
if (sApplyMethod != null) {
|
||||
sApplyMethod.invoke(editor);
|
||||
return;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (InvocationTargetException e) {
|
||||
}
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,8 @@ import java.util.List;
|
||||
public class SysSettingUtils {
|
||||
private static final String TAG = "SysSettingUtils";
|
||||
|
||||
private static final MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
public SysSettingUtils() {
|
||||
|
||||
}
|
||||
@@ -65,7 +67,7 @@ public class SysSettingUtils {
|
||||
setAutoTime(context, jsonObject);
|
||||
String navJsonString = jsonObject.get("setting_nav").getAsString();
|
||||
JsonObject navJson = JsonParser.parseString(navJsonString).getAsJsonObject();
|
||||
SPUtils.put(context, JGYActions.ACTION_STATUSBAR_STATUS, navJsonString);
|
||||
mMMKV.encode(JGYActions.ACTION_STATUSBAR_STATUS, navJsonString);
|
||||
setStatusBar(context, navJson);
|
||||
setBrowserInput(context, jsonObject);
|
||||
// TODO: 2022/4/11 不是酷比定制的会报错,无法抛出异常
|
||||
@@ -113,6 +115,9 @@ public class SysSettingUtils {
|
||||
setNotification(context, 0);
|
||||
setSettingsMenuShow(context);
|
||||
setEyeProtectionModeEntrance(context, 1);
|
||||
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.DX11Platform) {
|
||||
Settings.System.putInt(context.getContentResolver(), CommonConfig.AOLE_ACTION_DEVELOPER_OPTIONS, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -501,7 +506,7 @@ public class SysSettingUtils {
|
||||
AllwinnerCubeMdmManager.getInstance().setBluetoothEnable(setting_bht == 0);
|
||||
}
|
||||
|
||||
int bt_rename = changeNum(jsonObject.get("bluetooth_name_change").getAsInt());
|
||||
int bt_rename = jsonObject.get("bluetooth_name_change").getAsInt();
|
||||
Settings.System.putInt(context.getContentResolver(), CommonConfig.AOLE_ACTION_BT_RENAME_ALLOW, bt_rename);
|
||||
|
||||
}
|
||||
@@ -1347,11 +1352,13 @@ public class SysSettingUtils {
|
||||
if (power_saving_mode == 0) {
|
||||
menus.add("battery");
|
||||
}
|
||||
// // 安全 (默认允许)
|
||||
// int security = jsonObject.get("security").getAsInt();
|
||||
// if (security == 0) {
|
||||
// menus.add("security");
|
||||
// }
|
||||
|
||||
// 安全 (默认允许)
|
||||
int security = jsonObject.get("security").getAsInt();
|
||||
if (security == 0) {
|
||||
menus.add("security");
|
||||
}
|
||||
|
||||
// // 大屏特色功能 (默认允许)
|
||||
// int large_screen_features = jsonObject.get("large_screen_features").getAsInt();
|
||||
// if (large_screen_features == 0) {
|
||||
@@ -1383,10 +1390,11 @@ public class SysSettingUtils {
|
||||
menus.add("apps");
|
||||
}
|
||||
// // 荣耀账号登录 (默认禁止)
|
||||
// int honor_account_login = jsonObject.get("honor_account_login").getAsInt();
|
||||
// if (honor_account_login == 0) {
|
||||
int honor_account_login = jsonObject.get("honor_account_login").getAsInt();
|
||||
if (honor_account_login == 0) {
|
||||
// menus.add("honor_id");
|
||||
// }
|
||||
menus.add("account_id");
|
||||
}
|
||||
// // 智慧互联 (默认禁止)
|
||||
// int smart_interconnect = jsonObject.get("smart_interconnect").getAsInt();
|
||||
// if (smart_interconnect == 0) {
|
||||
@@ -1412,12 +1420,21 @@ public class SysSettingUtils {
|
||||
// if (app_startup_management == 0) {
|
||||
// menus.add("apps_startup_management");
|
||||
// }
|
||||
|
||||
// 应用管理 (默认禁止)
|
||||
// int app_management_control = jsonObject.get("app_management_control").getAsInt();
|
||||
// if (app_management_control == 0) {
|
||||
int app_management_control = jsonObject.get("app_management_control").getAsInt();
|
||||
if (app_management_control == 0) {
|
||||
// menus.add("apps_management");
|
||||
// menus.add("apps_honor_tips");
|
||||
// }
|
||||
menus.add("apps");
|
||||
}
|
||||
|
||||
// 儿童 (默认禁止)
|
||||
int children_mode = jsonObject.get("children_mode").getAsInt();
|
||||
if (children_mode == 0) {
|
||||
menus.add("kids_mode");
|
||||
}
|
||||
|
||||
Log.e(TAG, "setSettingsMenuShow: " + menus);
|
||||
Settings.System.putString(context.getContentResolver(), CommonConfig.AOLE_SETTINGS_DISALLOW, String.join(",", menus));
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.aoleyun.sn.bean.PoweroffBean;
|
||||
import com.aoleyun.sn.comm.CommonConfig;
|
||||
import com.aoleyun.sn.service.main.MainService;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
@@ -37,6 +39,8 @@ public class TimeUtils {
|
||||
public static long dayTime = 60 * 60 * 24 * 1000;
|
||||
public static long minuteTime = 60 * 1000;
|
||||
|
||||
private static final MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
public static String getNowTime() {
|
||||
long nowTime = System.currentTimeMillis();
|
||||
DateFormat df = ContralTime.getDf();
|
||||
@@ -183,8 +187,8 @@ public class TimeUtils {
|
||||
throw new RuntimeException("Time format error!");
|
||||
}
|
||||
try {
|
||||
SPUtils.put(context, START_TIME_KEY, time[0].trim());
|
||||
SPUtils.put(context, END_TIME_KEY, time[1].trim());
|
||||
mMMKV.encode(START_TIME_KEY, time[0].trim());
|
||||
mMMKV.encode(END_TIME_KEY, time[1].trim());
|
||||
Date startDate = df.parse(time[0].trim());
|
||||
Date endDate = df.parse(time[1].trim());
|
||||
ContralTime contralTime = new ContralTime();
|
||||
@@ -203,8 +207,8 @@ public class TimeUtils {
|
||||
}
|
||||
|
||||
public static ContralTime getDefaltContralTime(Context context) {
|
||||
String startTime = (String) SPUtils.get(context, START_TIME_KEY, "00:00");
|
||||
String endTime = (String) SPUtils.get(context, END_TIME_KEY, "00:00");
|
||||
String startTime = mMMKV.decodeString(START_TIME_KEY, "00:00");
|
||||
String endTime = mMMKV.decodeString(END_TIME_KEY, "00:00");
|
||||
if (null == startTime || null == endTime || ("00:00".equals(startTime) && "00:00".equals(endTime))) {
|
||||
return null;
|
||||
} else {
|
||||
@@ -223,8 +227,8 @@ public class TimeUtils {
|
||||
}
|
||||
|
||||
public static void setEmpty(Context context) {
|
||||
SPUtils.put(context, START_TIME_KEY, "00:00");
|
||||
SPUtils.put(context, END_TIME_KEY, "00:00");
|
||||
mMMKV.encode(START_TIME_KEY, "00:00");
|
||||
mMMKV.encode(END_TIME_KEY, "00:00");
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(MainService.TimeChangedReceiver.ACTION_UPDATE);
|
||||
context.sendBroadcast(intent);
|
||||
|
||||
@@ -7,6 +7,9 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebSettings;
|
||||
|
||||
import com.aoleyun.sn.comm.CommonConfig;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
@@ -37,6 +40,8 @@ public class URLUtils {
|
||||
private long time1;
|
||||
private long time2;
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
public URLUtils(Context context) {
|
||||
this.mContext = context;
|
||||
}
|
||||
@@ -45,12 +50,12 @@ public class URLUtils {
|
||||
time1 = System.currentTimeMillis();
|
||||
//会写入两次,第一次是不带前缀的,第二次会把实际的地址写入进去
|
||||
String oldwhiteList = Settings.System.getString(mContext.getContentResolver(), "DeselectBrowserArray");
|
||||
String oldWhiteIP = (String) SPUtils.get(mContext, "white_ip", "");
|
||||
String oldWhiteIP = mMMKV.decodeString("white_ip", "");
|
||||
if (TextUtils.isEmpty(oldwhiteList) && TextUtils.isEmpty(oldWhiteIP)) {
|
||||
Log.e(TAG, "getBrowserWhiteList: " + "oldwhiteList is empty");
|
||||
Settings.System.putString(mContext.getContentResolver(), "DeselectBrowserArray", "Invalid");
|
||||
} else {
|
||||
String white_ip = (String) SPUtils.get(mContext, "white_ip", "");
|
||||
String white_ip = mMMKV.decodeString("white_ip", "");
|
||||
if (!TextUtils.isEmpty(white_ip)) {
|
||||
whiteIPList = new HashSet<>(Arrays.asList(white_ip.split(",")));
|
||||
}
|
||||
@@ -221,7 +226,7 @@ public class URLUtils {
|
||||
if (TextUtils.isEmpty(qch_webblack_url)) {
|
||||
Settings.System.putString(mContext.getContentResolver(), "qch_webblack_url", "Invalid");
|
||||
} else {
|
||||
String black_ip = (String) SPUtils.get(mContext, "black_ip", "");
|
||||
String black_ip = mMMKV.decodeString("black_ip", "");
|
||||
if (!TextUtils.isEmpty(black_ip)) {
|
||||
blackIPList = new HashSet<>(Arrays.asList(black_ip.split(",")));
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
@@ -59,6 +58,7 @@ import com.aoleyun.sn.comm.CommonConfig;
|
||||
import com.aoleyun.sn.manager.AllwinnerCubeMdmManager;
|
||||
import com.arialyy.aria.core.Aria;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
@@ -67,6 +67,7 @@ import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
@@ -115,6 +116,8 @@ import vendor.mediatek.hardware.nvram.V1_0.INvram;
|
||||
public class Utils {
|
||||
private static final String TAG = "Utils";
|
||||
|
||||
private static final MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
// MD5 设备地址标识
|
||||
public static String getMAC(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 如果当前设备系统大于等于6.0 使用下面的方法
|
||||
@@ -597,22 +600,6 @@ public class Utils {
|
||||
return intent;
|
||||
}
|
||||
|
||||
// 设置系统亮度模式
|
||||
public static void systemBrightness(Context context) {
|
||||
try {
|
||||
SharedPreferences mPrefs = context.getSharedPreferences("colorflykids", 0);
|
||||
boolean initSetting = mPrefs.getBoolean("init_setting", true);
|
||||
if (initSetting) {
|
||||
mPrefs.edit().putBoolean("init_setting", false).commit();
|
||||
}
|
||||
Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, 0);
|
||||
mPrefs.edit().putBoolean("first_init", false).commit();
|
||||
|
||||
} catch (Exception err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 版本比较
|
||||
public static boolean isUpdate(String oldVersion, String newVersion) {
|
||||
if (TextUtils.isEmpty(oldVersion) || TextUtils.isEmpty(newVersion) || oldVersion.equals(newVersion)) {
|
||||
@@ -1205,47 +1192,57 @@ public class Utils {
|
||||
*/
|
||||
public static void ariaDownload(Context context, String url, JsonObject jsonObject) {
|
||||
Log.e(TAG, "ariaDownload: " + jsonObject);
|
||||
String fileName = getFileNamefromURL(url);
|
||||
String urlMD5 = "";
|
||||
if (jsonObject.get("MD5") == null) {
|
||||
if (jsonObject.get("app_md5") != null) {
|
||||
urlMD5 = jsonObject.get("app_md5").getAsString();
|
||||
}
|
||||
} else {
|
||||
if (jsonObject.get("MD5") != null) {
|
||||
if (!jsonObject.get("MD5").isJsonNull()) {
|
||||
urlMD5 = jsonObject.get("MD5").getAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
final String fileName = getFileNamefromURL(url);
|
||||
final String urlMD5 = resolveUrlMD5(jsonObject);
|
||||
Log.e("ariaDownload", "urlMD5=" + urlMD5);
|
||||
String p = JgyUtils.getInstance().getDownLoadPath();
|
||||
File file = new File(JgyUtils.getInstance().getDownLoadPath() + fileName);
|
||||
if (file.exists() && !file.isDirectory()) {
|
||||
String filenameMD5 = getMD5fromFileName(url);
|
||||
String fileMD5 = FileUtils.getFileMD5ToString(file);
|
||||
Log.e("ariaDownload", "filenameMD5=" + filenameMD5);
|
||||
Log.e("ariaDownload", "fileMD5=" + fileMD5);
|
||||
//后端还没有完成json字段的MD5,暂时用文件名获取
|
||||
if (fileMD5.equalsIgnoreCase(urlMD5)) {
|
||||
ApkUtils.installApp(context, file.getAbsolutePath());
|
||||
} else {
|
||||
Aria.download(context)
|
||||
.load(url) //读取下载地址
|
||||
.setFilePath(JgyUtils.getInstance().getDownLoadPath() + fileName)
|
||||
.ignoreFilePathOccupy()
|
||||
.setExtendField(jsonObject.toString())
|
||||
.create(); //启动下载}
|
||||
final File file = new File(JgyUtils.getInstance().getDownLoadPath() + fileName);
|
||||
// 校验安装包 MD5 需要完整读取文件,属于耗时 IO;安装 APK 也可能抛异常,
|
||||
// 统一放到子线程执行,避免阻塞主线程(历史上这里曾导致主线程闪退/ANR)
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if (file.exists() && !file.isDirectory()) {
|
||||
String filenameMD5 = getMD5fromFileName(url);
|
||||
String fileMD5 = FileUtils.getFileMD5ToString(file);
|
||||
Log.e("ariaDownload", "filenameMD5=" + filenameMD5);
|
||||
Log.e("ariaDownload", "fileMD5=" + fileMD5);
|
||||
//后端还没有完成json字段的MD5,暂时用文件名获取
|
||||
if (fileMD5.equalsIgnoreCase(urlMD5)) {
|
||||
ApkUtils.installApp(context, file.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.e("ariaDownload", "检查安装包异常: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
startAriaDownload(context, url, fileName, jsonObject);
|
||||
}, "apk-install-check").start();
|
||||
}
|
||||
|
||||
Aria.download(context)
|
||||
.load(url) //读取下载地址
|
||||
.setFilePath(JgyUtils.getInstance().getDownLoadPath() + fileName)
|
||||
.ignoreFilePathOccupy()
|
||||
.setExtendField(jsonObject.toString())
|
||||
.create(); //启动下载}
|
||||
/**
|
||||
* 解析下载 JSON 中的 MD5 字段,兼容 MD5 / app_md5 两种命名。
|
||||
*/
|
||||
private static String resolveUrlMD5(JsonObject jsonObject) {
|
||||
String urlMD5 = "";
|
||||
JsonElement md5Element = jsonObject.get("MD5");
|
||||
if (md5Element == null || md5Element.isJsonNull()) {
|
||||
md5Element = jsonObject.get("app_md5");
|
||||
}
|
||||
if (md5Element != null && !md5Element.isJsonNull()) {
|
||||
urlMD5 = md5Element.getAsString();
|
||||
}
|
||||
return urlMD5;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交 Aria 下载任务。
|
||||
*/
|
||||
private static void startAriaDownload(Context context, String url, String fileName, JsonObject jsonObject) {
|
||||
Aria.download(context)
|
||||
.load(url) //读取下载地址
|
||||
.setFilePath(JgyUtils.getInstance().getDownLoadPath() + fileName)
|
||||
.ignoreFilePathOccupy()
|
||||
.setExtendField(jsonObject.toString())
|
||||
.create(); //启动下载}
|
||||
}
|
||||
|
||||
private void showToast() {
|
||||
@@ -1485,7 +1482,7 @@ public class Utils {
|
||||
jsonObject.addProperty("rom", rom);
|
||||
jsonObject.addProperty("screen_rate", screen_rate);
|
||||
jsonObject.addProperty("working_time", SystemClock.elapsedRealtime() / 1000);
|
||||
jsonObject.addProperty("restart_count", (int) SPUtils.get(context, "restart_count", 0));
|
||||
jsonObject.addProperty("restart_count", mMMKV.decodeInt("restart_count", 0));
|
||||
jsonObject.addProperty("storage_usable", getDataTotalSizeGb(context));
|
||||
jsonObject.addProperty("storage_total", getDataTotalSizeGb(context));
|
||||
String jsonString = jsonObject.toString();
|
||||
@@ -1517,12 +1514,12 @@ public class Utils {
|
||||
}
|
||||
|
||||
public static String getAddressJsonString(Context context) {
|
||||
String address = String.valueOf(SPUtils.get(context, "MapAddress", "-"));
|
||||
String address = String.valueOf(mMMKV.decodeString("MapAddress", "-"));
|
||||
if ("-".equals(address)) {
|
||||
address = "定位失败";
|
||||
}
|
||||
String longitude = String.valueOf(SPUtils.get(context, "longitude", "0"));
|
||||
String latitude = String.valueOf(SPUtils.get(context, "latitude", "0"));
|
||||
String longitude = String.valueOf(mMMKV.decodeString("longitude", "0"));
|
||||
String latitude = String.valueOf(mMMKV.decodeString("latitude", "0"));
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("address", address);
|
||||
jsonObject.addProperty("longitude", longitude);
|
||||
@@ -1616,7 +1613,8 @@ public class Utils {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
return ((BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE)).getIntProperty(4);
|
||||
} else {
|
||||
Intent intent = (new ContextWrapper(mContext)).registerReceiver(null, new IntentFilter("android.intent.action.BATTERY_CHANGED"));
|
||||
Intent intent = ReceiverCompat.registerReceiver(new ContextWrapper(mContext), null,
|
||||
new IntentFilter("android.intent.action.BATTERY_CHANGED"), ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
return intent.getIntExtra("level", -1) * 100 / intent.getIntExtra("scale", -1);
|
||||
}
|
||||
}
|
||||
@@ -1624,7 +1622,7 @@ public class Utils {
|
||||
public static int getBatteryPercentage(Context context) {
|
||||
|
||||
IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
||||
Intent batteryStatus = context.registerReceiver(null, iFilter);
|
||||
Intent batteryStatus = ReceiverCompat.registerReceiver(context, null, iFilter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
|
||||
int level = batteryStatus != null ? batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) : -1;
|
||||
int scale = batteryStatus != null ? batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1) : -1;
|
||||
@@ -1636,7 +1634,7 @@ public class Utils {
|
||||
|
||||
public static int getIsCharging(Context context) {
|
||||
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
||||
Intent batteryStatus = context.registerReceiver(null, ifilter);
|
||||
Intent batteryStatus = ReceiverCompat.registerReceiver(context, null, ifilter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
|
||||
// Are we charging / charged?
|
||||
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
||||
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
|
||||
|
||||
@@ -323,40 +323,81 @@ public class XAPKUtils {
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public static void installAppatPie(Context context, List<String> apkFilePath) {
|
||||
Log.e(TAG, "installApps: 正在安装应用 " + apkFilePath);
|
||||
try {
|
||||
Log.e(TAG, "installApps: 正在安装应用 " + apkFilePath);
|
||||
// File file = new File(apkFilePath);
|
||||
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
|
||||
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
|
||||
.SessionParams.MODE_FULL_INSTALL);
|
||||
sessionParams.setSize(XAPKSize);
|
||||
int sessionId = createSession(packageInstaller, sessionParams);
|
||||
if (sessionId != -1) {
|
||||
for (String apkPath : apkFilePath) {
|
||||
copyApkFile(packageInstaller, sessionId, apkPath);
|
||||
}
|
||||
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
|
||||
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
|
||||
.SessionParams.MODE_FULL_INSTALL);
|
||||
sessionParams.setSize(XAPKSize);
|
||||
int sessionId = createSession(packageInstaller, sessionParams);
|
||||
if (sessionId != -1) {
|
||||
for (String apkPath : apkFilePath) {
|
||||
copyApkFile(packageInstaller, sessionId, apkPath);
|
||||
}
|
||||
// boolean copySuccess = ;
|
||||
// if (copySuccess) {
|
||||
Toaster.show("正在安装应用");
|
||||
install(packageInstaller, sessionId, context);
|
||||
Toaster.show("正在安装应用");
|
||||
install(packageInstaller, sessionId, context);
|
||||
// }
|
||||
//
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// 安装失败只记录日志,避免异常冒泡导致进程闪退
|
||||
Log.e(TAG, "installAppatPie: 安装异常 " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
private static void install(PackageInstaller packageInstaller, int sessionId, Context context) {
|
||||
PackageInstaller.Session session = null;
|
||||
try {
|
||||
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
|
||||
Intent intent = new Intent(context, InstallResultReceiver.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
1, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
session.commit(pendingIntent.getIntentSender());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "install: " + e.getMessage());
|
||||
session = packageInstaller.openSession(sessionId);
|
||||
session.commit(createResultPendingIntent(context).getIntentSender());
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "install: 安装失败 " + e.getMessage(), e);
|
||||
abandonSession(packageInstaller, sessionId);
|
||||
} finally {
|
||||
if (session != null) {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 14(targetSdk 34+)起,PackageInstaller 的 commit 结果接收器必须是可变
|
||||
* (Mutable)PendingIntent,系统需要向其中回填安装结果,否则 commit() 会抛出
|
||||
* IllegalArgumentException 并导致进程闪退。
|
||||
*/
|
||||
private static PendingIntent createResultPendingIntent(Context context) {
|
||||
Intent intent = new Intent(context, InstallResultReceiver.class);
|
||||
intent.setPackage(context.getPackageName());
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_12) {
|
||||
flags |= FLAG_MUTABLE;
|
||||
}
|
||||
return PendingIntent.getBroadcast(context, 1, intent, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 12(API 31)。编译使用的 framework.jar 为设备提取版,不含 API 31 新增符号,
|
||||
* 因此这里使用字面量常量代替 {@code Build.VERSION_CODES.S} 与 {@code PendingIntent.FLAG_MUTABLE}。
|
||||
*/
|
||||
private static final int ANDROID_12 = 31;
|
||||
/**
|
||||
* PendingIntent.FLAG_MUTABLE(API 31 新增,值为 1 << 25)。
|
||||
*/
|
||||
private static final int FLAG_MUTABLE = 1 << 25;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
private static void abandonSession(PackageInstaller packageInstaller, int sessionId) {
|
||||
try {
|
||||
packageInstaller.abandonSession(sessionId);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "abandonSession: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user