From 4217eb7f8b0c328e386151a852687aad3bd0397a Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Tue, 2 Jun 2026 15:52:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9F=BA=E6=9C=AC=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E6=B2=A1=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在自动返回到桌面时,后端说有接口没有传上去,待修复。 准备升级Android Studio版本 --- app/build.gradle | 30 +- app/src/main/AndroidManifest.xml | 36 +- .../fuying/sn/activity/home/HomeActivity.java | 14 + .../com/fuying/sn/base/BaseApplication.java | 40 +- .../fuying/sn/bean/AppTimeControlInfo.java | 95 ++ .../com/fuying/sn/bean/SnTimeControlInfo.java | 133 +++ .../com/fuying/sn/config/CommonConfig.java | 4 +- .../fuying/sn/desktop/RunningAppManager.java | 21 +- .../fuying/sn/desktop/TimeControlManager.java | 19 +- .../sn/fragment/device/DeviceFragment.java | 1 + .../com/fuying/sn/manager/ControlManager.java | 123 +-- .../com/fuying/sn/manager/DeviceManager.java | 3 +- .../time/FuyingActivityController.java | 55 ++ .../fuying/sn/manager/time/TimeManager.java | 885 ++++++++++++++++++ .../sn/network/NetInterfaceManager.java | 148 ++- .../com/fuying/sn/network/UrlAddress.java | 2 +- .../sn/network/api/newly/ControlApi.java | 8 +- .../java/com/fuying/sn/push/PushManager.java | 4 + .../com/fuying/sn/service/ManagerService.java | 21 +- .../com/fuying/sn/service/RemoteService.java | 22 +- .../sn/service/main/MainSPresenter.java | 7 +- .../fuying/sn/service/main/MainService.java | 4 +- .../java/com/fuying/sn/utils/ApkUtils.java | 14 + .../main/java/com/fuying/sn/utils/Utils.java | 2 +- build.gradle | 2 + 25 files changed, 1538 insertions(+), 155 deletions(-) create mode 100644 app/src/main/java/com/fuying/sn/bean/AppTimeControlInfo.java create mode 100644 app/src/main/java/com/fuying/sn/bean/SnTimeControlInfo.java create mode 100644 app/src/main/java/com/fuying/sn/manager/time/FuyingActivityController.java create mode 100644 app/src/main/java/com/fuying/sn/manager/time/TimeManager.java diff --git a/app/build.gradle b/app/build.gradle index b19725c..7364729 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,7 +27,6 @@ android { buildToolsVersion "29.0.3" defaultConfig { - applicationId "com.fuying.sn" minSdkVersion 24 targetSdkVersion 29 @@ -59,26 +58,31 @@ android { enabled true } - manifestPlaceholders = [ - XG_ACCESS_ID : "1500031216", - XG_ACCESS_KEY: "A1HBG2922B9Z", - ] - buildConfigField "String", "SCREEN_URL", '"https://as.fuyingy.com:3018/wm/is_online"' - buildConfigField "String", "WEBSOCKET_URL", '"wss://47.111.23.154:3018"' } //多版本 productFlavors { -// beta { -// flavorDimensions "default" -// versionCode 33 -// versionName "4.2" -// } + beta { + flavorDimensions "default" + versionCode 89 + versionName "4.3" +// applicationId "com.fuying.sn" + applicationId "com.fying.sntest" + + buildConfigField "String", "ROOT_URL", '"https://fxyapi.17hxg.com/android/"' + buildConfigField "String", "SCREEN_URL", '"https://fxyapi.17hxg.com:3018/wm/is_online"' +// buildConfigField "String", "WEBSOCKET_URL", '"wss://47.111.23.154:3018"' + } official { flavorDimensions "default" versionCode 88 versionName "3.4.8" + applicationId "com.fuying.sn" + + buildConfigField "String", "ROOT_URL", '"https://as.fuyingy.com/android/"' + buildConfigField "String", "SCREEN_URL", '"https://as.fuyingy.com:3018/wm/is_online"' +// buildConfigField "String", "WEBSOCKET_URL", '"wss://47.111.23.154:3018"' } } @@ -555,7 +559,7 @@ dependencies { // //腾讯移动推送 TPNS // implementation 'com.tencent.tpns:tpns:1.4.4.2-release' //阿里云推送 - implementation 'com.aliyun.ams:alicloud-android-push:3.8.0' + implementation 'com.aliyun.ams:alicloud-android-push:3.9.3' // //百度地图 // implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.1.8' //高德地图定位 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a066575..31ef4eb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,8 @@ + + @@ -46,7 +48,7 @@ - + @@ -239,19 +241,19 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -267,10 +269,12 @@ + android:value="335685739" /> + + android:value="713e04b35a6249fc979e3d1f74ad59fd" /> + \ No newline at end of file diff --git a/app/src/main/java/com/fuying/sn/activity/home/HomeActivity.java b/app/src/main/java/com/fuying/sn/activity/home/HomeActivity.java index cda04e7..1bc77ef 100644 --- a/app/src/main/java/com/fuying/sn/activity/home/HomeActivity.java +++ b/app/src/main/java/com/fuying/sn/activity/home/HomeActivity.java @@ -2,6 +2,7 @@ package com.fuying.sn.activity.home; import android.content.Intent; import android.os.SystemClock; +import android.provider.Settings; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; @@ -13,6 +14,7 @@ import androidx.fragment.app.FragmentManager; import androidx.viewpager.widget.ViewPager; import com.blankj.utilcode.util.NetworkUtils; +import com.fuying.sn.BuildConfig; import com.fuying.sn.R; import com.fuying.sn.base.mvvm.BaseMvvmActivity; import com.fuying.sn.bean.BaseResponse; @@ -24,6 +26,7 @@ import com.fuying.sn.fragment.BaseFragmentPagerAdapter; import com.fuying.sn.fragment.device.DeviceFragment; import com.fuying.sn.manager.ControlManager; import com.fuying.sn.manager.DeviceManager; +import com.fuying.sn.manager.time.TimeManager; import com.fuying.sn.network.NetInterfaceManager; import com.fuying.sn.utils.SPUtils; import com.fuying.sn.utils.ToastUtil; @@ -87,6 +90,12 @@ public class HomeActivity extends BaseMvvmActivity>() { @Override public void onChanged(BaseResponse snInfoBaseResponse) { @@ -186,6 +195,11 @@ public class HomeActivity extends BaseMvvmActivity time_part; + long today_time; + + public String getApp_name() { + return app_name; + } + + public void setApp_name(String app_name) { + this.app_name = app_name; + } + + public String getApp_package() { + return app_package; + } + + public void setApp_package(String app_package) { + this.app_package = app_package; + } + + public int getControl_type() { + return control_type; + } + + public void setControl_type(int control_type) { + this.control_type = control_type; + } + + public int getIs_control() { + return is_control; + } + + public void setIs_control(int is_control) { + this.is_control = is_control; + } + + public long getWork_time() { + return work_time; + } + + public void setWork_time(long work_time) { + this.work_time = work_time; + } + + public long getRest_time() { + return rest_time; + } + + public void setRest_time(long rest_time) { + this.rest_time = rest_time; + } + + public List getTime_part() { + return time_part; + } + + public void setTime_part(List time_part) { + this.time_part = time_part; + } + + public long getToday_time() { + return today_time; + } + + public void setToday_time(long today_time) { + this.today_time = today_time; + } + + @NonNull + @Override + public String toString() { + return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString(); + } +} diff --git a/app/src/main/java/com/fuying/sn/bean/SnTimeControlInfo.java b/app/src/main/java/com/fuying/sn/bean/SnTimeControlInfo.java new file mode 100644 index 0000000..8c6e21d --- /dev/null +++ b/app/src/main/java/com/fuying/sn/bean/SnTimeControlInfo.java @@ -0,0 +1,133 @@ +package com.fuying.sn.bean; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.JsonParser; + +import java.io.Serializable; +import java.util.List; + +public class SnTimeControlInfo implements Serializable { + private static final long serialVersionUID = -3814696589100564944L; + + int id; + /** + * 0 不限制,1 限制时长,2限制时间段 + */ + int control_type; + /**/ + int is_quota; + int is_part; + int work_time; + int rest_time; + int part_type; + int today_time; + int today_time_update; + List time_part; + /*今日使用时间*/ + int sn_use_time_part; + int is_set; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getControl_type() { + return control_type; + } + + public void setControl_type(int control_type) { + this.control_type = control_type; + } + + public int getIs_quota() { + return is_quota; + } + + public void setIs_quota(int is_quota) { + this.is_quota = is_quota; + } + + public int getIs_part() { + return is_part; + } + + public void setIs_part(int is_part) { + this.is_part = is_part; + } + + public int getWork_time() { + return work_time; + } + + public void setWork_time(int work_time) { + this.work_time = work_time; + } + + public int getRest_time() { + return rest_time; + } + + public void setRest_time(int rest_time) { + this.rest_time = rest_time; + } + + public int getPart_type() { + return part_type; + } + + public void setPart_type(int part_type) { + this.part_type = part_type; + } + + public int getToday_time() { + return today_time; + } + + public void setToday_time(int today_time) { + this.today_time = today_time; + } + + public int getToday_time_update() { + return today_time_update; + } + + public void setToday_time_update(int today_time_update) { + this.today_time_update = today_time_update; + } + + public List getTime_part() { + return time_part; + } + + public void setTime_part(List time_part) { + this.time_part = time_part; + } + + public int getSn_use_time_part() { + return sn_use_time_part; + } + + public void setSn_use_time_part(int sn_use_time_part) { + this.sn_use_time_part = sn_use_time_part; + } + + public int getIs_set() { + return is_set; + } + + public void setIs_set(int is_set) { + this.is_set = is_set; + } + + @NonNull + @Override + public String toString() { + return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString(); + } +} diff --git a/app/src/main/java/com/fuying/sn/config/CommonConfig.java b/app/src/main/java/com/fuying/sn/config/CommonConfig.java index f1d75cd..2cb3986 100644 --- a/app/src/main/java/com/fuying/sn/config/CommonConfig.java +++ b/app/src/main/java/com/fuying/sn/config/CommonConfig.java @@ -69,9 +69,9 @@ public class CommonConfig { public static final String AOLE_APP_WEB_WHITE_LIST = "app_web_white_list"; /*内部黑名单*/ public static final String AOLE_APP_WEB_BLACK_LIST = "app_web_black_list"; - /*应用白名单安装总开关*/ - public static final String AOLE_ACTION_APP_FORBID = "aole_app_forbid"; /*应用安装白名单*/ + public static final String AOLE_ACTION_APP_FORBID = "aole_app_forbid"; + /*应用白名单安装总开关*/ public static final String AOLE_APP_ALLOW_INSTALL = "aole_app_allow_install"; /*指定应用安装源 非指定包名不能安装apk*/ public static final String APP_SOURCE_WHITE_LIST = "app_source_white_list"; diff --git a/app/src/main/java/com/fuying/sn/desktop/RunningAppManager.java b/app/src/main/java/com/fuying/sn/desktop/RunningAppManager.java index 4186794..ac9ea71 100644 --- a/app/src/main/java/com/fuying/sn/desktop/RunningAppManager.java +++ b/app/src/main/java/com/fuying/sn/desktop/RunningAppManager.java @@ -75,7 +75,7 @@ public class RunningAppManager { private static final String TAG = "RunningAppManager"; @SuppressLint("StaticFieldLeak") - private static RunningAppManager mRunningAppManager; + private static RunningAppManager sInstance; private Context mContext; private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @@ -149,16 +149,17 @@ public class RunningAppManager { } public static void init(Context context) { - if (mRunningAppManager == null) { - mRunningAppManager = new RunningAppManager(context); + Log.e(TAG, "init: " ); + if (sInstance == null) { + sInstance = new RunningAppManager(context); } } public static RunningAppManager getInstance() { - if (mRunningAppManager == null) { + if (sInstance == null) { throw new IllegalStateException("You must be init RunningAppManager first"); } - return mRunningAppManager; + return sInstance; } /** @@ -221,7 +222,7 @@ public class RunningAppManager { public void onComplete() { Log.i(TAG, "onComplete: "); NetInterfaceManager.getInstance().getAppTimeControl(); - NetInterfaceManager.getInstance().getSnTimeControl(); + NetInterfaceManager.getInstance().getMachineTimeControl(); } }); NetInterfaceManager.getInstance().getMyAppList(new NetInterfaceManager.MyAppListCallback() { @@ -254,7 +255,7 @@ public class RunningAppManager { Log.i(TAG, "checkForegroundAppName: 没有剩余时间1"); // removeTask(topPackage); NetInterfaceManager.getInstance().getAppTimeControl(); - NetInterfaceManager.getInstance().getSnTimeControl(); + NetInterfaceManager.getInstance().getMachineTimeControl(); JgyUtils.getInstance().killPackage(appPackageName); killApp(); gotoLauncher(); @@ -268,7 +269,7 @@ public class RunningAppManager { @Override public void onComplete() { NetInterfaceManager.getInstance().getAppTimeControl(); - NetInterfaceManager.getInstance().getSnTimeControl(); + NetInterfaceManager.getInstance().getMachineTimeControl(); } }); @@ -285,7 +286,7 @@ public class RunningAppManager { JgyUtils.getInstance().killPackage(appPackageName); gotoLauncher(); NetInterfaceManager.getInstance().getAppTimeControl(); - NetInterfaceManager.getInstance().getSnTimeControl(); + NetInterfaceManager.getInstance().getMachineTimeControl(); } else { recordPackageOpenTime(topPackage); Log.i(TAG, "checkForegroundAppName: 没有管控2"); @@ -370,7 +371,7 @@ public class RunningAppManager { mGlobalUsageTime.clear(); mAllAppUsageTime.clear(); NetInterfaceManager.getInstance().getAppTimeControl(); - NetInterfaceManager.getInstance().getSnTimeControl(); + NetInterfaceManager.getInstance().getMachineTimeControl(); } @Override diff --git a/app/src/main/java/com/fuying/sn/desktop/TimeControlManager.java b/app/src/main/java/com/fuying/sn/desktop/TimeControlManager.java index c5a594b..598c9ed 100644 --- a/app/src/main/java/com/fuying/sn/desktop/TimeControlManager.java +++ b/app/src/main/java/com/fuying/sn/desktop/TimeControlManager.java @@ -5,6 +5,7 @@ import android.content.Context; import android.text.TextUtils; import android.util.Log; +import com.fuying.sn.BuildConfig; import com.fuying.sn.disklrucache.CacheHelper; import com.fuying.sn.network.UrlAddress; import com.google.gson.Gson; @@ -20,7 +21,7 @@ public class TimeControlManager { private static final String TAG = "TimeControlManager"; @SuppressLint("StaticFieldLeak") - private static TimeControlManager mTimeControlManager; + private static TimeControlManager sInstance; private Context mContext; private CacheHelper mCacheHelper; @@ -47,16 +48,16 @@ public class TimeControlManager { } public static void init(Context context) { - if (mTimeControlManager == null) { - mTimeControlManager = new TimeControlManager(context); + if (sInstance == null) { + sInstance = new TimeControlManager(context); } } public static TimeControlManager getInstance() { - if (mTimeControlManager == null) { + if (sInstance == null) { throw new IllegalStateException("You must be init TimeControlManager first"); } - return mTimeControlManager; + return sInstance; } public HashMap getAppTimeControlMap() { @@ -111,7 +112,9 @@ public class TimeControlManager { public void setGlobalMachineControl(MachineControl machineControl) { this.mGlobalMachineControl = machineControl; if (machineControl != null) { - RunningAppManager.getInstance().setGlobalUsageTime(machineControl.getToday_time()); + if (BuildConfig.VERSION_CODE < 88) { + RunningAppManager.getInstance().setGlobalUsageTime(machineControl.getToday_time()); + } } } @@ -183,7 +186,9 @@ public class TimeControlManager { appTimeControls.add(appTimeControl); // } } - RunningAppManager.getInstance().syncAppRemainingTime(appTimeControls); + if (BuildConfig.VERSION_CODE < 88) { + RunningAppManager.getInstance().syncAppRemainingTime(appTimeControls); + } this.mAppTimeControlMap = appTimeControlMap; } } diff --git a/app/src/main/java/com/fuying/sn/fragment/device/DeviceFragment.java b/app/src/main/java/com/fuying/sn/fragment/device/DeviceFragment.java index 3969339..ddbc492 100644 --- a/app/src/main/java/com/fuying/sn/fragment/device/DeviceFragment.java +++ b/app/src/main/java/com/fuying/sn/fragment/device/DeviceFragment.java @@ -74,6 +74,7 @@ public class DeviceFragment extends BaseMvvmFragment mStringAppTimeControlInfoMap; + + // 剩余时间 + private long mGlobalRemainingTime; + private String GLOBAL_REMAINING_TIME_KEY = "GLOBAL_REMAINING_TIME_KEY"; + + // 存储每个单独有配置app剩余时间,会改变时间 + // TODO: 2026/3/16 工作日和休息日时间不同需要同步或重新请求接口 + private Map mRemainingTimeMap = new HashMap<>(); + + public HashSet allowPackage = new HashSet() { + { + this.add(BuildConfig.APPLICATION_ID); + this.add("com.android.launcher3"); + this.add("com.android.packageinstaller"); + this.add("com.android.systemui"); + this.add("com.android.settings"); + this.add("com.android.permissioncontroller"); + + this.add("com.fuying.sn"); + this.add("com.uiui.os"); + this.add("com.uiui.store"); + this.add("com.uiui.info"); + this.add("com.tt.ttutils"); + this.add(JgyUtils.fuxiaoying); + this.add("com.sprd.engineermode"); + this.add("com.teclast.update"); + this.add("com.incar.update"); + this.add("com.adups.fota"); + } + }; + + public TimeManager(Context context) { + if (context == null) { + throw new RuntimeException("Context is NULL"); + } + this.mContext = context.getApplicationContext(); + this.mUsageStatsManager = (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE); + this.mPackageManager = mContext.getPackageManager(); + this.mStringAppTimeControlInfoMap = new HashMap<>(); + registerTimeReceiver(); + + IActivityManager activityManager = ActivityManagerNative.getDefault(); + try { + activityManager.setActivityController(new FuyingActivityController(), false); + } catch (RemoteException e) { + Log.e(TAG, "setActivityController: " + e.getMessage()); + e.printStackTrace(); + } + + ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + am.addOnUidImportanceListener(new ActivityManager.OnUidImportanceListener() { + @Override + public void onUidImportance(int uid, int importance) { + String foregroundPackage = getFirstPackageNameForUid(uid); + if (!TextUtils.isEmpty(foregroundPackage)) { + if (ApkUtils.isSystemApp(mContext, foregroundPackage)) { + return; + } + String previousPackage = AppUsedTimeUtils.getInstance().getApp_package(); + Log.e(TAG, "onUidImportance: previousPackage = " + previousPackage); + long nowTime = System.currentTimeMillis() / 1000; + if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + // 该 UID 切到了前台 + Log.e(TAG, "onUidImportance: " + foregroundPackage + " foreground"); + + AppUsedTimeUtils.getInstance().setApp_package(foregroundPackage); + AppUsedTimeUtils.getInstance().setStart_time(nowTime); + + } else { + // 该 UID 切到了后台 + Log.e(TAG, "onUidImportance: " + foregroundPackage + " background"); + if (foregroundPackage.equals(previousPackage)) { + NetInterfaceManager.getInstance().sendAppUsageRecord(foregroundPackage, nowTime, + new NetInterfaceManager.CompleteCallback() { + @Override + public void onComplete() { + getTimeControl(); + } + }); + } + } + } + } + }, ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); + + Type type = new TypeToken() { + }.getType(); + Gson gson = new Gson(); + String jsonString = mMMKV.decodeString(SN_TIME_CONTROL_INFO, ""); + try { + mSnTimeControlInfo = gson.fromJson(jsonString, type); + } catch (Exception e) { + Log.e(TAG, "TimeManager: " + e.getMessage()); + mMMKV.remove(SN_TIME_CONTROL_INFO); + } + + mGlobalRemainingTime = mMMKV.decodeLong(GLOBAL_REMAINING_TIME_KEY, 0); + + getTimeControl(); + inInterval(); + } + + public static void init(Context context) { + Log.e(TAG, "init: "); + if (sInstance == null) { + sInstance = new TimeManager(context); + } + } + + public static TimeManager getInstance() { + if (sInstance == null) { + throw new IllegalStateException("You must be init TimeManager first"); + } + return sInstance; + } + + public void getTimeControl() { + Log.e(TAG, "getTimeControl: "); + getSnTimeControl(); + getAppTimeControlInfo(); + } + + public SnTimeControlInfo getSnTimeControlInfo() { + return mSnTimeControlInfo; + } + + public void setSnTimeControlInfo(SnTimeControlInfo snTimeControlInfo) { + mSnTimeControlInfo = snTimeControlInfo; + } + + public boolean isGetSnTimeControlInfoSuccessful() { + return GetSnTimeControlInfoSuccessful; + } + + public void setGetSnTimeControlInfoSuccessful(boolean getSnTimeControlInfoSuccessful) { + GetSnTimeControlInfoSuccessful = getSnTimeControlInfoSuccessful; + } + + /** + * 通过应用的 UID 获取其包名列表。 + * 注意:由于可能存在多用户或共享 UID 的情况,一个 UID 可能对应多个包名。 + * + * @param uid 要查询的应用 UID + * @return 对应 UID 的包名数组。如果未找到,则返回 null 或空数组。 + */ + public String[] getPackagesForUid(int uid) { + String[] packageNames = mPackageManager.getPackagesForUid(uid); + return packageNames; // 可能为 null + } + + /** + * 便捷方法:获取 UID 对应的第一个包名(通常也是最常用的一个)。 + * + * @param uid 要查询的应用 UID + * @return 第一个包名。如果未找到,返回 null。 + */ + public String getFirstPackageNameForUid(int uid) { + String[] packageNames = getPackagesForUid(uid); + if (packageNames != null && packageNames.length > 0) { + return packageNames[0]; + } + return null; + } + + /** + * 初始化计时 + */ + private void inInterval() { + Observable.interval(1, TimeUnit.SECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Consumer() { + @Override + public void accept(Long aLong) throws Throwable { + // Log.e(TAG, "inInterval accept: " + aLong); + boolean isScreenOn = JgyUtils.getInstance().isScreenOn(); + if (!isScreenOn) { + return; + } + checkForegroundAppName(); + } + }); + } + + private void checkForegroundAppName() { + String foregroundPackage = getTopActivityInfo(); + Log.v(TAG, "checkForegroundAppName: foregroundPackage = " + foregroundPackage); + // if (mRemainingTimeMap.get(topPackage) != null) { + // long remainTime = mRemainingTimeMap.get(topPackage); + // } + // if (mSnTimeControlInfo != null) { + // if (mSnTimeControlInfo.getControl_type() == 1 && mCountdownDisposable == + // null) { + // startCountdown(); + // } + // } + if (allowPackage.contains(foregroundPackage)) { + Log.v(TAG, "checkForegroundAppName: skip"); + return; + } + int isControl = Settings.System.getInt(mContext.getContentResolver(), CommonConfig.KEY_IS_CONTROL, 1); + Log.e(TAG, "checkForegroundAppName: isControl = " + isControl); + // 只要关闭家长管控开关,相关内容需无效 + if (isControl == 0) { + Log.e(TAG, "checkForegroundAppName: 家长管控开关关闭"); + return; + } + if (mSnTimeControlInfo == null) { + if (!GetSnTimeControlInfoSuccessful) { + getSnTimeControl(); + } + Log.e(TAG, "checkForegroundAppName: no SnTimeControlInfo"); + return; + } + int snControlType = mSnTimeControlInfo.getControl_type(); + AppTimeControlInfo appTimeControlInfo = mStringAppTimeControlInfoMap.get(foregroundPackage); + if (appTimeControlInfo == null) { + // TODO: 2026/3/16 无配置实现逻辑再讨论 + gotoLauncher(); + } else { + if (snControlType == 0) {// 不限制整机 + if (!singleApplicationLimit(foregroundPackage)) { + gotoLauncher(); + } + } else if (snControlType == 1) {// 整机限制时长 + if (timeLimit(foregroundPackage)) { + Long appRemainingTime = mRemainingTimeMap.get(foregroundPackage); + if (appRemainingTime != null) { + Log.e(TAG, "checkForegroundAppName: " + foregroundPackage + " appRemainingTime = " + + appRemainingTime); + // + if (appTimeControlInfo.getControl_type() == 0 + || appTimeControlInfo.getControl_type() == 2) { + if (mGlobalRemainingTime <= 0) { + gotoLauncher(); + } else { + mGlobalRemainingTime -= 1; + } + } else { + if (appRemainingTime <= 0) { + gotoLauncher(); + } else { + long time = appRemainingTime - 1; + mRemainingTimeMap.put(foregroundPackage, time); + } + } + } else { + gotoLauncher(); + } + } else { + gotoLauncher(); + } + } else if (snControlType == 2) {// 整机限制时间段 + // 应用被禁用 + if (appTimeControlInfo.getIs_control() == 0) { + Toaster.show("应用已被禁用"); + gotoLauncher(); + return; + } + List partTimes = mSnTimeControlInfo.getTime_part(); + // 是否为默认时间段 + if (isDefaultTimePeriod(partTimes)) { + Log.e(TAG, "checkForegroundAppName: isDefaultTimePeriod true"); + + if (appTimeControlInfo.getControl_type() == 1) { + Log.e(TAG, "checkForegroundAppName: control_type = 1"); + Long appRemainTime = mRemainingTimeMap.get(foregroundPackage); + if (appRemainTime == null) { + Log.e(TAG, "checkForegroundAppName: appRemainTime is null"); + gotoLauncher(); + } else { + if (appRemainTime <= 0) { + gotoLauncher(); + Toaster.show("今日该应用使用时长已超限"); + } else { + mRemainingTimeMap.put(foregroundPackage, appRemainTime - 1); + } + } + } + + } else { + boolean inTime = inOpenTimePeriod(partTimes); + Log.e(TAG, "timePeriodLimit: inTime = " + inTime); + if (inTime) { + if (timePeriodLimit(foregroundPackage)) { + Long appRemainTime = mRemainingTimeMap.get(foregroundPackage); + if (appRemainTime == null) { + Log.e(TAG, "checkForegroundAppName: appRemainTime is null"); + gotoLauncher(); + } else { + Log.e(TAG, "checkForegroundAppName: appRemainTime = " + appRemainTime); + if (appTimeControlInfo.getControl_type() == 0) { + Log.e(TAG, "checkForegroundAppName: control_type = 0 allow running"); + } else if (appTimeControlInfo.getControl_type() == 2) { + Log.e(TAG, "checkForegroundAppName: control_type = 2"); + } else { + if (appRemainTime <= 0) { + gotoLauncher(); + Toaster.show("今日该应用使用时长已超限"); + } else { + mRemainingTimeMap.put(foregroundPackage, appRemainTime - 1); + } + } + } + } else { + gotoLauncher(); + Toaster.show("当前时间不在应用的可使用时间段内,请前往家长守护查看可用时间段"); + Log.e(TAG, "timePeriodLimit: Outside of the app's usable time period"); + } + } else { + Toaster.show("当前时间不在设备的可使用时间段内,请前往家长守护查看可用时间段"); + Log.e(TAG, "timePeriodLimit: Outside of the device's usable time period"); + } + } + } + } + } + + private String getTopActivityInfo() { + ActivityManager manager = (ActivityManager) mContext.getApplicationContext() + .getSystemService(Context.ACTIVITY_SERVICE); + List runningTaskInfos = manager.getRunningTasks(1); + if (runningTaskInfos != null && !runningTaskInfos.isEmpty()) { + ComponentName componentName = runningTaskInfos.get(0).topActivity; + if (componentName != null) { + String currentClassName = componentName.getPackageName(); + return currentClassName; + } + } + return ""; + } + + /** + * @param pkg + * @return true为可运行,false为禁止运行 + */ + public boolean appCanRun(String pkg) { + Log.e(TAG, "appCanRun: " + pkg); + if (allowPackage.contains(pkg)) { + return true; + } + + int isControl = Settings.System.getInt(mContext.getContentResolver(), CommonConfig.KEY_IS_CONTROL, 1); + Log.e(TAG, "appCanRun: isControl = " + isControl); + // 只要关闭家长管控开关,相关内容需无效 + if (isControl == 0) { + Log.e(TAG, "appCanRun: 家长管控开关关闭"); + return true; + } + if (mSnTimeControlInfo == null) { + if (!GetSnTimeControlInfoSuccessful) { + getSnTimeControl(); + } + return false; + } + int controlType = mSnTimeControlInfo.getControl_type(); + switch (controlType) { + case 0: + return singleApplicationLimit(pkg); + case 1: + return timeLimit(pkg); + case 2: + return timePeriodLimit(pkg); + default: + } + + return false; + } + + /** + * 单个应用限制 + * + * @return + */ + private boolean singleApplicationLimit(String pkg) { + AppTimeControlInfo appTimeControlInfo = mStringAppTimeControlInfoMap.get(pkg); + if (appTimeControlInfo != null) { + Log.e(TAG, "singleApplicationLimit: " + appTimeControlInfo); + if (appTimeControlInfo.getIs_control() == 0) { + Toaster.show("应用已被禁用"); + return false; + } + int control_type = appTimeControlInfo.getControl_type(); + switch (control_type) { + case 0: + return true; + case 1: + return appTimeLimit(pkg); + case 2: + boolean inTime = inOpenTimePeriod(appTimeControlInfo.getTime_part()); + if (!inTime) { + Toaster.show("当前时间不在应用的可使用时间段内,请前往家长守护查看可用时间段"); + } + return inTime; + } + } else { + Log.e(TAG, "singleApplicationLimit: " + pkg + " not configure"); + } + return false; + } + + private Disposable mCountdownDisposable; + + /** + * 整机限制时长 + * + * @param pkg + * @return + */ + private boolean timeLimit(String pkg) { + Log.e(TAG, "timeLimit: remainTime = " + mGlobalRemainingTime); + if (mGlobalRemainingTime <= 0) { + Toaster.show("今日整机使用时长已超限"); + return false; + } else { + boolean canRun = singleApplicationLimit(pkg); + if (canRun) { + // startSnCountdown(); + } else { + Log.e(TAG, "timeLimit: canRun = " + canRun); + } + return canRun; + } + } + + // 在类成员变量区域添加 + private boolean isSnCountdownRunning = false; + + // 替换 startSnCountdown 方法 + private void startSnCountdown() { + Log.e(TAG, "startCountdown: "); + + // 1. 如果已经在运行,直接返回,避免重复生成 + if (isSnCountdownRunning) { + if (mCountdownDisposable != null && !mCountdownDisposable.isDisposed()) { + Log.e(TAG, "startCountdown: already running, skip"); + return; + } else { + // 如果标志位是true但Disposable已销毁,重置标志位 + isSnCountdownRunning = false; + } + } + + // 2. 清理可能存在的旧连接 + if (mCountdownDisposable != null && !mCountdownDisposable.isDisposed()) { + mCountdownDisposable.dispose(); + } + + // 3. 检查剩余时间 + if (mGlobalRemainingTime <= 0) { + gotoLauncher(); + return; + } + + // 4. 设置标志位 + isSnCountdownRunning = true; + + // 5. 创建新的倒计时 + // 注意:这里使用当前的 mGlobalRemainingTime 作为总时长 + final long duration = mGlobalRemainingTime; + + mCountdownDisposable = Observable.intervalRange(0, duration, 0, 1, TimeUnit.SECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + new Consumer() { + @Override + public void accept(Long aLong) throws Throwable { + // 倒计时进行中,可以在这里更新UI或日志 + // 注意:不要在这里再次扣减 mGlobalRemainingTime,因为 checkForegroundAppName 可能也在做 + // 如果希望由这个定时器全权负责扣减,需要修改 checkForegroundAppName + Log.e(TAG, "startCountdown tick: " + aLong); + } + }, + new Consumer() { + @Override + public void accept(Throwable throwable) throws Throwable { + Log.e(TAG, "startCountdown error: " + throwable.getMessage()); + isSnCountdownRunning = false; + } + }, + new io.reactivex.rxjava3.functions.Action() { + @Override + public void run() throws Throwable { + Log.e(TAG, "startCountdown finished"); + mGlobalRemainingTime = 0; + mMMKV.encode(GLOBAL_REMAINING_TIME_KEY, mGlobalRemainingTime); + isSnCountdownRunning = false; // 重置标志 + gotoLauncher(); + } + }); + } + + /** + * 整机时间段限制 + * + * @param pkg + * @return + */ + private boolean timePeriodLimit(String pkg) { + List partTimes = mSnTimeControlInfo.getTime_part(); + if (partTimes == null || partTimes.isEmpty()) { + Log.e(TAG, "timePeriodLimit: partTimes is empty"); + return true; + } + boolean inTime = inOpenTimePeriod(partTimes); + Log.e(TAG, "timePeriodLimit: inTime = " + inTime); + if (inTime) { + return singleApplicationLimit(pkg); + } else { + Toaster.show("当前时间不在设备的可使用时间段内,请前往家长守护查看可用时间段"); + Log.e(TAG, "timePeriodLimit: Outside of the device's usable time period"); + return false; + } + } + + private boolean appTimeLimit(String pkg) { + AppTimeControlInfo appTimeControlInfo = mStringAppTimeControlInfoMap.get(pkg); + if (appTimeControlInfo == null) { + Log.e(TAG, "appTimeLimit: " + pkg + " appTimeControlInfo not found"); + return false; + } else { + // 不限制时长直接返回 + if (appTimeControlInfo.getControl_type() == 0) { + Log.e(TAG, "appTimeLimit: " + pkg + " not limit"); + return true; + } + + long usageTime = getAppTodayUsageTime(pkg); + Log.e(TAG, "appTimeLimit: usageTime = " + usageTime); + Long aLong = mRemainingTimeMap.get(pkg); + if (aLong != null) { + long remainTime = aLong; + Log.e(TAG, "appTimeLimit: remainTime = " + remainTime); + boolean usedUp = remainTime <= 0; + if (usedUp) { + Toaster.show("今日该应用使用时长已超限"); + mRemainingTimeMap.put(pkg, 0L); + } + return !usedUp; + } else { + return false; + } + } + } + + /** + * 判断时间戳对应日期是否为工作日(周一至周五)。 + * + * @param timestamp 毫秒级时间戳(自1970-01-01T00:00:00Z) + * @return true表示工作日,false表示休息日 + */ + public static boolean isWorkday(long timestamp) { + // 将时间戳转换为Instant,然后根据默认时区获取日期时间 + Instant instant = Instant.ofEpochMilli(timestamp); + ZonedDateTime dateTime = instant.atZone(ZoneId.systemDefault()); + + // 获取星期几 + DayOfWeek dayOfWeek = dateTime.getDayOfWeek(); + + // 判断是否为工作日:周一至周五 + return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY; + } + + public static boolean isWorkday() { + long timestamp = System.currentTimeMillis(); + // 将时间戳转换为Instant,然后根据默认时区获取日期时间 + Instant instant = Instant.ofEpochMilli(timestamp); + ZonedDateTime dateTime = instant.atZone(ZoneId.systemDefault()); + + // 获取星期几 + DayOfWeek dayOfWeek = dateTime.getDayOfWeek(); + + // 判断是否为工作日:周一至周五 + return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY; + } + + /** + * 是否在禁用时间段内,只有开放时间段 + * + * @param partTimes + * @return + */ + public boolean inOpenTimePeriod(List partTimes) { + for (PartTime partTime : partTimes) { + if (inOpenTimePeriod(partTime)) { + return true; + } + } + return false; + } + + public boolean inOpenTimePeriod(PartTime partTime) { + int dayType = partTime.getDay_type(); + if (isDefaultTimePeriod(partTime)) { + Log.e(TAG, "inOpenTimePeriod: default settings"); + return true; + } + + String startTimeStr = partTime.getStart_time(); + String endTimeStr = partTime.getEnd_time(); + + // 获取当前日期和时间 + LocalDate currentDate = LocalDate.now(); + LocalTime currentTime = LocalTime.now(); + + // 判断当前日期是工作日还是周末 + boolean isWeekday = currentDate.getDayOfWeek() != DayOfWeek.SATURDAY && + currentDate.getDayOfWeek() != DayOfWeek.SUNDAY; + boolean dayTypeMatch = (dayType == 0 && isWeekday) || (dayType == 1 && !isWeekday); + Log.e(TAG, "inOpenTimePeriod: dayTypeMatch = " + dayTypeMatch); + + if (!dayTypeMatch) { + return false; // 日期类型不匹配 + } + + // 解析时间并比较 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); + LocalTime startTime = LocalTime.parse(startTimeStr, formatter); + LocalTime endTime = LocalTime.parse(endTimeStr, formatter); + + boolean inTime = !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime); + return inTime; + } + + private boolean isDefaultTimePeriod(List partTimeList) { + if (partTimeList == null || partTimeList.isEmpty()) { + return false; // 或者根据你的业务需求返回 true/false + } + + return partTimeList.stream() + .allMatch( + partTime -> "00:00".equals(partTime.getStart_time()) && "24:00".equals(partTime.getEnd_time())); + } + + private boolean isDefaultTimePeriod(PartTime partTime) { + String startTimeStr = partTime.getStart_time(); + String endTimeStr = partTime.getEnd_time(); + + if ("00:00".equals(startTimeStr) && "24:00".equals(endTimeStr)) { + Log.e(TAG, "isDefaultTimePeriod: default settings"); + return true; + } else { + Log.e(TAG, "isDefaultTimePeriod: startTimeStr = " + startTimeStr + "\tendTimeStr = " + endTimeStr); + return false; + } + } + + @Deprecated + private void checkTimePeriod() { + int controlType = mSnTimeControlInfo.getControl_type(); + if (controlType == 2) { + List partTimes = mSnTimeControlInfo.getTime_part(); + boolean inTime = inOpenTimePeriod(partTimes); + if (!inTime) { + gotoLauncher(); + } + } + } + + /** + * 如果界面正在最近任务列表,有些app可能不会被清理 + */ + private void gotoLauncher() { + Intent i = new Intent(Intent.ACTION_MAIN); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // android123提示如果是服务里调用,必须加入new task标识 + i.addCategory(Intent.CATEGORY_HOME); + mContext.startActivity(i); + } + + /** + * getTotalTimeInForeground 需要应用返回后台才会更新使用时间 + */ + private long getAppTodayUsageTime(String pkgName) { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + long startTime = calendar.getTimeInMillis(); // 当天0点 + long endTime = System.currentTimeMillis(); // 当前时间 + Map stats = mUsageStatsManager.queryAndAggregateUsageStats(startTime, endTime); + UsageStats usageStats = stats.get(pkgName); + if (usageStats != null) { + long usageTime = usageStats.getTotalTimeInForeground(); + Log.e(TAG, "getAppUsageTime: " + pkgName + " getTotalTimeInForeground = " + usageTime); + return usageTime / 1000; + } else { + return 0; + } + } + + public void onDestroy() { + if (mCountdownDisposable != null && !mCountdownDisposable.isDisposed()) { + mCountdownDisposable.dispose(); + mCountdownDisposable = null; + } + if (mTimeChangedReceiver != null) { + mContext.unregisterReceiver(mTimeChangedReceiver); + } + } + + private TimeChangedReceiver mTimeChangedReceiver; + + /** + * 监听时间和日期变化 + */ + private void registerTimeReceiver() { + if (mTimeChangedReceiver == null) { + mTimeChangedReceiver = new TimeChangedReceiver(); + } + IntentFilter filter = new IntentFilter(); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + filter.addAction(Intent.ACTION_DATE_CHANGED); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.addAction(Intent.ACTION_TIME_TICK); + mContext.registerReceiver(mTimeChangedReceiver, filter); + } + + private class TimeChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.e(TAG, "TimeChangedReceiver:" + action); + + switch (action) { + case Intent.ACTION_DATE_CHANGED: + case Intent.ACTION_TIME_CHANGED: + case Intent.ACTION_TIMEZONE_CHANGED: + case Intent.ACTION_TIME_TICK: + // checkTimePeriod(); + break; + } + + } + } + + private void getSnTimeControl() { + NetInterfaceManager.getInstance().getSnTimeControlObservable() + .subscribe(new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getSnTimeControl", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse response) { + Log.e("getSnTimeControl", "onNext: " + response); + GetSnTimeControlInfoSuccessful = true; + if (response.code == 200) { + SnTimeControlInfo snTimeControlInfo = response.data; + mSnTimeControlInfo = snTimeControlInfo; + mMMKV.encode(SN_TIME_CONTROL_INFO, GsonUtils.toJSONString(snTimeControlInfo)); + mGlobalRemainingTime = snTimeControlInfo.getToday_time(); + mMMKV.encode(GLOBAL_REMAINING_TIME_KEY, mGlobalRemainingTime); + } else { + + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getSnTimeControl", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getSnTimeControl", "onComplete: "); + } + }); + } + + public void getAppTimeControlInfo() { + NetInterfaceManager.getInstance().getAppTimeControlInfoObservable() + .subscribe(new Observer>>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getAppTimeControlInfo", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse> response) { + Log.e("getAppTimeControlInfo", "onNext: " + response); + if (response.code == 200) { + List appTimeControlInfos = response.data; + mStringAppTimeControlInfoMap = appTimeControlInfos.stream() + .collect(Collectors.toMap(AppTimeControlInfo::getApp_package, Function.identity())); + mRemainingTimeMap = appTimeControlInfos.stream().collect(Collectors + .toMap(AppTimeControlInfo::getApp_package, AppTimeControlInfo::getToday_time)); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getAppTimeControlInfo", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getAppTimeControlInfo", "onComplete: "); + } + }); + } + +} diff --git a/app/src/main/java/com/fuying/sn/network/NetInterfaceManager.java b/app/src/main/java/com/fuying/sn/network/NetInterfaceManager.java index 2d926ff..dd700d4 100644 --- a/app/src/main/java/com/fuying/sn/network/NetInterfaceManager.java +++ b/app/src/main/java/com/fuying/sn/network/NetInterfaceManager.java @@ -25,6 +25,7 @@ import com.fuying.sn.bean.AppAndWhiteBean; import com.fuying.sn.bean.AppInfo; import com.fuying.sn.bean.AppJump; import com.fuying.sn.bean.AppStart; +import com.fuying.sn.bean.AppTimeControlInfo; import com.fuying.sn.bean.AppUploadInfo; import com.fuying.sn.bean.AppletQRCode; import com.fuying.sn.bean.BaseResponse; @@ -39,6 +40,7 @@ import com.fuying.sn.bean.SnBindState; import com.fuying.sn.bean.SnInfo; import com.fuying.sn.bean.SnRunLog; import com.fuying.sn.bean.SnTag; +import com.fuying.sn.bean.SnTimeControlInfo; import com.fuying.sn.bean.SystemSettingsSet; import com.fuying.sn.bean.UserAvatarInfo; import com.fuying.sn.bean.browser.BrowserApiData; @@ -53,6 +55,7 @@ import com.fuying.sn.gson.GsonUtils; import com.fuying.sn.manager.ConnectManager; import com.fuying.sn.manager.ConnectMode; import com.fuying.sn.manager.ControlManager; +import com.fuying.sn.manager.time.TimeManager; import com.fuying.sn.network.api.AppApi; import com.fuying.sn.network.api.CommonApi; import com.fuying.sn.network.api.FileApi; @@ -63,6 +66,7 @@ import com.fuying.sn.network.api.SnApi; import com.fuying.sn.network.api.SnControlApi; import com.fuying.sn.network.api.SnInfoApi; import com.fuying.sn.network.api.SnLogApi; +import com.fuying.sn.network.api.newly.ControlApi; import com.fuying.sn.network.interceptor.RepeatRequestInterceptor; import com.fuying.sn.service.LogcatService; import com.fuying.sn.utils.ApkUtils; @@ -161,7 +165,7 @@ public class NetInterfaceManager { } mRetrofit = new Retrofit.Builder() .client(okHttpClient) - .baseUrl(UrlAddress.ROOT_URL) + .baseUrl(BuildConfig.ROOT_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .build(); @@ -249,7 +253,9 @@ public class NetInterfaceManager { return mRetrofit.create(ScreenStateApi.class); } - + public ControlApi getControlApi() { + return mRetrofit.create(ControlApi.class); + } /* @@ -469,7 +475,19 @@ public class NetInterfaceManager { .observeOn(AndroidSchedulers.mainThread()); } + public Observable> getSnTimeControlObservable() { + return getControlApi() + .getSnTimeControl(Utils.getSerial()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + public Observable>> getAppTimeControlInfoObservable() { + return getControlApi() + .getAppTimeControl(Utils.getSerial()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } /* * @@ -2443,18 +2461,18 @@ public class NetInterfaceManager { } - public void getSnTimeControl(boolean refresh, BehaviorSubject lifecycle, CompleteCallback callback) { + public void getMachineTimeControl(boolean refresh, BehaviorSubject lifecycle, CompleteCallback callback) { ConnectMode connectMode = ConnectMode.DEFAULT; if (refresh) { connectMode = ConnectMode.DEFAULT; } if (ConnectManager.getInstance().isNeedConnect(UrlAddress.GET_SN_TIME_CONTROL, connectMode)) { - getSnTimeControl(lifecycle, callback); + getMachineTimeControl(lifecycle, callback); } else { String jsonString = mCacheHelper.getAsString(UrlAddress.GET_SN_TIME_CONTROL); //为 "" 是已经请求成功的 if (jsonString == null) { - getSnTimeControl(lifecycle, callback); + getMachineTimeControl(lifecycle, callback); } else { // Gson gson = new Gson(); // Type type = new TypeToken() { @@ -2469,32 +2487,32 @@ public class NetInterfaceManager { return mCacheHelper.getAsString(UrlAddress.GET_SN_TIME_CONTROL); } - public void getSnTimeControl(BehaviorSubject lifecycle, CompleteCallback callback) { + public void getMachineTimeControl(BehaviorSubject lifecycle, CompleteCallback callback) { getMachineTimeControlObservable() .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) - .subscribe(getSnTimeControlObserver(callback)); + .subscribe(getMachineTimeControlObserver(callback)); } - public void getSnTimeControl(CompleteCallback callback) { + public void getMachineTimeControl(CompleteCallback callback) { getMachineTimeControlObservable() - .subscribe(getSnTimeControlObserver(callback)); + .subscribe(getMachineTimeControlObserver(callback)); } - public void getSnTimeControl() { + public void getMachineTimeControl() { getMachineTimeControlObservable() - .subscribe(getSnTimeControlObserver(null)); + .subscribe(getMachineTimeControlObserver(null)); } - public Observer> getSnTimeControlObserver(CompleteCallback callback) { + public Observer> getMachineTimeControlObserver(CompleteCallback callback) { return new Observer>() { @Override public void onSubscribe(@NonNull Disposable d) { - Log.e("getSnTimeControlObserver", "onSubscribe: "); + Log.e("getMachineTimeControlObserver", "onSubscribe: "); } @Override public void onNext(@NonNull BaseResponse machineControlBaseResponse) { - Log.e("getSnTimeControlObserver", "onNext: " + machineControlBaseResponse); + Log.e("getMachineTimeControlObserver", "onNext: " + machineControlBaseResponse); if (machineControlBaseResponse.code == 200) { MachineControl machineControl = machineControlBaseResponse.data; TimeControlManager.getInstance().setGlobalMachineControl(machineControl); @@ -2509,13 +2527,13 @@ public class NetInterfaceManager { @Override public void onError(@NonNull Throwable e) { - Log.e("getSnTimeControlObserver", "onError: " + e.getMessage()); + Log.e("getMachineTimeControlObserver", "onError: " + e.getMessage()); onComplete(); } @Override public void onComplete() { - Log.e("getSnTimeControlObserver", "onComplete: "); + Log.e("getMachineTimeControlObserver", "onComplete: "); if (callback != null) { callback.onComplete(); } @@ -2523,6 +2541,43 @@ public class NetInterfaceManager { }; } + public void sendAppUsageRecord(String packageName, long closeTime, CompleteCallback completeCallback) { + int isLogined = (int) SPUtils.get(mContext, CommonConfig.isLogined, 2); + if (isLogined != 1) { + Log.e(TAG, "sendAppUsageRecord: 没有绑定"); + // TODO: 2025/6/13 延迟获取不了数据 +// return; + } + + String label = ApkUtils.getAppLable(mContext, packageName); + long openTime = AppUsedTimeUtils.getInstance().getStart_time(); + + NetInterfaceManager.getInstance().getAppUsageRecordObservable(packageName, label, openTime, closeTime) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("sendAppUsageRecord", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse baseResponse) { + Log.e("sendAppUsageRecord", "onNext: " + baseResponse); + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("sendAppUsageRecord", "onError: " + e.getMessage()); + onComplete(); + } + + @Override + public void onComplete() { + Log.e("sendAppUsageRecord", "onComplete: "); + completeCallback.onComplete(); + } + }); + } + public void sendCloseApp(String packageName, CompleteCallback completeCallback) { int isLogined = (int) SPUtils.get(mContext, CommonConfig.isLogined, 2); if (isLogined != 1) { @@ -2539,7 +2594,12 @@ public class NetInterfaceManager { } if (null != appInfo) { String label = appInfo.applicationInfo.loadLabel(pm).toString(); - long openTime = RunningAppManager.getInstance().getOnClickTime(); + long openTime = 0; + if (BuildConfig.VERSION_CODE < 88) { + openTime = RunningAppManager.getInstance().getOnClickTime(); + } else { + + } long closeTime = System.currentTimeMillis() / 1000; NetInterfaceManager.getInstance().getAppUsageRecordObservable(packageName, label, openTime, closeTime) .subscribe(new Observer() { @@ -3049,17 +3109,18 @@ public class NetInterfaceManager { @Override public void onNext(@NonNull BaseResponse screenPasswordBaseResponse) { - Log.e("onNext", "onSubscribe: "); + Log.e("onNext", "onNext: "); if (screenPasswordBaseResponse.code == 200) { ScreenPassword screenPassword = screenPasswordBaseResponse.data; if (screenPassword == null || TextUtils.isEmpty(screenPassword.getPwd())) { mCacheHelper.put(UrlAddress.LOCK_SCREEN_PWD, ""); } else { mCacheHelper.put(UrlAddress.LOCK_SCREEN_PWD, GsonUtils.toJSONString(screenPassword)); - SPUtils.put(mContext, UrlAddress.LOCK_SCREEN_PWD, screenPassword.getPwd()); + mMMKV.encode(UrlAddress.LOCK_SCREEN_PWD, screenPassword.getPwd()); } } else { mCacheHelper.put(UrlAddress.LOCK_SCREEN_PWD, ""); + mMMKV.remove(UrlAddress.LOCK_SCREEN_PWD); } } @@ -3252,4 +3313,53 @@ public class NetInterfaceManager { } }); } + + public void getSnTimeControl(BehaviorSubject lifecycle, CompleteCallback callback) { + getSnTimeControlObservable() + .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) + .subscribe(getSnTimeControlObserver(callback)); + } + + public void getSnTimeControl(CompleteCallback callback) { + getSnTimeControlObservable() + .subscribe(getSnTimeControlObserver(callback)); + } + + public void getSnTimeControl() { + getSnTimeControlObservable() + .subscribe(getSnTimeControlObserver(null)); + } + + public Observer> getSnTimeControlObserver(CompleteCallback callback) { + return new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getSnTimeControlObserver", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse snTimeControlInfoBaseResponse) { + Log.e("getSnTimeControlObserver", "onNext: "); + if (snTimeControlInfoBaseResponse.code == 200) { + SnTimeControlInfo snTimeControlInfo = snTimeControlInfoBaseResponse.data; + TimeManager.getInstance().setSnTimeControlInfo(snTimeControlInfo); + mMMKV.encode(TimeManager.SN_TIME_CONTROL_INFO, GsonUtils.toJSONString(snTimeControlInfo)); + } else { + TimeManager.getInstance().setSnTimeControlInfo(null); + } + TimeManager.getInstance().setGetSnTimeControlInfoSuccessful(true); + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getSnTimeControlObserver", "onError: "); + } + + @Override + public void onComplete() { + Log.e("getSnTimeControlObserver", "onComplete: "); + } + }; + } + } diff --git a/app/src/main/java/com/fuying/sn/network/UrlAddress.java b/app/src/main/java/com/fuying/sn/network/UrlAddress.java index a70ce1b..430867a 100644 --- a/app/src/main/java/com/fuying/sn/network/UrlAddress.java +++ b/app/src/main/java/com/fuying/sn/network/UrlAddress.java @@ -2,7 +2,7 @@ package com.fuying.sn.network; public class UrlAddress { /*主页接口*/ - static final String ROOT_URL = "http://47.111.23.154/android/"; +// static final String ROOT_URL = "http://47.111.23.154/android/"; /*设备信息接口*/ public static final String SN_INFO = "sn/getSnInfo"; diff --git a/app/src/main/java/com/fuying/sn/network/api/newly/ControlApi.java b/app/src/main/java/com/fuying/sn/network/api/newly/ControlApi.java index 0d2ab50..22695b0 100644 --- a/app/src/main/java/com/fuying/sn/network/api/newly/ControlApi.java +++ b/app/src/main/java/com/fuying/sn/network/api/newly/ControlApi.java @@ -1,20 +1,24 @@ package com.fuying.sn.network.api.newly; +import com.fuying.sn.bean.AppTimeControlInfo; import com.fuying.sn.bean.BaseResponse; +import com.fuying.sn.bean.SnTimeControlInfo; import com.fuying.sn.network.UrlAddress; +import java.util.List; + import io.reactivex.rxjava3.core.Observable; import retrofit2.http.GET; import retrofit2.http.Query; public interface ControlApi { @GET(UrlAddress.SN_TIME_CONTROL) - Observable getSnTimeControl( + Observable> getSnTimeControl( @Query("sn") String sn ); @GET(UrlAddress.APP_TIME_CONTROL) - Observable getAppTimeControl( + Observable>> getAppTimeControl( @Query("sn") String sn ); } diff --git a/app/src/main/java/com/fuying/sn/push/PushManager.java b/app/src/main/java/com/fuying/sn/push/PushManager.java index 481fa79..8e53dc8 100644 --- a/app/src/main/java/com/fuying/sn/push/PushManager.java +++ b/app/src/main/java/com/fuying/sn/push/PushManager.java @@ -29,6 +29,7 @@ import com.fuying.sn.disklrucache.CacheHelper; import com.fuying.sn.gson.GsonUtils; import com.fuying.sn.manager.ControlManager; import com.fuying.sn.manager.DeviceManager; +import com.fuying.sn.manager.time.TimeManager; import com.fuying.sn.network.NetInterfaceManager; import com.fuying.sn.receiver.BootReceiver; import com.fuying.sn.service.LogcatService; @@ -575,7 +576,10 @@ public class PushManager { NetInterfaceManager.getInstance().getSnInfo(); NetInterfaceManager.getInstance().getSystemSettings(); NetInterfaceManager.getInstance().getAppTimeControl(); + NetInterfaceManager.getInstance().getMachineTimeControl(); NetInterfaceManager.getInstance().getSnTimeControl(); + TimeManager.getInstance().getTimeControl(); + } }, 2345); } diff --git a/app/src/main/java/com/fuying/sn/service/ManagerService.java b/app/src/main/java/com/fuying/sn/service/ManagerService.java index 9388bd2..36c3180 100644 --- a/app/src/main/java/com/fuying/sn/service/ManagerService.java +++ b/app/src/main/java/com/fuying/sn/service/ManagerService.java @@ -21,6 +21,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.blankj.utilcode.util.NetworkUtils; +import com.fuying.sn.BuildConfig; import com.fuying.sn.R; import com.fuying.sn.bean.BaseResponse; import com.fuying.sn.config.CommonConfig; @@ -33,6 +34,7 @@ import com.fuying.sn.utils.SPUtils; import com.fuying.sn.utils.TimeUtils; import com.fuying.sn.utils.Utils; import com.google.gson.JsonObject; +import com.tencent.mmkv.MMKV; import com.tuo.customview.VerificationCodeView; import java.util.concurrent.TimeUnit; @@ -53,6 +55,8 @@ import io.reactivex.rxjava3.schedulers.Schedulers; public class ManagerService extends Service implements NetworkUtils.OnNetworkStatusChangedListener { private static final String TAG = "ManagerService"; + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + public static final String ACTION_LOCK = "LockScreenReceiver_lockscreen"; public static final String ACTION_UNLOCK = "LockScreenReceiver_unlockscreen"; public static final String ACTION_UPDATE = "TimeChangedReceiver_update"; @@ -204,6 +208,10 @@ public class ManagerService extends Service implements NetworkUtils.OnNetworkSta public static final String LOCK_STATE = "SCRENN_LOOCKED_STATE"; private void showFloatingWindow(String name) { + if (BuildConfig.DEBUG) { + Log.e(TAG, "showFloatingWindow: debug mode skip"); + return; + } if (Settings.canDrawOverlays(this)) { // 获取WindowManager服务 if (null == windowManager) { @@ -324,17 +332,10 @@ public class ManagerService extends Service implements NetworkUtils.OnNetworkSta return; } Log.e(TAG, "inputComplete: " + content); - String password = (String) SPUtils.get(ManagerService.this, UrlAddress.LOCK_SCREEN_PWD, ""); - if ((!TextUtils.isEmpty(content) && !TextUtils.isEmpty(password))) { - if (password.equals(content)) { - hide(); - }else { - tv_hint.setText("密码错误"); - } - } else if (DEFAULT_PASSWORD.equals(content)){ + String password = mMMKV.decodeString(UrlAddress.LOCK_SCREEN_PWD, DEFAULT_PASSWORD); + if (content.equals(password)) { hide(); - } else{ -// ToastUtil.show("密码错误"); + } else { tv_hint.setText("密码错误"); } } diff --git a/app/src/main/java/com/fuying/sn/service/RemoteService.java b/app/src/main/java/com/fuying/sn/service/RemoteService.java index e761b02..2fb60f1 100644 --- a/app/src/main/java/com/fuying/sn/service/RemoteService.java +++ b/app/src/main/java/com/fuying/sn/service/RemoteService.java @@ -51,18 +51,26 @@ public class RemoteService extends Service { @Override public boolean inControl(String pkg) throws RemoteException { - boolean control = RunningAppManager.getInstance().inControlTime(pkg); - Log.e(TAG, "inControl: " + control); - return control; + if (BuildConfig.VERSION_CODE < 88) { + boolean control = RunningAppManager.getInstance().inControlTime(pkg); + Log.e(TAG, "inControl: " + control); + return control; + } else { + return false; + } } @Override public String getDisableContent(String pkg) throws RemoteException { - String content = RunningAppManager.getInstance().getDisableContent(pkg); - if (BuildConfig.DEBUG) { - Log.e(TAG, "getDisableContent: " + content); + if (BuildConfig.VERSION_CODE < 88) { + String content = RunningAppManager.getInstance().getDisableContent(pkg); + if (BuildConfig.DEBUG) { + Log.e(TAG, "getDisableContent: " + content); + } + return content; + } else { + return "新版本测试"; } - return content; } }; } diff --git a/app/src/main/java/com/fuying/sn/service/main/MainSPresenter.java b/app/src/main/java/com/fuying/sn/service/main/MainSPresenter.java index 5d947ec..e60c8ba 100644 --- a/app/src/main/java/com/fuying/sn/service/main/MainSPresenter.java +++ b/app/src/main/java/com/fuying/sn/service/main/MainSPresenter.java @@ -5,6 +5,7 @@ import android.content.Intent; import android.provider.Settings; import android.util.Log; +import com.fuying.sn.BuildConfig; import com.fuying.sn.bean.AppStart; import com.fuying.sn.bean.BaseResponse; import com.fuying.sn.bean.MyAppUsageBean; @@ -329,7 +330,7 @@ public class MainSPresenter implements MainSContact.Presenter { @Override public void getSnTimeControl() { - NetInterfaceManager.getInstance().getSnTimeControl(getLifecycle(), new NetInterfaceManager.CompleteCallback() { + NetInterfaceManager.getInstance().getMachineTimeControl(getLifecycle(), new NetInterfaceManager.CompleteCallback() { @Override public void onComplete() { mView.setSnTimeControl(); @@ -342,7 +343,9 @@ public class MainSPresenter implements MainSContact.Presenter { NetInterfaceManager.getInstance().getMyAppList(new NetInterfaceManager.MyAppListCallback() { @Override public void setMyAppList(List myAppList) { - RunningAppManager.getInstance().syncAllAppUsageTime(myAppList); + if (BuildConfig.VERSION_CODE < 88) { + RunningAppManager.getInstance().syncAllAppUsageTime(myAppList); + } mView.setMyAppList(); } }); diff --git a/app/src/main/java/com/fuying/sn/service/main/MainService.java b/app/src/main/java/com/fuying/sn/service/main/MainService.java index 3ae7dcb..e08862a 100644 --- a/app/src/main/java/com/fuying/sn/service/main/MainService.java +++ b/app/src/main/java/com/fuying/sn/service/main/MainService.java @@ -444,7 +444,9 @@ public class MainService extends BaseRxService implements MainSContact.MainView, Log.e(TAG, "getAppControl: requests have been made today"); } else { Log.e(TAG, "getAppControl: getConfig"); - RunningAppManager.getInstance().getConfig(); + if (BuildConfig.VERSION_CODE < 88) { + RunningAppManager.getInstance().getConfig(); + } } } diff --git a/app/src/main/java/com/fuying/sn/utils/ApkUtils.java b/app/src/main/java/com/fuying/sn/utils/ApkUtils.java index 8847bb3..0d29ad1 100644 --- a/app/src/main/java/com/fuying/sn/utils/ApkUtils.java +++ b/app/src/main/java/com/fuying/sn/utils/ApkUtils.java @@ -1033,5 +1033,19 @@ public class ApkUtils { } } + public static String getAppLable(Context context,String pkgName){ + PackageManager pm = context.getPackageManager(); + PackageInfo appInfo = null; + try { + appInfo = pm.getPackageInfo(pkgName, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + if (null != appInfo) { + String label = appInfo.applicationInfo.loadLabel(pm).toString(); + return label; + } + return "unknown"; + } } diff --git a/app/src/main/java/com/fuying/sn/utils/Utils.java b/app/src/main/java/com/fuying/sn/utils/Utils.java index 99cdcaa..b400fda 100644 --- a/app/src/main/java/com/fuying/sn/utils/Utils.java +++ b/app/src/main/java/com/fuying/sn/utils/Utils.java @@ -948,7 +948,7 @@ public class Utils { // IMEI = TelephonyMgr.getDeviceId(); // } else {//9.0到10.0获取 - IMEI = Settings.System.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + IMEI = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); } // Log.e("IMEI:", "IMEI: " + IMEI); if (null == IMEI) { diff --git a/build.gradle b/build.gradle index 56b0369..f863ec5 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ buildscript { // mavenCentral() maven { url "https://jitpack.io" } maven { url 'https://developer.huawei.com/repo/' } + maven { url 'https://developer.hihonor.com/repo' } maven { url 'https://maven.aliyun.com/repository/central' } maven { url "https://maven.aliyun.com/repository/jcenter" } maven { url 'https://maven.aliyun.com/repository/public' } @@ -25,6 +26,7 @@ allprojects { // mavenCentral() maven { url "https://jitpack.io" } maven { url 'https://developer.huawei.com/repo/' } + maven { url 'https://developer.hihonor.com/repo' } maven { url 'https://maven.aliyun.com/repository/central' } maven { url "https://maven.aliyun.com/repository/jcenter" } maven { url 'https://maven.aliyun.com/repository/public' }