20251213_134317_469
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sat Dec 13 03:37:48 GMT 2025
|
||||
#Sat Dec 13 05:41:14 GMT 2025
|
||||
stageCount=1
|
||||
libraryProject=
|
||||
baseVersion=15.12
|
||||
publishVersion=15.12.0
|
||||
buildCount=81
|
||||
buildCount=99
|
||||
baseBetaVersion=15.12.1
|
||||
|
||||
@@ -18,11 +18,16 @@ public class App extends GlobalApplication {
|
||||
super.onCreate();
|
||||
// 设置应用调试标志
|
||||
setIsDebugging(BuildConfig.DEBUG);
|
||||
|
||||
|
||||
// 初始化窗口管理类
|
||||
WinBoLLActivityManager.init(this);
|
||||
// 初始化 Toast 框架
|
||||
ToastUtils.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
ToastUtils.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import cc.winboll.studio.contacts.dun.Rules;
|
||||
import cc.winboll.studio.contacts.fragments.CallLogFragment;
|
||||
import cc.winboll.studio.contacts.fragments.ContactsFragment;
|
||||
import cc.winboll.studio.contacts.fragments.LogFragment;
|
||||
import cc.winboll.studio.contacts.model.MainServiceBean;
|
||||
import cc.winboll.studio.contacts.services.MainService;
|
||||
import cc.winboll.studio.contacts.utils.PermissionUtils;
|
||||
import cc.winboll.studio.contacts.views.DunTemperatureView;
|
||||
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||
@@ -111,6 +113,17 @@ public final class MainActivity extends WinBollActivity implements IWinBoLLActiv
|
||||
|
||||
// 直接初始化UI(原权限检查逻辑注释保留,按需启用)
|
||||
initUIAndLogic(savedInstanceState);
|
||||
|
||||
MainServiceBean mainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||
if (mainServiceBean != null && mainServiceBean.isEnable()) {
|
||||
Intent intent = new Intent(this, MainService.class);
|
||||
// 根据应用前后台状态选择启动方式(Android 12+ 后台用 startForegroundService)
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
startForegroundService(intent);
|
||||
} else {
|
||||
startService(intent);
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "===== onCreate: 主Activity创建流程结束 =====");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,32 @@
|
||||
package cc.winboll.studio.contacts.phonecallui;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaRecorder;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.CallLog;
|
||||
import android.telecom.Call;
|
||||
import android.telecom.InCallService;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import cc.winboll.studio.contacts.ActivityStack;
|
||||
import cc.winboll.studio.contacts.model.RingTongBean;
|
||||
import cc.winboll.studio.contacts.dun.Rules;
|
||||
import cc.winboll.studio.contacts.fragments.CallLogFragment;
|
||||
import cc.winboll.studio.contacts.model.RingTongBean;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @Author aJIEw, ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/02/13 06:58:04
|
||||
* @Describe 通话状态监听服务(需 Android 6.0+),负责通话状态回调、录音、铃音控制及黑白名单校验
|
||||
* @Describe 通话状态监听服务(需 Android 6.0+),负责通话状态回调、铃音控制及黑白名单校验
|
||||
* @see PhoneCallActivity
|
||||
* @see android.telecom.InCallService
|
||||
*/
|
||||
public class PhoneCallService extends InCallService {
|
||||
// ====================== 常量定义区 ======================
|
||||
public static final String TAG = "PhoneCallService";
|
||||
// 强制指定正确常量值,彻底解决冲突
|
||||
// TelephonyManager 通话状态
|
||||
//private static final int CALL_STATE_OFFHOOK = TelephonyManager.CALL_STATE_OFFHOOK; // 固定值=2
|
||||
//private static final int CALL_STATE_IDLE = TelephonyManager.CALL_STATE_IDLE; // 固定值=0
|
||||
//private static final int CALL_STATE_RINGING_TELE = TelephonyManager.CALL_STATE_RINGING; // 固定值=1
|
||||
// Call 通话状态
|
||||
private static final int CALL_STATE_IDLE = Call.STATE_NEW;
|
||||
private static final int CALL_STATE_RINGING = Call.STATE_RINGING; //正确值=1
|
||||
private static final int CALL_STATE_CONNECTING = Call.STATE_CONNECTING; //正确值=3
|
||||
private static final int CALL_STATE_ACTIVE = Call.STATE_ACTIVE; //正确值=2
|
||||
private static final int CALL_STATE_DISCONNECTED = Call.STATE_DISCONNECTED; //正确值=4
|
||||
// Call 通话状态(固定正确值,避免冲突)
|
||||
private static final int CALL_STATE_IDLE = Call.STATE_NEW;
|
||||
private static final int CALL_STATE_RINGING = Call.STATE_RINGING; // 正确值=1
|
||||
private static final int CALL_STATE_CONNECTING = Call.STATE_CONNECTING; // 正确值=3
|
||||
private static final int CALL_STATE_ACTIVE = Call.STATE_ACTIVE; // 正确值=2
|
||||
private static final int CALL_STATE_DISCONNECTED = Call.STATE_DISCONNECTED; // 正确值=4
|
||||
|
||||
// ====================== 成员变量区 ======================
|
||||
private MediaRecorder mMediaRecorder;
|
||||
private Call.Callback mCallCallback;
|
||||
|
||||
// ====================== 内部枚举类 ======================
|
||||
@@ -101,12 +84,11 @@ public class PhoneCallService extends InCallService {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
releaseRecorder();
|
||||
CallLogFragment.updateCallLogFragment();
|
||||
LogUtils.d(TAG, "onDestroy: 通话服务已销毁,资源已释放");
|
||||
}
|
||||
|
||||
// ====================== 核心修复:通话状态回调 ======================
|
||||
// ====================== 核心:通话状态回调 ======================
|
||||
private void initCallCallback() {
|
||||
mCallCallback = new Call.Callback() {
|
||||
@Override
|
||||
@@ -115,29 +97,24 @@ public class PhoneCallService extends InCallService {
|
||||
LogUtils.d(TAG, "onStateChanged: 状态变更 | 状态码=" + state + " | 描述=" + getCallStateDesc(state));
|
||||
|
||||
switch (state) {
|
||||
// 仅合并值为2的两个状态,其他状态独立分支
|
||||
// 通话活跃状态(仅监听,无录音)
|
||||
case CALL_STATE_ACTIVE:
|
||||
//case CALL_STATE_OFFHOOK:
|
||||
long callId = getCurrentCallId();
|
||||
if (callId != -1) {
|
||||
LogUtils.d(TAG, "onStateChanged: 通话接通/活跃,启动录音 | 通话ID=" + callId);
|
||||
startRecording(callId);
|
||||
} else {
|
||||
LogUtils.w(TAG, "onStateChanged: 未获取到通话ID,无法录音");
|
||||
}
|
||||
LogUtils.d(TAG, "onStateChanged: 通话已接通/活跃");
|
||||
break;
|
||||
// 状态码1独立分支,彻底解决重复问题
|
||||
// 响铃状态
|
||||
case CALL_STATE_RINGING:
|
||||
LogUtils.d(TAG, "onStateChanged: 通话处于响铃状态");
|
||||
break;
|
||||
// 空闲状态(通话挂断后)
|
||||
case CALL_STATE_IDLE:
|
||||
LogUtils.d(TAG, "onStateChanged: 通话挂断,停止录音");
|
||||
stopRecording();
|
||||
LogUtils.d(TAG, "onStateChanged: 通话挂断");
|
||||
break;
|
||||
// 通话断开,关闭界面
|
||||
case CALL_STATE_DISCONNECTED:
|
||||
ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);
|
||||
LogUtils.d(TAG, "onStateChanged: 通话断开,关闭通话界面");
|
||||
break;
|
||||
// 通话连接中
|
||||
case CALL_STATE_CONNECTING:
|
||||
LogUtils.d(TAG, "onStateChanged: 通话正在连接");
|
||||
break;
|
||||
@@ -149,7 +126,7 @@ public class PhoneCallService extends InCallService {
|
||||
};
|
||||
}
|
||||
|
||||
// ====================== 核心业务方法区 ======================
|
||||
// ====================== 核心业务方法区(铃音控制+黑白名单) ======================
|
||||
private CallType judgeCallType(int callState) {
|
||||
switch (callState) {
|
||||
case CALL_STATE_RINGING:
|
||||
@@ -232,90 +209,7 @@ public class PhoneCallService extends InCallService {
|
||||
LogUtils.d(TAG, "startPhoneCallActivity: 通话界面已启动");
|
||||
}
|
||||
|
||||
// ====================== 录音相关方法区 ======================
|
||||
private void startRecording(long callId) {
|
||||
releaseRecorder();
|
||||
mMediaRecorder = new MediaRecorder();
|
||||
try {
|
||||
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
|
||||
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
|
||||
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
|
||||
String path = getOutputFilePath(callId);
|
||||
mMediaRecorder.setOutputFile(path);
|
||||
mMediaRecorder.prepare();
|
||||
mMediaRecorder.start();
|
||||
LogUtils.d(TAG, "startRecording: 录音启动成功 | 路径=" + path);
|
||||
} catch (IOException | IllegalStateException e) {
|
||||
LogUtils.e(TAG, "startRecording: 录音启动失败", e);
|
||||
releaseRecorder();
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRecording() {
|
||||
if (mMediaRecorder != null) {
|
||||
try {
|
||||
mMediaRecorder.stop();
|
||||
} catch (IllegalStateException e) {
|
||||
LogUtils.e(TAG, "stopRecording: 录音停止失败", e);
|
||||
} finally {
|
||||
releaseRecorder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getOutputFilePath(long callId) {
|
||||
File dir = getExternalFilesDir(TAG);
|
||||
if (dir == null) {
|
||||
dir = new File(getFilesDir(), TAG);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
}
|
||||
return new File(dir, String.format("call_%d.mp4", callId)).getAbsolutePath();
|
||||
}
|
||||
|
||||
private void releaseRecorder() {
|
||||
if (mMediaRecorder != null) {
|
||||
mMediaRecorder.release();
|
||||
mMediaRecorder = null;
|
||||
LogUtils.d(TAG, "releaseRecorder: 录音资源已释放");
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 辅助工具方法区 ======================
|
||||
private long getCurrentCallId() {
|
||||
ContentResolver resolver = getApplicationContext().getContentResolver();
|
||||
if (resolver == null) {
|
||||
LogUtils.w(TAG, "getCurrentCallId: 内容解析器为null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Uri uri = CallLog.Calls.CONTENT_URI;
|
||||
String[] projection = {CallLog.Calls._ID};
|
||||
String selection = CallLog.Calls.TYPE + " = " + CallLog.Calls.OUTGOING_TYPE
|
||||
+ " OR " + CallLog.Calls.TYPE + " = " + CallLog.Calls.INCOMING_TYPE;
|
||||
String sort = CallLog.Calls.DATE + " DESC";
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = resolver.query(uri, projection, selection, null, sort);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
long id = cursor.getLong(cursor.getColumnIndex(CallLog.Calls._ID));
|
||||
LogUtils.d(TAG, "getCurrentCallId: 通话ID=" + id);
|
||||
return id;
|
||||
}
|
||||
LogUtils.w(TAG, "getCurrentCallId: 未查询到通话记录");
|
||||
return -1;
|
||||
} catch (SecurityException e) {
|
||||
LogUtils.e(TAG, "getCurrentCallId: 查询失败(权限不足)", e);
|
||||
return -1;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getCallStateDesc(int state) {
|
||||
switch (state) {
|
||||
case CALL_STATE_RINGING:
|
||||
@@ -324,8 +218,6 @@ public class PhoneCallService extends InCallService {
|
||||
return "连接中";
|
||||
case CALL_STATE_ACTIVE:
|
||||
return "活跃";
|
||||
//case CALL_STATE_OFFHOOK:
|
||||
// return "摘机";
|
||||
case CALL_STATE_IDLE:
|
||||
return "空闲";
|
||||
case CALL_STATE_DISCONNECTED:
|
||||
@@ -334,5 +226,18 @@ public class PhoneCallService extends InCallService {
|
||||
return "未知状态";
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 静态内部类:PhoneCallManager(避免编译报错) ======================
|
||||
public static class PhoneCallManager {
|
||||
public static Call sCurrentCall;
|
||||
|
||||
private PhoneCallManager() {
|
||||
// 私有构造,禁止实例化
|
||||
}
|
||||
|
||||
public static Call getCurrentCall() {
|
||||
return sCurrentCall;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,9 @@ import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
|
||||
import cc.winboll.studio.contacts.R;
|
||||
import cc.winboll.studio.contacts.model.MainServiceBean;
|
||||
import cc.winboll.studio.contacts.utils.AppForegroundUtils;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
/**
|
||||
@@ -159,7 +155,7 @@ public class AssistantService extends Service {
|
||||
LogUtils.d(TAG, "onCreate: 守护服务创建");
|
||||
|
||||
// 适配 Android 12+ 后台启动限制:应用后台时启动为前台服务
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_12_API && !AppForegroundUtils.isAppForeground(this)) {
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_12_API) {
|
||||
Notification notification = createForegroundNotification();
|
||||
// 修复:使用 dataSync 类型,添加异常捕获防止崩溃
|
||||
try {
|
||||
@@ -256,7 +252,7 @@ public class AssistantService extends Service {
|
||||
|
||||
Intent intent = new Intent(this, MainService.class);
|
||||
// 根据应用前后台状态选择启动方式(Android 12+ 后台用 startForegroundService)
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_12_API && !AppForegroundUtils.isAppForeground(this)) {
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_12_API) {
|
||||
LogUtils.d(TAG, "wakeupAndBindMain: 应用后台,启动主服务为前台服务");
|
||||
startForegroundService(intent);
|
||||
} else {
|
||||
|
||||
@@ -23,8 +23,6 @@ import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
|
||||
import cc.winboll.studio.contacts.model.MainServiceBean;
|
||||
import cc.winboll.studio.contacts.model.RingTongBean;
|
||||
import cc.winboll.studio.contacts.receivers.MainReceiver;
|
||||
import cc.winboll.studio.contacts.threads.MainServiceThread;
|
||||
import cc.winboll.studio.contacts.utils.AppForegroundUtils;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@@ -34,7 +32,7 @@ import java.util.TimerTask;
|
||||
* @Date 2025/02/13 06:56:41
|
||||
* @Describe 拨号主服务,负责核心业务逻辑、守护进程绑定、铃声音量监控及通话监听启动
|
||||
* 严格适配 Android API 30 + Java 7 语法规范 | 解决前台服务启动超时崩溃
|
||||
* 新增:统一在 mainService() 中启动电话监听服务 + 通话筛选服务
|
||||
* 优化:移除延迟启动,非核心服务直接在 mainService() 中启动
|
||||
*/
|
||||
public class MainService extends Service {
|
||||
// ====================== 常量定义区(全硬编码,杜绝高版本 API 依赖) ======================
|
||||
@@ -52,9 +50,8 @@ public class MainService extends Service {
|
||||
private static final int ANDROID_8_API = 26;
|
||||
private static final int ANDROID_10_API = 29;
|
||||
private static final int ANDROID_12_API = 31;
|
||||
// 重试延迟时间 & 非核心服务延迟启动时间
|
||||
// 重试延迟时间(仅保留守护服务重绑定延迟,移除非核心服务延迟)
|
||||
private static final long RETRY_DELAY_MS = 3000L;
|
||||
private static final long DELAY_NON_CORE_SERVICE_MS = 100L;
|
||||
|
||||
// ====================== 静态成员变量区 ======================
|
||||
private static MainService sMainServiceInstance;
|
||||
@@ -63,14 +60,13 @@ public class MainService extends Service {
|
||||
// ====================== 成员变量区 ======================
|
||||
private volatile boolean mIsServiceRunning;
|
||||
private MainServiceBean mMainServiceBean;
|
||||
private MainServiceThread mMainServiceThread;
|
||||
//private MainServiceThread mMainServiceThread;
|
||||
private MainServiceHandler mMainServiceHandler;
|
||||
private MyServiceConnection mMyServiceConnection;
|
||||
private AssistantService mAssistantService;
|
||||
private boolean mIsBound;
|
||||
private MainReceiver mMainReceiver;
|
||||
private Timer mStreamVolumeCheckTimer;
|
||||
private Handler mDelayHandler;
|
||||
|
||||
// ====================== Binder 内部类 ======================
|
||||
public class MyBinder extends Binder {
|
||||
@@ -147,13 +143,7 @@ public class MainService extends Service {
|
||||
}
|
||||
LogUtils.d(TAG, "startMainService: 执行启动主服务操作");
|
||||
Intent intent = new Intent(context, MainService.class);
|
||||
// API 30 属于 Android 10+,按前台服务逻辑处理
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_10_API && !AppForegroundUtils.isAppForeground(context)) {
|
||||
LogUtils.d(TAG, "startMainService: 应用后台,使用 startForegroundService 启动");
|
||||
context.startForegroundService(intent);
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
context.startForegroundService(intent);
|
||||
}
|
||||
|
||||
public static void restartMainService(Context context) {
|
||||
@@ -194,6 +184,7 @@ public class MainService extends Service {
|
||||
MainServiceBean bean = new MainServiceBean();
|
||||
bean.setIsEnable(true);
|
||||
MainServiceBean.saveBean(context, bean);
|
||||
stopMainService(context);
|
||||
startMainService(context);
|
||||
}
|
||||
|
||||
@@ -213,52 +204,6 @@ public class MainService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务启动前置校验
|
||||
*/
|
||||
private boolean canStartService(Class<?> serviceClass) {
|
||||
// 服务未启用/已运行 → 禁止启动
|
||||
if (mMainServiceBean == null || !mMainServiceBean.isEnable()) {
|
||||
LogUtils.w(TAG, "canStartService: 主服务未启用,禁止启动 " + serviceClass.getSimpleName());
|
||||
return false;
|
||||
}
|
||||
if (isServiceRunning(serviceClass)) {
|
||||
LogUtils.d(TAG, "canStartService: " + serviceClass.getSimpleName() + " 已运行,跳过启动");
|
||||
return false;
|
||||
}
|
||||
// Android 12+ 应用后台 → 禁止启动非核心服务
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_12_API && !AppForegroundUtils.isAppForeground(this)) {
|
||||
LogUtils.w(TAG, "canStartService: Android 12+ 应用后台,禁止启动 " + serviceClass.getSimpleName());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟启动非核心服务
|
||||
*/
|
||||
private void delayStartNonCoreService(final Class<?> serviceClass) {
|
||||
if (!canStartService(serviceClass)) {
|
||||
return;
|
||||
}
|
||||
mDelayHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Intent intent = new Intent(MainService.this, serviceClass);
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_10_API && !AppForegroundUtils.isAppForeground(MainService.this)) {
|
||||
startForegroundService(intent);
|
||||
} else {
|
||||
startService(intent);
|
||||
}
|
||||
LogUtils.d(TAG, "delayStartNonCoreService: " + serviceClass.getSimpleName() + " 启动成功");
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "delayStartNonCoreService: " + serviceClass.getSimpleName() + " 启动失败", e);
|
||||
}
|
||||
}
|
||||
}, DELAY_NON_CORE_SERVICE_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建前台服务通知
|
||||
*/
|
||||
@@ -299,25 +244,6 @@ public class MainService extends Service {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动电话监听服务(原有:CallListenerService)
|
||||
*/
|
||||
private void startPhoneCallListener() {
|
||||
delayStartNonCoreService(CallListenerService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:启动通话筛选服务(MyCallScreeningService,API 10+ 生效)
|
||||
*/
|
||||
private void startCallScreeningService() {
|
||||
// 通话筛选服务仅 Android 10+(API 29+)支持,低版本跳过
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_10_API) {
|
||||
delayStartNonCoreService(MyCallScreeningService.class);
|
||||
} else {
|
||||
LogUtils.d(TAG, "startCallScreeningService: 系统版本低于 Android 10,不支持通话筛选服务,跳过启动");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查服务是否正在运行
|
||||
*/
|
||||
@@ -349,15 +275,17 @@ public class MainService extends Service {
|
||||
sMainServiceInstance = this;
|
||||
mIsServiceRunning = false;
|
||||
|
||||
// 仅初始化核心组件
|
||||
// 仅初始化核心组件(移除 mDelayHandler 初始化,无需延迟启动)
|
||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||
mMyServiceConnection = new MyServiceConnection();
|
||||
mMainServiceHandler = new MainServiceHandler(this);
|
||||
mDelayHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
// 初始化铃声音量检查定时器
|
||||
initVolumeCheckTimer();
|
||||
|
||||
// 启动服务
|
||||
mainService();
|
||||
|
||||
LogUtils.d(TAG, "===== onCreate: 主服务创建完成(仅初始化组件) =====");
|
||||
}
|
||||
|
||||
@@ -380,19 +308,10 @@ public class MainService extends Service {
|
||||
super.onDestroy();
|
||||
LogUtils.d(TAG, "===== onDestroy: 主服务开始销毁(强制清理所有资源) =====");
|
||||
|
||||
// 1. 强制标记服务为未运行
|
||||
mIsServiceRunning = false;
|
||||
|
||||
// 2. 释放延迟 Handler 资源
|
||||
if (mDelayHandler != null) {
|
||||
mDelayHandler.removeCallbacksAndMessages(null);
|
||||
mDelayHandler = null;
|
||||
}
|
||||
|
||||
// 3. 释放定时器资源
|
||||
// 2. 释放定时器资源
|
||||
cancelVolumeCheckTimer();
|
||||
|
||||
// 4. 强制解除守护服务绑定
|
||||
// 3. 强制解除守护服务绑定
|
||||
if (mIsBound && mMyServiceConnection != null) {
|
||||
try {
|
||||
unbindService(mMyServiceConnection);
|
||||
@@ -404,97 +323,101 @@ public class MainService extends Service {
|
||||
mMyServiceConnection = null;
|
||||
}
|
||||
|
||||
// 5. 强制注销广播接收器
|
||||
// 4. 强制注销广播接收器
|
||||
if (mMainReceiver != null) {
|
||||
mMainReceiver.unregisterAction(this);
|
||||
mMainReceiver = null;
|
||||
LogUtils.d(TAG, "onDestroy: 广播接收器已注销");
|
||||
}
|
||||
|
||||
// 6. 强制停止主业务线程
|
||||
if (mMainServiceThread != null) {
|
||||
mMainServiceThread.setIsExit(true);
|
||||
mMainServiceThread = null;
|
||||
}
|
||||
// 5. 强制停止主业务线程
|
||||
// if (mMainServiceThread != null) {
|
||||
// mMainServiceThread.setIsExit(true);
|
||||
// mMainServiceThread = null;
|
||||
// }
|
||||
|
||||
// 7. 强制停止所有子服务(新增:停止通话筛选服务 MyCallScreeningService)
|
||||
// 6. 强制停止所有子服务
|
||||
stopService(new Intent(this, AssistantService.class));
|
||||
stopService(new Intent(this, CallListenerService.class));
|
||||
stopService(new Intent(this, MyCallScreeningService.class));
|
||||
LogUtils.d(TAG, "onDestroy: 所有子服务已强制停止");
|
||||
|
||||
// 8. 清空静态实例,避免内存泄漏(不改动 Bean 配置)
|
||||
// 7. 清空静态实例,避免内存泄漏(不改动 Bean 配置)
|
||||
sMainServiceInstance = null;
|
||||
sTomCatInstance = null;
|
||||
mMainServiceHandler = null;
|
||||
mAssistantService = null;
|
||||
mMainServiceBean = null;
|
||||
|
||||
// 标记服务为未运行
|
||||
mIsServiceRunning = false;
|
||||
LogUtils.d(TAG, "===== onDestroy: 主服务销毁完成(资源全清理,Bean 配置无改动) =====");
|
||||
}
|
||||
|
||||
// ====================== 核心业务逻辑:优先判断运行状态 → 再校验配置启用状态 ======================
|
||||
private void mainService() {
|
||||
// ====================== 核心业务逻辑:非核心服务直接启动,无延迟 ======================
|
||||
private synchronized void mainService() {
|
||||
LogUtils.d(TAG, "mainService: 执行核心业务逻辑");
|
||||
|
||||
// 第一步:优先判断服务是否已运行,已运行则直接退出(不做任何动作)
|
||||
if (mIsServiceRunning) {
|
||||
LogUtils.d(TAG, "mainService: 服务已处于运行状态,直接退出");
|
||||
return;
|
||||
}
|
||||
if (!mIsServiceRunning) {
|
||||
mIsServiceRunning = true;
|
||||
// 第二步:加载最新配置,校验服务是否启用(未启用则退出)
|
||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||
if (mMainServiceBean == null && mMainServiceBean.isEnable()) {
|
||||
// 第三步:配置启用且服务未运行,执行启动流程
|
||||
LogUtils.i(TAG, "mainService: 服务未运行且配置已启用,开始启动核心流程");
|
||||
|
||||
// 第二步:加载最新配置,校验服务是否启用(未启用则退出)
|
||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||
if (mMainServiceBean == null || !mMainServiceBean.isEnable()) {
|
||||
LogUtils.w(TAG, "mainService: 服务配置未启用/配置加载失败,退出启动流程");
|
||||
return;
|
||||
}
|
||||
// 1. 启动前台服务(优先执行,避免超时)
|
||||
try {
|
||||
Notification foregroundNotification = createForegroundNotification();
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, foregroundNotification, FOREGROUND_SERVICE_TYPE_DATA_SYNC);
|
||||
//startForeground(FOREGROUND_NOTIFICATION_ID, foregroundNotification);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LogUtils.e(TAG, "mainService: 启动前台服务失败,类型不匹配", e);
|
||||
mIsServiceRunning = false;
|
||||
stopSelf();
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "mainService: 启动前台服务异常", e);
|
||||
mIsServiceRunning = false;
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
|
||||
// 第三步:配置启用且服务未运行,执行启动流程
|
||||
mIsServiceRunning = true;
|
||||
LogUtils.i(TAG, "mainService: 服务未运行且配置已启用,开始启动核心流程");
|
||||
// 2. 绑定守护服务
|
||||
wakeupAndBindAssistant();
|
||||
|
||||
// 1. 启动前台服务(优先执行,避免超时)
|
||||
try {
|
||||
Notification foregroundNotification = createForegroundNotification();
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_10_API) {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, foregroundNotification, FOREGROUND_SERVICE_TYPE_DATA_SYNC);
|
||||
LogUtils.d(TAG, "mainService: 主服务已启动为前台服务(dataSync 类型,API30+)");
|
||||
} else {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, foregroundNotification);
|
||||
LogUtils.d(TAG, "mainService: 主服务已启动为前台服务(低版本兼容)");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
LogUtils.e(TAG, "mainService: 启动前台服务失败,类型不匹配", e);
|
||||
mIsServiceRunning = false;
|
||||
stopSelf();
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "mainService: 启动前台服务异常", e);
|
||||
mIsServiceRunning = false;
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
// 3. 初始化业务组件
|
||||
initTomCat();
|
||||
initMainReceiver();
|
||||
Rules.getInstance(this).loadRules();
|
||||
LogUtils.d(TAG, "mainService: 黑白名单规则已加载");
|
||||
|
||||
// 2. 绑定守护服务
|
||||
wakeupAndBindAssistant();
|
||||
// 4. 直接启动电话监听服务(移除延迟,校验通过则立即启动)
|
||||
Intent callListenerIntent = new Intent(this, CallListenerService.class);
|
||||
try {
|
||||
startForegroundService(callListenerIntent);
|
||||
//startService(callListenerIntent);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "mainService: CallListenerService 启动失败", e);
|
||||
}
|
||||
|
||||
// 3. 初始化业务组件
|
||||
initTomCat();
|
||||
initMainReceiver();
|
||||
Rules.getInstance(this).loadRules();
|
||||
LogUtils.d(TAG, "mainService: 黑白名单规则已加载");
|
||||
|
||||
// 4. 启动电话监听服务(原有:CallListenerService)
|
||||
startPhoneCallListener();
|
||||
// 5. 直接启动通话筛选服务(API 10+ 生效,无延迟)
|
||||
Intent screeningIntent = new Intent(this, MyCallScreeningService.class);
|
||||
try {
|
||||
startForegroundService(screeningIntent);
|
||||
//startService(screeningIntent);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "mainService: MyCallScreeningService 启动失败", e);
|
||||
}
|
||||
|
||||
// 5. 新增:启动通话筛选服务(MyCallScreeningService,API 10+ 生效)
|
||||
startCallScreeningService();
|
||||
|
||||
// 6. 启动主业务线程
|
||||
mMainServiceThread = MainServiceThread.getInstance(this, mMainServiceHandler);
|
||||
mMainServiceThread.start();
|
||||
LogUtils.i(TAG, "mainService: 核心流程启动完成,主服务正常运行");
|
||||
// 6. 启动主业务线程
|
||||
// mMainServiceThread = MainServiceThread.getInstance(this, mMainServiceHandler);
|
||||
// mMainServiceThread.start();
|
||||
LogUtils.i(TAG, "mainService: 核心流程启动完成,主服务正常运行");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void wakeupAndBindAssistant() {
|
||||
@@ -503,7 +426,7 @@ public class MainService extends Service {
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(this, AssistantService.class);
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_10_API && !AppForegroundUtils.isAppForeground(this)) {
|
||||
if (Build.VERSION.SDK_INT >= ANDROID_10_API) {
|
||||
LogUtils.d(TAG, "wakeupAndBindAssistant: 应用后台,启动 AssistantService 为前台服务");
|
||||
startForegroundService(intent);
|
||||
} else {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package cc.winboll.studio.contacts.utils;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/12/12 15:20
|
||||
* @Describe AppForegroundUtils 应用前后台状态判断工具类(兼容 Java 7)
|
||||
*/
|
||||
public class AppForegroundUtils {
|
||||
|
||||
public static final String TAG = "AppForegroundUtils";
|
||||
|
||||
/**
|
||||
* 判断应用是否处于前台状态
|
||||
* @param context 上下文
|
||||
* @return true-前台,false-后台/无上下文
|
||||
*/
|
||||
public static boolean isAppForeground(Context context) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (activityManager == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String packageName = context.getPackageName();
|
||||
List<ActivityManager.RunningAppProcessInfo> processList = activityManager.getRunningAppProcesses();
|
||||
if (processList == null || processList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 遍历进程列表,匹配当前应用包名并判断前台状态
|
||||
for (ActivityManager.RunningAppProcessInfo processInfo : processList) {
|
||||
// Java 7 兼容:避免 Lambda,使用普通循环 + TextUtils 判等
|
||||
if (!TextUtils.isEmpty(processInfo.processName)
|
||||
&& TextUtils.equals(processInfo.processName, packageName)
|
||||
&& processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user