diff --git a/positions/build.properties b/positions/build.properties index ed9288b..aa053ed 100644 --- a/positions/build.properties +++ b/positions/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon May 04 03:36:37 CST 2026 +#Mon May 04 09:35:11 CST 2026 stageCount=20 libraryProject= baseVersion=15.12 publishVersion=15.12.19 -buildCount=23 +buildCount=29 baseBetaVersion=15.12.20 diff --git a/positions/src/main/java/cc/winboll/studio/positions/App.java b/positions/src/main/java/cc/winboll/studio/positions/App.java index fd5eade..ded1168 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/App.java +++ b/positions/src/main/java/cc/winboll/studio/positions/App.java @@ -5,6 +5,7 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.res.Resources; import android.graphics.Typeface; @@ -25,6 +26,7 @@ import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; +import cc.winboll.studio.positions.services.MainService; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -59,9 +61,13 @@ public class App extends GlobalApplication { //===================== 全局静态常量与变量 ===================== public static final String TAG = "App"; private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper()); + private static App sInstance; + + private static final String SP_NAME = "app_idle_config"; + private static final String SP_KEY_IDLE_RUNNING = "is_idle_running"; // 应用全局空转状态标记 - public static boolean isAppIdleRunning = false; + private static boolean isAppIdleRunning = false; //===================== 空转状态对外方法 ===================== /** @@ -83,23 +89,67 @@ public class App extends GlobalApplication { LogUtils.d(TAG, "setAppIdleRunning -> 传入参数 idleRunning = " + idleRunning); if (isDebugging()) { isAppIdleRunning = idleRunning; - LogUtils.i(TAG, "setAppIdleRunning -> 调试模式,空转状态设置生效"); + saveIdleRunningToSp(idleRunning); + LogUtils.i(TAG, "setAppIdleRunning -> 调试模式,空转状态设置生效并持久化"); + + // 重启MainService服务以同步新的空转状态(stopService对未运行服务无效,故无需判断状态) + if (sInstance != null) { + LogUtils.i(TAG, "setAppIdleRunning -> 重启MainService服务以同步空转状态"); + Intent serviceIntent = new Intent(sInstance, MainService.class); + sInstance.stopService(serviceIntent); + sInstance.startService(serviceIntent); + } } else { LogUtils.i(TAG, "setAppIdleRunning -> 非调试模式,空转设置无效"); LogUtils.i(TAG, "Non-debug state, app idle setting is meaningless."); } } + /** + * 从SharedPreferences加载空转状态 + */ + private static void loadIdleRunningFromSp() { + try { + if (sInstance != null) { + SharedPreferences sp = sInstance.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + isAppIdleRunning = sp.getBoolean(SP_KEY_IDLE_RUNNING, false); + LogUtils.i(TAG, "loadIdleRunningFromSp -> 从SP加载空转状态:" + isAppIdleRunning); + } + } catch (Exception e) { + LogUtils.e(TAG, "loadIdleRunningFromSp -> 加载空转状态失败:" + e.getMessage()); + isAppIdleRunning = false; + } + } + + /** + * 保存空转状态到SharedPreferences + * @param idleRunning 空转状态 + */ + private static void saveIdleRunningToSp(boolean idleRunning) { + try { + if (sInstance != null) { + SharedPreferences sp = sInstance.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + sp.edit().putBoolean(SP_KEY_IDLE_RUNNING, idleRunning).apply(); + LogUtils.i(TAG, "saveIdleRunningToSp -> 空转状态已保存:" + idleRunning); + } + } catch (Exception e) { + LogUtils.e(TAG, "saveIdleRunningToSp -> 保存空转状态失败:" + e.getMessage()); + } + } + //===================== 应用生命周期 ===================== @Override public void onCreate() { super.onCreate(); + sInstance = this; LogUtils.i(TAG, "onCreate -> 全局Application初始化开始"); setIsDebugging(BuildConfig.DEBUG); WinBoLLActivityManager.init(this); ToastUtils.init(this); + loadIdleRunningFromSp(); + LogUtils.i(TAG, "onCreate -> 全局组件初始化全部完成"); } diff --git a/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java b/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java index ef0c9a2..e42e229 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java +++ b/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java @@ -36,7 +36,15 @@ import cc.winboll.studio.positions.handlers.AppIdleRunningModeHandler; import cc.winboll.studio.positions.utils.AppConfigsUtil; import cc.winboll.studio.positions.utils.ServiceUtil; import cc.winboll.studio.positions.services.IdleGpsService; +import cc.winboll.studio.positions.services.MainService; import cc.winboll.studio.positions.R; +import android.os.Handler; +import android.os.Looper; +import android.os.Handler; +import android.os.Handler; +import android.os.Looper; +import android.content.Intent; +import android.os.Handler; /** * 主页面控制器 @@ -345,11 +353,6 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity { boolean idleNow = App.isAppIdleRunning(); boolean idleNew = !idleNow; App.setAppIdleRunning(idleNew); - if (idleNew) { - IdleGpsService.getInstance().start(); - } else { - IdleGpsService.getInstance().stop(); - } AppIdleRunningModeHandler.sendIdleSwitch(idleNew); AppIdleRunningModeHandler.sendIdleLog("菜单手动切换空转状态:" + idleNew); LogUtils.d(TAG, "onOptionsItemSelected -> 空转状态已切换,当前:" + idleNew); diff --git a/positions/src/main/java/cc/winboll/studio/positions/services/IdleGpsService.java b/positions/src/main/java/cc/winboll/studio/positions/services/IdleGpsService.java index b504299..7c75003 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/services/IdleGpsService.java +++ b/positions/src/main/java/cc/winboll/studio/positions/services/IdleGpsService.java @@ -3,6 +3,7 @@ package cc.winboll.studio.positions.services; import android.os.Handler; import android.os.Looper; +import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.positions.handlers.AppIdleRunningModeHandler; import cc.winboll.studio.positions.models.PositionModel; @@ -100,6 +101,7 @@ public class IdleGpsService { currentBearing = 1; AppIdleRunningModeHandler.sendIdleLog("空转GPS服务已启动"); notifyStatusChange("空转GPS服务已启动"); + ToastUtils.show("空转GPS服务已启动"); handler.post(updateRunnable); handler.post(bearingRunnable); } diff --git a/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java b/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java index 9d39fb0..517cfcf 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java +++ b/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java @@ -22,6 +22,7 @@ import android.os.Looper; import android.text.TextUtils; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; +import cc.winboll.studio.positions.App; import cc.winboll.studio.positions.models.PositionModel; import cc.winboll.studio.positions.models.PositionTaskModel; import cc.winboll.studio.positions.utils.AppConfigsUtil; @@ -54,6 +55,35 @@ public class MainService extends Service { void onGpsStatusChanged(String status); } + // 空转GPS监听实例(用于注册到IdleGpsService) + private final GpsUpdateListener mIdleGpsListener = new GpsUpdateListener() { + @Override + public void onGpsPositionUpdated(PositionModel currentGpsPos) { + handleGpsPositionUpdate(currentGpsPos); + } + + @Override + public void onGpsStatusChanged(String status) { + handleGpsStatusChange(status); + } + }; + + // 中央处理:GPS 位置更新 + private void handleGpsPositionUpdate(PositionModel pos) { + if (pos == null) return; + syncCurrentGpsPosition(pos); + DistanceCalculatorUtil.getInstance(MainService.this).checkAllTaskTriggerCondition(pos); + String src = App.isAppIdleRunning() ? " (空转)" : ""; + LogUtils.d(TAG, "GPS位置更新:纬度=" + pos.getLatitude() + ",经度=" + pos.getLongitude() + src); + } + + // 中央处理:GPS 状态变化 + private void handleGpsStatusChange(String status) { + LogUtils.d(TAG, "GPS状态变化:" + status); + notifyAllGpsStatusListeners(status); + updateNotificationGpsStatus(status); + } + // 任务更新监听接口(Java 7 风格,供Adapter监听任务变化) public interface TaskUpdateListener { void onTaskUpdated(); @@ -77,6 +107,7 @@ public class MainService extends Service { private final ArrayList mPositionList = new ArrayList(); // 位置数据列表 private final ArrayList mAllTasks = new ArrayList();// 任务数据列表 private static PositionModel _mCurrentGpsPosition; // 当前GPS定位数据 + private boolean isListeningToIdleGps = false; // 当前是否监听空转GPS // 服务相关变量(Java 7 显式声明,保持原逻辑) MyServiceConnection mMyServiceConnection; @@ -366,6 +397,9 @@ public class MainService extends Service { } if (mAppConfigsUtil.isEnableMainService(true)) { + if (App.isAppIdleRunning()) { + IdleGpsService.getInstance().start(); + } run(); // 启动服务核心逻辑 } } @@ -382,6 +416,9 @@ public class MainService extends Service { // 启动前台服务(Java 7 显式调用,无方法引用) String initialStatus = "[ Positions ] is in Service."; + if (App.isAppIdleRunning()) { + initialStatus += " [IDLE RUNNING]"; + } NotificationUtil.createForegroundServiceNotification(this, initialStatus); startForeground(NotificationUtil.FOREGROUND_SERVICE_NOTIFICATION_ID, NotificationUtil.createForegroundServiceNotification(this, initialStatus)); @@ -599,21 +636,28 @@ public class MainService extends Service { if (!_mIsServiceRunning || _mCurrentGpsPosition == null) { return; } - // 格式化通知内容(Java 7 String.format,无String.join等Java 8+方法) - final String gpsStatus = String.format( - "GPS位置:北纬%.4f° 东经%.4f° | 可见位置:%d个", + // 根据空转状态决定通知前缀(区分空转GPS与真实GPS) + String prefix = App.isAppIdleRunning() ? "空转GPS" : "GPS位置"; + // 格式化通知内容(Java 7 String.format,使用%.15f显示全部GPS精度) + String gpsStatus = String.format( + "%s:北纬%.15f° 东经%.15f° | 可见位置:%d个", + prefix, _mCurrentGpsPosition.getLatitude(), _mCurrentGpsPosition.getLongitude(), mVisiblePositionIds.size() ); + if (App.isAppIdleRunning()) { + gpsStatus += " [IDLE RUNNING]"; + } + final String finalGpsStatus = gpsStatus; // 主线程判断+切换(Java 7 匿名内部类) if (Looper.myLooper() == Looper.getMainLooper()) { - NotificationUtil.updateForegroundServiceStatus(this, gpsStatus); + NotificationUtil.updateForegroundServiceStatus(this, finalGpsStatus); } else { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { - NotificationUtil.updateForegroundServiceStatus(MainService.this, gpsStatus); + NotificationUtil.updateForegroundServiceStatus(MainService.this, finalGpsStatus); } }); } @@ -634,6 +678,9 @@ public class MainService extends Service { if (intent != null) { isSettingToEnable = intent.getBooleanExtra(EXTRA_IS_SETTING_TO_ENABLE, false); if (isSettingToEnable) { + if (App.isAppIdleRunning()) { + IdleGpsService.getInstance().start(); + } run(); // 重启服务核心逻辑(保证服务启动后进入运行状态) } } @@ -701,10 +748,8 @@ public class MainService extends Service { gpsPos.setPositionId("CURRENT_GPS_POS"); gpsPos.setMemo("实时GPS位置"); - // 同步GPS位置+刷新距离+日志(原逻辑保留) - syncCurrentGpsPosition(gpsPos); - DistanceCalculatorUtil.getInstance(MainService.this).checkAllTaskTriggerCondition(gpsPos); - LogUtils.d(TAG, "GPS位置更新:纬度=" + location.getLatitude() + ",经度=" + location.getLongitude()); + // 调用中央处理方法 + handleGpsPositionUpdate(gpsPos); } } @@ -725,9 +770,7 @@ public class MainService extends Service { statusDesc = "GPS状态:临时不可用(遮挡)"; break; } - LogUtils.d(TAG, statusDesc); - notifyAllGpsStatusListeners(statusDesc); - updateNotificationGpsStatus(statusDesc); + handleGpsStatusChange(statusDesc); } } @@ -736,10 +779,7 @@ public class MainService extends Service { // GPS启用时更新状态+通知+重启定位(Java 7 基础逻辑) if (provider.equals(LocationManager.GPS_PROVIDER)) { isGpsEnabled = true; - String statusDesc = "GPS已开启(用户手动打开)"; - LogUtils.d(TAG, statusDesc); - notifyAllGpsStatusListeners(statusDesc); - updateNotificationGpsStatus("GPS已开启,正在获取位置..."); + handleGpsStatusChange("GPS已开启(用户手动打开)"); startGpsLocation(); } } @@ -750,10 +790,7 @@ public class MainService extends Service { if (provider.equals(LocationManager.GPS_PROVIDER)) { isGpsEnabled = false; _mCurrentGpsPosition = null; - String statusDesc = "GPS已关闭(用户手动关闭)"; - LogUtils.w(TAG, statusDesc); - notifyAllGpsStatusListeners(statusDesc); - updateNotificationGpsStatus("GPS已关闭,请在设置中开启"); + handleGpsStatusChange("GPS已关闭(用户手动关闭)"); ToastUtils.show("GPS已关闭,无法获取位置,请在设置中开启"); } } @@ -801,8 +838,27 @@ public class MainService extends Service { /** * 启动GPS定位(Java 7 异常处理,无try-with-resources,显式捕获SecurityException) + * 【关键修改】根据应用空转状态切换数据源(IdleGpsService 或 系统GPS) */ private void startGpsLocation() { + // 检查空转状态:如果处于空转,使用 IdleGpsService + if (App.isAppIdleRunning()) { + if (isListeningToIdleGps) return; // 已在监听空转GPS,无需重复注册 + stopGpsLocation(); // 停止系统GPS监听(如果正在运行) + IdleGpsService.getInstance().registerGpsUpdateListener(mIdleGpsListener); + isListeningToIdleGps = true; + LogUtils.d(TAG, "启动GPS定位:使用空转模拟数据"); + handleGpsStatusChange("空转GPS监听中..."); + return; + } + + // 系统GPS逻辑 + if (isListeningToIdleGps) { + // 之前是空转,现在切换到系统GPS + IdleGpsService.getInstance().unregisterGpsUpdateListener(mIdleGpsListener); + isListeningToIdleGps = false; + } + if (!checkGpsReady()) { return; } @@ -824,12 +880,11 @@ public class MainService extends Service { lastGpsPos.setLatitude(lastKnownLocation.getLatitude()); lastGpsPos.setLongitude(lastKnownLocation.getLongitude()); lastGpsPos.setPositionId("CURRENT_GPS_POS"); - syncCurrentGpsPosition(lastGpsPos); + handleGpsPositionUpdate(lastGpsPos); LogUtils.d(TAG, "已获取缓存GPS位置:纬度=" + lastKnownLocation.getLatitude()); } else { String tip = "无缓存GPS位置,等待实时定位..."; - LogUtils.d(TAG, tip); - notifyAllGpsStatusListeners(tip); + handleGpsStatusChange(tip); updateNotificationGpsStatus("GPS搜索中(请移至开阔地带)"); } @@ -837,33 +892,40 @@ public class MainService extends Service { // 定位权限异常(Java 7 显式捕获,无Lambda异常处理) String error = "启动GPS失败(权限异常):" + e.getMessage(); LogUtils.e(TAG, error); - notifyAllGpsStatusListeners(error); + handleGpsStatusChange(error); isGpsPermissionGranted = false; updateNotificationGpsStatus("定位权限异常,无法获取GPS"); } catch (Exception e) { // 其他异常(如LocationManager为空、系统服务异常等) String error = "启动GPS失败:" + e.getMessage(); LogUtils.e(TAG, error); - notifyAllGpsStatusListeners(error); + handleGpsStatusChange(error); updateNotificationGpsStatus("GPS启动失败,尝试重试..."); } } /** * 停止GPS定位(Java 7 异常处理,移除监听器避免内存泄漏) + * 【关键修改】根据当前监听源停止对应的服务 */ private void stopGpsLocation() { - // 校验参数:避免空指针+权限未授予时调用 - if (mLocationManager != null && mGpsLocationListener != null && isGpsPermissionGranted) { - try { - mLocationManager.removeUpdates(mGpsLocationListener); - String tip = "GPS定位已停止(移除监听器)"; - LogUtils.d(TAG, tip); - notifyAllGpsStatusListeners(tip); - } catch (Exception e) { - String error = "停止GPS失败:" + e.getMessage(); - LogUtils.e(TAG, error); - notifyAllGpsStatusListeners(error); + if (isListeningToIdleGps) { + IdleGpsService.getInstance().unregisterGpsUpdateListener(mIdleGpsListener); + isListeningToIdleGps = false; + LogUtils.d(TAG, "停止GPS定位:已注销空转GPS监听"); + } else { + // 校验参数:避免空指针+权限未授予时调用 + if (mLocationManager != null && mGpsLocationListener != null && isGpsPermissionGranted) { + try { + mLocationManager.removeUpdates(mGpsLocationListener); + String tip = "GPS定位已停止(移除监听器)"; + LogUtils.d(TAG, tip); + handleGpsStatusChange(tip); + } catch (Exception e) { + String error = "停止GPS失败:" + e.getMessage(); + LogUtils.e(TAG, error); + handleGpsStatusChange(error); + } } } } @@ -915,14 +977,19 @@ public class MainService extends Service { */ void updateNotificationGpsStatus(final String statusText) { if (_mIsServiceRunning) { + String text = statusText; + if (App.isAppIdleRunning()) { + text += " [IDLE RUNNING]"; + } + final String finalText = text; // 判断当前线程是否为主线程,避免UI操作在子线程 if (Looper.myLooper() == Looper.getMainLooper()) { - NotificationUtil.updateForegroundServiceStatus(this, statusText); + NotificationUtil.updateForegroundServiceStatus(this, finalText); } else { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { - NotificationUtil.updateForegroundServiceStatus(MainService.this, statusText); + NotificationUtil.updateForegroundServiceStatus(MainService.this, finalText); } }); }