20251213_134317_469

This commit is contained in:
2025-12-13 13:43:22 +08:00
parent 9cc211ec51
commit 2b7108940b
7 changed files with 129 additions and 339 deletions

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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创建流程结束 =====");
}

View File

@@ -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;
}
}
}

View File

@@ -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 {

View File

@@ -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);
}
/**
* 新增启动通话筛选服务MyCallScreeningServiceAPI 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. 新增启动通话筛选服务MyCallScreeningServiceAPI 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 {

View File

@@ -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;
}
}