diff --git a/gpsrelaysentinel/build.gradle b/gpsrelaysentinel/build.gradle index 8b8ddd9..1106541 100644 --- a/gpsrelaysentinel/build.gradle +++ b/gpsrelaysentinel/build.gradle @@ -115,8 +115,8 @@ dependencies { implementation 'com.termux:termux-shared:0.118.0' // WinBoLL库 nexus.winboll.cc 地址 - api 'cc.winboll.studio:libaes:15.15.2' - api 'cc.winboll.studio:libappbase:15.15.11' + api 'cc.winboll.studio:libaes:15.15.9' + api 'cc.winboll.studio:libappbase:15.15.21' // WinBoLL备用库 jitpack.io 地址 //api 'com.github.ZhanGSKen:AES:aes-v15.15.7' diff --git a/gpsrelaysentinel/build.properties b/gpsrelaysentinel/build.properties index 5095505..0f99e65 100644 --- a/gpsrelaysentinel/build.properties +++ b/gpsrelaysentinel/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Thu May 07 10:59:47 CST 2026 +#Thu May 07 15:04:39 CST 2026 stageCount=27 libraryProject= baseVersion=15.11 publishVersion=15.11.26 -buildCount=31 +buildCount=33 baseBetaVersion=15.11.27 diff --git a/gpsrelaysentinel/src/main/AndroidManifest.xml b/gpsrelaysentinel/src/main/AndroidManifest.xml index 7e89b56..1d557fb 100644 --- a/gpsrelaysentinel/src/main/AndroidManifest.xml +++ b/gpsrelaysentinel/src/main/AndroidManifest.xml @@ -3,10 +3,17 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="cc.winboll.studio.gpsrelaysentinel"> - - - - + + + + + + + + + + + - + android:exported="false"/> + + + + + + - + \ No newline at end of file diff --git a/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService1.java b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService1.java new file mode 100644 index 0000000..d65cce0 --- /dev/null +++ b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService1.java @@ -0,0 +1,27 @@ +package cc.winboll.studio.gpsrelaysentinel; + +import android.content.Intent; + +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libgpsrelaysentinel.model.GpsSubscribeMsg; +import cc.winboll.studio.libgpsrelaysentinel.model.LocationPoint; +import cc.winboll.studio.libgpsrelaysentinel.service.GpsSubscribeReceiverService; + +public final class GpsReceiverChildService1 extends GpsSubscribeReceiverService { + + public static final String TAG = "GpsReceiverChildService1"; + + @Override + public void onReceiveGpsData(LocationPoint point, GpsSubscribeMsg config) { + super.onReceiveGpsData(point, config); + //当前独立接收日志 + LogUtils.d(TAG,"独立接收服务1 成功收到GPS消息"); + LogUtils.d(TAG,"纬度:"+point.getLatitude()+" 经度:"+point.getLongitude()); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return START_NOT_STICKY; + } +} + diff --git a/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService2.java b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService2.java new file mode 100644 index 0000000..d058f92 --- /dev/null +++ b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService2.java @@ -0,0 +1,26 @@ +package cc.winboll.studio.gpsrelaysentinel; + +import android.content.Intent; + +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libgpsrelaysentinel.model.GpsSubscribeMsg; +import cc.winboll.studio.libgpsrelaysentinel.model.LocationPoint; +import cc.winboll.studio.libgpsrelaysentinel.service.GpsSubscribeReceiverService; + +public final class GpsReceiverChildService2 extends GpsSubscribeReceiverService { + + public static final String TAG = "GpsReceiverChildService2"; + + @Override + public void onReceiveGpsData(LocationPoint point, GpsSubscribeMsg config) { + super.onReceiveGpsData(point, config); + LogUtils.d(TAG,"独立接收服务2 成功收到GPS消息"); + LogUtils.d(TAG,"纬度:"+point.getLatitude()+" 经度:"+point.getLongitude()); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return START_NOT_STICKY; + } +} + diff --git a/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService3.java b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService3.java new file mode 100644 index 0000000..fb04c12 --- /dev/null +++ b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/GpsReceiverChildService3.java @@ -0,0 +1,26 @@ +package cc.winboll.studio.gpsrelaysentinel; + +import android.content.Intent; + +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libgpsrelaysentinel.model.GpsSubscribeMsg; +import cc.winboll.studio.libgpsrelaysentinel.model.LocationPoint; +import cc.winboll.studio.libgpsrelaysentinel.service.GpsSubscribeReceiverService; + +public final class GpsReceiverChildService3 extends GpsSubscribeReceiverService { + + public static final String TAG = "GpsReceiverChildService3"; + + @Override + public void onReceiveGpsData(LocationPoint point, GpsSubscribeMsg config) { + super.onReceiveGpsData(point, config); + LogUtils.d(TAG,"独立接收服务3 成功收到GPS消息"); + LogUtils.d(TAG,"纬度:"+point.getLatitude()+" 经度:"+point.getLongitude()); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return START_NOT_STICKY; + } +} + diff --git a/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/MainActivity.java b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/MainActivity.java index 9026924..9cf9284 100644 --- a/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/MainActivity.java +++ b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/MainActivity.java @@ -5,18 +5,58 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.Spinner; import android.widget.Switch; +import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import cc.winboll.studio.gpsrelaysentinel.R; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogView; import cc.winboll.studio.libappbase.ToastUtils; -public class MainActivity extends AppCompatActivity { +/** + * WinBoLL Studio + * GPSRelaySentinel 主控制页面 + * Java7 | API26~30 + * 新增:模拟模式勾选控制 + 按钮互斥可用状态 + */ +public final class MainActivity extends AppCompatActivity { - LogView mLogView; - Switch mSwitchService; + //原有控件 + private Toolbar mToolbar; + private LogView mLogView; + private Switch mSwitchService; + + //新增 + private CheckBox mCheckBoxSimMode; + private Button btnSendLastGps; + private Spinner spinDirection; + private EditText etSimDistance; + private TextView tvTargetPreview; + private Button btnSimSend; + + //全局模式标识 供给MainService判断 + public static boolean IS_GPS_SIM_MODE = false; + + //最后真实GPS坐标 + public static double lastLat = 30.5928; + public static double lastLng = 114.3055; + + //全局模拟坐标 供给MainService使用 + public static double simLat = 30.5928; + public static double simLng = 114.3055; + + //方位对应角度(正北0° 顺时针) + private double currentAngle = 0.0D; + + //权限请求常量 private static final int REQUEST_LOCATION_PERMISSION = 1; @Override @@ -24,78 +64,273 @@ public class MainActivity extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - mLogView = findViewById(R.id.logview); - mSwitchService = findViewById(R.id.switch_service); - - // 根据当前权限状态初始化switch - mSwitchService.setChecked(hasLocationPermission()); - - mSwitchService.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - // 打开时检查权限 - if (hasLocationPermission()) { - startService(); - } else { - // 没有权限,申请权限 - requestLocationPermission(); - // 暂时不打开switch,等权限申请结果 - mSwitchService.setChecked(false); - } - } else { - stopService(); - } - } - }); + initView(); + initToolbar(); + initSwitchEvent(); + initSimPanelEvent(); + initSimModeCheck(); ToastUtils.show("onCreate"); } + /** + * 全部控件绑定 + */ + private void initView() { + //原有 + mToolbar = findViewById(R.id.toolbar); + mLogView = findViewById(R.id.logview); + mSwitchService = findViewById(R.id.switch_service); + + //新增 + mCheckBoxSimMode = findViewById(R.id.checkbox_sim_mode); + btnSendLastGps = findViewById(R.id.btn_send_last_gps); + spinDirection = findViewById(R.id.spin_direction); + etSimDistance = findViewById(R.id.et_sim_distance); + tvTargetPreview = findViewById(R.id.tv_target_point_preview); + btnSimSend = findViewById(R.id.btn_sim_send_gps); + + //方位下拉 全局灰色文字 + ArrayAdapter dirAdapter = ArrayAdapter.createFromResource( + this, + R.array.direction_list, + R.layout.spinner_item_gray + ); + dirAdapter.setDropDownViewResource(R.layout.spinner_item_gray); + spinDirection.setAdapter(dirAdapter); + + //初始化开关状态 + mSwitchService.setChecked(hasLocationPermission()); + refreshButtonEnableStatus(); + refreshTargetPreview(); + } + + //模拟勾选框监听 + private void initSimModeCheck() { + mCheckBoxSimMode.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + IS_GPS_SIM_MODE = isChecked; + refreshButtonEnableStatus(); + if (isChecked) { + ToastUtils.show("已进入GPS模拟模式"); + } else { + ToastUtils.show("退出模拟模式,使用真实GPS"); + } + } + }); + } + + //刷新按钮互斥可用状态 + private void refreshButtonEnableStatus() { + if (IS_GPS_SIM_MODE) { + //模拟模式:真实按钮禁用、模拟按钮可用 + btnSendLastGps.setEnabled(false); + btnSimSend.setEnabled(true); + } else { + //正常模式:真实可用、模拟禁用 + btnSendLastGps.setEnabled(true); + btnSimSend.setEnabled(false); + } + } + + /** + * 初始化标题栏 + */ + private void initToolbar() { + setSupportActionBar(mToolbar); + } + + /** + * GPS服务开关监听 + */ + private void initSwitchEvent() { + mSwitchService.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + if (hasLocationPermission()) { + startGpsService(); + } else { + requestLocationPermission(); + mSwitchService.setChecked(false); + } + } else { + stopGpsService(); + } + } + }); + } + + /** + * 模拟发送面板 全部事件初始化 + */ + private void initSimPanelEvent() { + //1.原按钮:发送最后一条真实GPS + btnSendLastGps.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + sendLastRealGpsBroadcast(); + } + }); + + //2.方位下拉选择 -> 切换角度并刷新预览 + spinDirection.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + currentAngle = getDirectionAngle(position); + refreshTargetPreview(); + } + + @Override + public void onNothingSelected(AdapterView parent) {} + }); + + //3.距离输入变化自动预览 + etSimDistance.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!hasFocus) { + refreshTargetPreview(); + } + } + }); + + //4.模拟发送按钮:计算偏移并赋值全局模拟坐标 + btnSimSend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + saveSimGpsData(); + ToastUtils.show("已设置当前模拟GPS坐标"); + } + }); + } + + /** + * 保存模拟坐标到全局静态变量 供给MainService使用 + */ + private void saveSimGpsData() { + String disText = etSimDistance.getText().toString().trim(); + double distance = 10D; + try { + distance = Double.parseDouble(disText); + } catch (Exception e) { + ToastUtils.show("请输入合法距离"); + return; + } + double[] target = calculateOffsetLatLng(lastLat, lastLng, distance, currentAngle); + simLat = target[0]; + simLng = target[1]; + refreshTargetPreview(); + } + + /** + * 根据下拉position获取对应方位角度 + */ + private double getDirectionAngle(int pos) { + switch (pos) { + case 0: return 0.0D; //正北 + case 1: return 180.0D; //正南 + case 2: return 90.0D; //正东 + case 3: return 270.0D; //正西 + case 4: return 45.0D; //东北 + case 5: return 315.0D; //西北 + case 6: return 135.0D; //东南 + case 7: return 225.0D; //西南 + default:return 0.0D; + } + } + + /** + * 根据基准坐标+距离+角度 计算偏移经纬度 + */ + private double[] calculateOffsetLatLng(double lat, double lng, double distanceMeter, double angle) { + double radAngle = Math.toRadians(angle); + double radLat = Math.toRadians(lat); + + double meterPerLat = 111320D; + double meterPerLng = Math.cos(radLat) * 111320D; + + double offsetLat = (distanceMeter * Math.cos(radAngle)) / meterPerLat; + double offsetLng = (distanceMeter * Math.sin(radAngle)) / meterPerLng; + + return new double[]{lat + offsetLat , lng + offsetLng}; + } + + /** + * 刷新目标坐标预览 + */ + private void refreshTargetPreview() { + String disText = etSimDistance.getText().toString().trim(); + double distance = 10D; + try { + distance = Double.parseDouble(disText); + } catch (Exception e) {} + + double[] target = calculateOffsetLatLng(lastLat, lastLng, distance, currentAngle); + String info = "目标模拟坐标:" + + String.format("%.6f", target[0]) + + " , " + + String.format("%.6f", target[1]); + tvTargetPreview.setText(info); + } + + /** + * 发送【最后真实GPS】广播 + */ + private void sendLastRealGpsBroadcast() { + Intent broadcast = new Intent("GPS_DATA_BROADCAST"); + broadcast.putExtra("isSim", false); + broadcast.putExtra("lat", lastLat); + broadcast.putExtra("lng", lastLng); + sendBroadcast(broadcast); + LogUtils.d("GPS_SEND", "发送真实GPS -> lat:" + lastLat + " lng:" + lastLng); + } + + //—————— 原有权限 & 服务启停 完全原样保留 —————— + private boolean hasLocationPermission() { - boolean hasBasic = checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED - || checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; - if (hasBasic && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + boolean basicPermission = checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED + || checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; + + if (basicPermission && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { return checkSelfPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED; } - return hasBasic; + return basicPermission; } private void requestLocationPermission() { + String[] permissionArray; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { - String[] permissions = new String[] { - android.Manifest.permission.ACCESS_FINE_LOCATION, - android.Manifest.permission.ACCESS_COARSE_LOCATION, - android.Manifest.permission.ACCESS_BACKGROUND_LOCATION + permissionArray = new String[]{ + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_COARSE_LOCATION, + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION }; - requestPermissions(permissions, REQUEST_LOCATION_PERMISSION); } else { - String[] permissions = new String[] { - android.Manifest.permission.ACCESS_FINE_LOCATION, - android.Manifest.permission.ACCESS_COARSE_LOCATION + permissionArray = new String[]{ + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_COARSE_LOCATION }; - requestPermissions(permissions, REQUEST_LOCATION_PERMISSION); } + requestPermissions(permissionArray, REQUEST_LOCATION_PERMISSION); } - private void startService() { - Intent intent = new Intent(MainActivity.this, MainService.class); - startForegroundService(intent); + private void startGpsService() { + Intent serviceIntent = new Intent(MainActivity.this, MainService.class); + startForegroundService(serviceIntent); ToastUtils.show("GPS Service started"); LogUtils.d(MainService.TAG, "GPS Service started from MainActivity"); } - private void stopService() { - // 先设置SP标记为不启用 - MainActivity.this.getSharedPreferences(MainService.PREF_NAME, Context.MODE_PRIVATE) - .edit() - .putBoolean(MainService.KEY_SERVICE_ENABLED, false) - .apply(); - Intent intent = new Intent(MainActivity.this, MainService.class); - stopService(intent); + private void stopGpsService() { + getSharedPreferences(MainService.PREF_NAME, Context.MODE_PRIVATE) + .edit() + .putBoolean(MainService.KEY_SERVICE_ENABLED, false) + .apply(); + + Intent serviceIntent = new Intent(MainActivity.this, MainService.class); + stopService(serviceIntent); ToastUtils.show("GPS Service stopped"); LogUtils.d(MainService.TAG, "GPS Service stopped from MainActivity"); } @@ -105,9 +340,8 @@ public class MainActivity extends AppCompatActivity { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_LOCATION_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - // 权限申请成功,启动服务 mSwitchService.setChecked(true); - startService(); + startGpsService(); } else { ToastUtils.show("需要位置权限才能使用GPS服务"); mSwitchService.setChecked(false); @@ -120,8 +354,5 @@ public class MainActivity extends AppCompatActivity { super.onResume(); mLogView.start(); } - -// public void onLibraryActivity(View view) { -// startActivity(new Intent(this, LibraryActivity.class)); -// } } + diff --git a/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/MainService.java b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/MainService.java index f1aded1..cc4f25a 100644 --- a/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/MainService.java +++ b/gpsrelaysentinel/src/main/java/cc/winboll/studio/gpsrelaysentinel/MainService.java @@ -12,154 +12,236 @@ import android.location.LocationManager; import android.os.Build; import android.os.Bundle; import android.os.IBinder; + import androidx.core.app.NotificationCompat; + import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libgpsrelaysentinel.manager.GpsSubscribeManager; import cc.winboll.studio.libgpsrelaysentinel.manager.SubscribeLocationManager; import cc.winboll.studio.libgpsrelaysentinel.model.GpsSubscribeMsg; + import java.util.Map; -public class MainService extends Service { +/** + * WinBoLL Studio + * GPS定位核心前台服务 + * 负责GPS持续监听、订阅者步长判断、基准坐标刷新、前台常驻通知 + * Java7 | API26~30 + * 新增:实时同步最新GPS到MainActivity静态坐标 + */ +public final class MainService extends Service { + //日志标签 public static final String TAG = "MainService"; - private LocationManager mLocationManager; - private LocationListener mLocationListener; - private boolean mIsRunning = false; - private NotificationManager mNotificationManager; - private NotificationCompat.Builder mNotificationBuilder; + + //前台通知常量 private static final String CHANNEL_ID = "gps_relay_channel"; private static final int NOTIFICATION_ID = 1; + + //SP配置常量 static final String PREF_NAME = "gps_relay_service_prefs"; static final String KEY_SERVICE_ENABLED = "service_enabled"; - private int mGpsCount = 0; + + //系统定位 & 通知控件 + private LocationManager mLocationManager; + private LocationListener mLocationListener; + private NotificationManager mNotificationManager; + private NotificationCompat.Builder mNotificationBuilder; + + //运行状态 & 计数 + private boolean mIsRunning = false; + private int mGpsLocationCount = 0; + + //订阅管理器 + private GpsSubscribeManager mSubscribeManager; + private SubscribeLocationManager mLocationRuleManager; + @Override public void onCreate() { super.onCreate(); LogUtils.d(TAG, "Service onCreate"); - mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - createNotificationChannel(); - if (isServiceEnabled()) { - LogUtils.d(TAG, "Service was enabled, starting GPS updates"); - run(); + + initManager(); + initNotificationConfig(); + + //上次开启状态则自动重启GPS监听 + if (checkServiceEnableStatus()) { + LogUtils.d(TAG, "历史服务已启用,自动启动GPS监听"); + startGpsLocationListen(); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { LogUtils.d(TAG, "Service onStartCommand"); - setServiceEnabled(true); - run(); + saveServiceEnableStatus(true); + startGpsLocationListen(); return START_STICKY; } - private boolean isServiceEnabled() { - return getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).getBoolean(KEY_SERVICE_ENABLED, false); + /** + * 初始化订阅规则管理器 + */ + private void initManager() { + mSubscribeManager = GpsSubscribeManager.getInstance(); + mLocationRuleManager = SubscribeLocationManager.getInstance(); } - private void setServiceEnabled(boolean enabled) { - getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit().putBoolean(KEY_SERVICE_ENABLED, enabled).apply(); - LogUtils.d(TAG, "Service enabled set to: " + enabled); + /** + * 初始化通知渠道与管理类 + */ + private void initNotificationConfig() { + mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + createSystemNotificationChannel(); } - private void run() { + /** + * 读取服务启用状态 + */ + private boolean checkServiceEnableStatus() { + return getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getBoolean(KEY_SERVICE_ENABLED, false); + } + + /** + * 保存服务启用状态 + */ + private void saveServiceEnableStatus(boolean enabled) { + getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .edit() + .putBoolean(KEY_SERVICE_ENABLED, enabled) + .apply(); + LogUtils.d(TAG, "服务启用状态已设置:" + enabled); + } + + /** + * 启动GPS定位监听核心逻辑 + */ + private void startGpsLocationListen() { if (mIsRunning) { - LogUtils.d(TAG, "GPS updates already running"); + LogUtils.d(TAG, "GPS监听已正在运行,无需重复启动"); return; } + mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); + initLocationListener(); + + try { + if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { + //定位间隔:1000毫秒 / 最小位移1米 + mLocationManager.requestLocationUpdates( + LocationManager.GPS_PROVIDER, + 1000, + 1, + mLocationListener + ); + mIsRunning = true; + startServiceForegroundNotification(); + LogUtils.d(TAG, "GPS定位监听已成功注册"); + } + } catch (SecurityException e) { + LogUtils.e(TAG, "定位权限缺失,监听启动失败:" + e.getMessage()); + } + } + + /** + * 初始化定位监听回调 + */ + private void initLocationListener() { mLocationListener = new LocationListener() { @Override public void onLocationChanged(Location location) { - mGpsCount++; - String gpsInfo = "Lat: " + location.getLatitude() + ", Lng: " + location.getLongitude(); - LogUtils.d(TAG, "Location changed: " + gpsInfo); - updateNotification(gpsInfo); - - //管理器初始化 - GpsSubscribeManager subscribeManager = GpsSubscribeManager.getInstance(); - SubscribeLocationManager locationManager = SubscribeLocationManager.getInstance(); - - //遍历所有订阅者做距离判断+定点更新 - Map subscribeMap = subscribeManager.getSubscribeMap(); - for (Map.Entry entry : subscribeMap.entrySet()) { - String sid = entry.getKey(); - GpsSubscribeMsg subscribeMsg = entry.getValue(); - - double nowLat = location.getLatitude(); - double nowLng = location.getLongitude(); - - //判断是否满足推送 - boolean canPush = locationManager.isNeedPush(sid, nowLat, nowLng); - if (canPush) { - //执行发送GPS广播 - //sendGpsBroadcast(...); - - //推送成功立刻刷新订阅者基准坐标 - locationManager.updateSubscriberPoint(sid, nowLat, nowLng); - } - } - + handleLocationUpdate(location); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { - LogUtils.d(TAG, "Status changed: " + provider + ", status: " + status); + LogUtils.d(TAG, "GPS状态变更 -> 提供者:" + provider + " 状态:" + status); } @Override public void onProviderEnabled(String provider) { - LogUtils.d(TAG, "Provider enabled: " + provider); + LogUtils.d(TAG, "GPS提供者已启用:" + provider); } @Override public void onProviderDisabled(String provider) { - LogUtils.d(TAG, "Provider disabled: " + provider); + LogUtils.d(TAG, "GPS提供者已禁用:" + provider); } }; + } - try { - if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { - mLocationManager.requestLocationUpdates( - LocationManager.GPS_PROVIDER, - 1000, - 1, - mLocationListener - ); - LogUtils.d(TAG, "GPS location updates requested"); - mIsRunning = true; - startForegroundNotification(); + /** + * 处理每次定位刷新|核心:步长判断 + 基准坐标更新 + * 新增:同步最新坐标到MainActivity静态变量 + */ + private void handleLocationUpdate(Location location) { + mGpsLocationCount ++; + String locationInfo = "纬度:" + location.getLatitude() + " , 经度:" + location.getLongitude(); + LogUtils.d(TAG, "定位刷新 -> " + locationInfo); + + //========== 新增关键代码:实时同步最新真实GPS坐标 ========== + MainActivity.lastLat = location.getLatitude(); + MainActivity.lastLng = location.getLongitude(); + //========================================================== + + //更新前台通知文案 + updateForegroundNotification(locationInfo); + + //遍历全部订阅者进行推送规则判断 + Map subscribeAllMap = mSubscribeManager.getSubscribeMap(); + for (Map.Entry entry : subscribeAllMap.entrySet()) { + final String subscribeSid = entry.getKey(); + final GpsSubscribeMsg subscribeConfig = entry.getValue(); + + double currentLat = location.getLatitude(); + double currentLng = location.getLongitude(); + + //判断是否满足推送条件(全订阅/步长阈值) + boolean allowPush = mLocationRuleManager.isNeedPush(subscribeSid, currentLat, currentLng); + if (allowPush) { + //推送成功后刷新该订阅者基准定点坐标 + mLocationRuleManager.updateSubscriberPoint(subscribeSid, currentLat, currentLng); } - } catch (SecurityException e) { - LogUtils.e(TAG, "Permission denied: " + e.getMessage()); } } - private void createNotificationChannel() { + /** + * 创建系统通知渠道 + */ + private void createSystemNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, - "GPS Relay Service", - NotificationManager.IMPORTANCE_LOW + NotificationChannel notificationChannel = new NotificationChannel( + CHANNEL_ID, + "GPS Relay Service", + NotificationManager.IMPORTANCE_LOW ); - channel.setDescription("GPS Relay Sentinel service channel"); - mNotificationManager.createNotificationChannel(channel); + notificationChannel.setDescription("GPSRelaySentinel 后台常驻服务通知"); + mNotificationManager.createNotificationChannel(notificationChannel); } } - private void startForegroundNotification() { + /** + * 开启前台常驻通知 + */ + private void startServiceForegroundNotification() { mNotificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle("GPS Relay Service") - .setContentText("Waiting for GPS data...") - .setSmallIcon(android.R.drawable.ic_menu_mylocation) - .setOngoing(true); + .setContentTitle("GPS 中继服务") + .setContentText("等待GPS定位数据...") + .setSmallIcon(android.R.drawable.ic_menu_mylocation) + .setOngoing(true); + Notification notification = mNotificationBuilder.build(); startForeground(NOTIFICATION_ID, notification); } - private void updateNotification(String gpsInfo) { + /** + * 动态更新通知内容 + */ + private void updateForegroundNotification(String locationText) { if (mNotificationBuilder != null) { - mNotificationBuilder.setContentText(gpsInfo + " | Count: " + mGpsCount); + mNotificationBuilder.setContentText(locationText + " | 定位次数:" + mGpsLocationCount); mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build()); } } @@ -172,14 +254,16 @@ public class MainService extends Service { @Override public void onDestroy() { super.onDestroy(); + //注销定位监听 if (mLocationManager != null && mLocationListener != null) { try { mLocationManager.removeUpdates(mLocationListener); } catch (SecurityException e) { - LogUtils.e(TAG, "Permission denied when removing updates: " + e.getMessage()); + LogUtils.e(TAG, "移除定位监听权限异常:" + e.getMessage()); } } mIsRunning = false; - LogUtils.d(TAG, "Service onDestroy"); + LogUtils.d(TAG, "MainService 已销毁,GPS监听已停止"); } } + diff --git a/gpsrelaysentinel/src/main/res/drawable/border_gray.xml b/gpsrelaysentinel/src/main/res/drawable/border_gray.xml new file mode 100644 index 0000000..a8c93bd --- /dev/null +++ b/gpsrelaysentinel/src/main/res/drawable/border_gray.xml @@ -0,0 +1,13 @@ + + + android:shape="rectangle"> + + + + + + + + diff --git a/gpsrelaysentinel/src/main/res/layout/activity_main.xml b/gpsrelaysentinel/src/main/res/layout/activity_main.xml index 6c15ec1..4e9f9bc 100644 --- a/gpsrelaysentinel/src/main/res/layout/activity_main.xml +++ b/gpsrelaysentinel/src/main/res/layout/activity_main.xml @@ -4,7 +4,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + android:background="#1c1c1c"> - + + android:layout_height="wrap_content" + android:gravity="center_horizontal"> + + + android:orientation="horizontal" + android:gravity="center_vertical" + android:layout_marginTop="8dp" + android:spacing="12dp"> - + android:text="模拟模式" + android:textColor="#999999" + android:padding="4dp" + android:background="@drawable/border_gray" + android:textSize="11sp"/> +