267 lines
12 KiB
Java
267 lines
12 KiB
Java
package cc.winboll.studio.positions;
|
||
|
||
import android.Manifest;
|
||
import android.app.Activity;
|
||
import android.content.Intent;
|
||
import android.content.pm.PackageManager;
|
||
import android.os.Build;
|
||
import android.os.Bundle;
|
||
import android.view.View;
|
||
import android.widget.Button;
|
||
import android.widget.CompoundButton;
|
||
import android.widget.Switch;
|
||
import android.widget.Toast;
|
||
import androidx.annotation.NonNull;
|
||
import androidx.appcompat.widget.Toolbar;
|
||
import androidx.core.content.ContextCompat;
|
||
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||
import cc.winboll.studio.libappbase.LogUtils;
|
||
import cc.winboll.studio.positions.activities.LocationActivity;
|
||
import cc.winboll.studio.positions.activities.WinBoLLActivity;
|
||
import cc.winboll.studio.positions.services.MainService;
|
||
import cc.winboll.studio.positions.utils.AppConfigsUtil;
|
||
|
||
/**
|
||
* 主页面:仅负责
|
||
* 1. 位置服务启动/停止(通过 Switch 开关控制)
|
||
* 2. 跳转至“位置管理页(LocationActivity)”和“日志页(LogActivity)”
|
||
* 3. Java 7 语法适配:无 Lambda、显式接口实现、兼容低版本
|
||
*/
|
||
public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||
public static final String TAG = "MainActivity";
|
||
// 权限请求码(建议定义为类常量,避免魔法值)
|
||
private static final int REQUEST_LOCATION_PERMISSIONS = 1001;
|
||
private static final int REQUEST_BACKGROUND_LOCATION_PERMISSION = 1002;
|
||
|
||
// UI 控件:服务控制开关、顶部工具栏
|
||
private Switch mServiceSwitch;
|
||
private Button mManagePositionsButton;
|
||
private Toolbar mToolbar;
|
||
// 服务相关:服务实例、绑定状态标记
|
||
//private DistanceRefreshService mDistanceService;
|
||
private boolean isServiceBound = false;
|
||
|
||
|
||
@Override
|
||
public Activity getActivity() {
|
||
return this;
|
||
}
|
||
|
||
@Override
|
||
public String getTag() {
|
||
return TAG;
|
||
}
|
||
|
||
// ---------------------- 服务连接回调(仅用于获取服务状态,不依赖服务执行核心逻辑) ----------------------
|
||
// private final ServiceConnection mServiceConn = new ServiceConnection() {
|
||
// /**
|
||
// * 服务绑定成功:获取服务实例,同步开关状态(以服务实际状态为准)
|
||
// */
|
||
// @Override
|
||
// public void onServiceConnected(ComponentName name, IBinder service) {
|
||
// // Java 7 显式强转 Binder 实例(确保类型匹配,避免ClassCastException)
|
||
// DistanceRefreshService.DistanceBinder binder = (DistanceRefreshService.DistanceBinder) service;
|
||
// mDistanceService = binder.getService();
|
||
// isServiceBound = true;
|
||
// }
|
||
//
|
||
// /**
|
||
// * 服务意外断开(如服务崩溃):重置服务实例和绑定状态
|
||
// */
|
||
// @Override
|
||
// public void onServiceDisconnected(ComponentName name) {
|
||
// mDistanceService = null;
|
||
// isServiceBound = false;
|
||
// }
|
||
// };
|
||
|
||
// ---------------------- Activity 生命周期(核心:初始化UI、申请权限、绑定服务、释放资源) ----------------------
|
||
@Override
|
||
protected void onCreate(Bundle savedInstanceState) {
|
||
super.onCreate(savedInstanceState);
|
||
setContentView(R.layout.activity_main); // 关联主页面布局
|
||
|
||
// 1. 初始化顶部 Toolbar(保留原逻辑,设置页面标题)
|
||
initToolbar();
|
||
// 2. 初始化其他控件
|
||
initViews();
|
||
// 3. 检查并申请位置权限(含后台GPS权限,确保服务启动前权限就绪)
|
||
if (!checkLocationPermissions()) {
|
||
requestLocationPermissions();
|
||
}
|
||
// 4. 绑定服务(仅用于获取服务实时状态,不影响服务独立运行)
|
||
//bindDistanceService();
|
||
}
|
||
|
||
@Override
|
||
protected void onDestroy() {
|
||
super.onDestroy();
|
||
// 页面销毁时解绑服务,避免Activity与服务相互引用导致内存泄漏
|
||
// if (isServiceBound) {
|
||
// unbindService(mServiceConn);
|
||
// isServiceBound = false;
|
||
// mDistanceService = null;
|
||
// }
|
||
}
|
||
|
||
// ---------------------- 核心功能1:初始化UI组件(Toolbar + 服务开关) ----------------------
|
||
/**
|
||
* 初始化顶部 Toolbar,设置页面标题
|
||
*/
|
||
private void initToolbar() {
|
||
mToolbar = (Toolbar) findViewById(R.id.toolbar); // Java 7 显式 findViewById + 强转
|
||
setSupportActionBar(mToolbar);
|
||
// 给ActionBar设置标题(先判断非空,避免空指针异常)
|
||
if (getSupportActionBar() != null) {
|
||
getSupportActionBar().setTitle(getString(R.string.app_name));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 初始化服务控制开关:读取SP状态、绑定点击事件(含权限检查)
|
||
*/
|
||
private void initViews() {
|
||
mServiceSwitch = (Switch) findViewById(R.id.switch_service_control); // 显式强转
|
||
mServiceSwitch.setChecked(AppConfigsUtil.getInstance(this).isEnableMainService(true));
|
||
|
||
mManagePositionsButton = (Button) findViewById(R.id.btn_manage_positions);
|
||
mManagePositionsButton.setEnabled(mServiceSwitch.isChecked());
|
||
|
||
// Java 7 用匿名内部类实现 CompoundButton.OnCheckedChangeListener
|
||
mServiceSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||
@Override
|
||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||
// 开关打开前先检查权限:无权限则终止操作、重置开关、引导申请
|
||
if (isChecked && !checkLocationPermissions()) {
|
||
requestLocationPermissions();
|
||
return;
|
||
}
|
||
|
||
// 权限就绪:执行服务启停逻辑
|
||
if (isChecked) {
|
||
LogUtils.d(TAG, "设置启动服务");
|
||
AppConfigsUtil.getInstance(MainActivity.this).setIsEnableMainService(true);
|
||
// 启动服务(startService确保服务独立运行,不受Activity绑定影响)
|
||
startService(new Intent(MainActivity.this, MainService.class));
|
||
} else {
|
||
LogUtils.d(TAG, "设置关闭服务");
|
||
AppConfigsUtil.getInstance(MainActivity.this).setIsEnableMainService(false);
|
||
// 停止服务前先解绑,避免服务被Activity持有
|
||
// if (isServiceBound) {
|
||
// unbindService(mServiceConn);
|
||
// isServiceBound = false;
|
||
// }
|
||
stopService(new Intent(MainActivity.this, MainService.class));
|
||
}
|
||
|
||
mManagePositionsButton.setEnabled(isChecked);
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
/**
|
||
* 绑定服务(仅用于获取服务状态,不启动服务)
|
||
*/
|
||
// private void bindDistanceService() {
|
||
// Intent serviceIntent = new Intent(this, MainService.class);
|
||
// // BIND_AUTO_CREATE:服务未启动则创建(仅为获取状态,启停由开关控制)
|
||
// bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
|
||
// }
|
||
|
||
// ---------------------- 核心功能3:页面跳转(位置管理页+日志页) ----------------------
|
||
/**
|
||
* 跳转至“位置管理页(LocationActivity)”(按钮点击触发,需在布局中设置 android:onClick="onPositions")
|
||
* 服务未启动时提示,不允许跳转(避免LocationActivity无数据)
|
||
*/
|
||
public void onPositions(View view) {
|
||
//ToastUtils.show("onPositions");
|
||
// 服务已启动:跳转到位置管理页
|
||
startActivity(new Intent(MainActivity.this, LocationActivity.class));
|
||
}
|
||
|
||
/**
|
||
* 跳转至“日志页(LogActivity)”(按钮点击触发,需在布局中设置 android:onClick="onLog")
|
||
* 无服务状态限制,直接跳转
|
||
*/
|
||
public void onLog(View view) {
|
||
WinBoLLActivityManager.getInstance().startLogActivity(this); // 调用LogActivity静态方法跳转(保留原逻辑)
|
||
}
|
||
|
||
// ---------------------- 新增:位置权限处理(适配Java7 + 后台GPS权限) ----------------------
|
||
/**
|
||
* 检查是否拥有「前台+后台」位置权限(适配Android版本差异)
|
||
* Java7 特性:显式类型判断、无Lambda、兼容低版本API
|
||
*/
|
||
private boolean checkLocationPermissions() {
|
||
// 1. 检查前台精确定位权限(Android 6.0+ 必需,显式强转权限常量)
|
||
int foregroundPermResult = checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||
boolean hasForegroundPerm = (foregroundPermResult == PackageManager.PERMISSION_GRANTED);
|
||
|
||
// 2. 检查后台定位权限(仅Android 10+ 需要,Java7 显式用Build.VERSION判断版本)
|
||
boolean hasBackgroundPerm = true;
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||
int backgroundPermResult = checkSelfPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION);
|
||
hasBackgroundPerm = (backgroundPermResult == PackageManager.PERMISSION_GRANTED);
|
||
}
|
||
|
||
// 前台+后台权限均满足,才返回true
|
||
return hasForegroundPerm && hasBackgroundPerm;
|
||
}
|
||
|
||
private void requestLocationPermissions() {
|
||
// 1. 先判断前台定位权限(ACCESS_FINE_LOCATION)是否已授予
|
||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
|
||
!= PackageManager.PERMISSION_GRANTED) {
|
||
// 1.1 未授予前台权限:先申请前台权限(API 30+ 后台权限依赖前台权限)
|
||
String[] foregroundPermissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION};
|
||
// 对API 23+(Android 6.0)动态申请,低版本会直接授予(清单已声明前提下)
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||
requestPermissions(foregroundPermissions, REQUEST_LOCATION_PERMISSIONS);
|
||
}
|
||
} else {
|
||
// 2. 已授予前台权限:判断是否需要申请后台权限(仅API 29+需要)
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||
// 2.1 检查后台权限是否未授予
|
||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||
!= PackageManager.PERMISSION_GRANTED) {
|
||
// 2.2 API 30+ 必须单独申请后台权限(不能和前台权限一起弹框)
|
||
requestPermissions(
|
||
new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
|
||
REQUEST_BACKGROUND_LOCATION_PERMISSION
|
||
);
|
||
}
|
||
}
|
||
// 3. 前台权限已授予(+ 后台权限按需授予):此处可执行定位相关逻辑
|
||
// doLocationRelatedLogic();
|
||
}
|
||
}
|
||
|
||
// 【必须补充】权限申请结果回调(处理用户同意/拒绝逻辑)
|
||
@Override
|
||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||
// 处理前台权限申请结果
|
||
if (requestCode == REQUEST_LOCATION_PERMISSIONS) {
|
||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||
// 前台权限同意:自动尝试申请后台权限(如果是API 29+)
|
||
requestLocationPermissions();
|
||
} else {
|
||
// 前台权限拒绝:提示用户(可选:引导跳转到应用设置页)
|
||
Toast.makeText(this, "需要前台定位权限才能使用该功能", Toast.LENGTH_SHORT).show();
|
||
}
|
||
} else if (requestCode == REQUEST_BACKGROUND_LOCATION_PERMISSION) {
|
||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||
// 后台权限同意:可执行后台定位逻辑
|
||
Toast.makeText(this, "已获得后台定位权限", Toast.LENGTH_SHORT).show();
|
||
} else {
|
||
// 后台权限拒绝:提示用户(可选:说明后台定位的用途,引导手动开启)
|
||
Toast.makeText(this, "拒绝后台权限将无法在后台持续定位", Toast.LENGTH_SHORT).show();
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|