恢复2a74fd2c304b571ab5ae349ffc3b7f06c5b4daf7提交点历史
This commit is contained in:
@@ -8,28 +8,25 @@ import android.os.Bundle;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import cc.winboll.studio.contacts.ActivityStack;
|
import cc.winboll.studio.contacts.ActivityStack;
|
||||||
|
import cc.winboll.studio.contacts.MainActivity;
|
||||||
import cc.winboll.studio.contacts.R;
|
import cc.winboll.studio.contacts.R;
|
||||||
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
/**
|
import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
|
||||||
* @Author aJIEw, ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
|
||||||
* @Date 2025/02/13 06:58:04
|
|
||||||
* @Describe 接打电话界面,仅支持 Android 6.0(API 23)及以上系统
|
/**
|
||||||
*/
|
* 提供接打电话的界面,仅支持 Android M (6.0, API 23) 及以上的系统
|
||||||
// Java7不支持@RequiresApi,通过代码内校验替代注解
|
*
|
||||||
public class PhoneCallActivity extends AppCompatActivity implements View.OnClickListener {
|
* @author aJIEw
|
||||||
// ====================== 常量定义区 ======================
|
*/
|
||||||
public static final String TAG = "PhoneCallActivity";
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
// Intent传参键常量,替代系统常量避免歧义
|
public class PhoneCallActivity extends AppCompatActivity implements View.OnClickListener {
|
||||||
private static final String EXTRA_CALL_TYPE = "extra_call_type";
|
|
||||||
private static final String EXTRA_PHONE_NUM = "extra_phone_number";
|
|
||||||
|
|
||||||
// ====================== 控件与成员变量区 ======================
|
|
||||||
private TextView tvCallNumberLabel;
|
private TextView tvCallNumberLabel;
|
||||||
private TextView tvCallNumber;
|
private TextView tvCallNumber;
|
||||||
private TextView tvPickUp;
|
private TextView tvPickUp;
|
||||||
@@ -39,217 +36,87 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
|
|||||||
private PhoneCallManager phoneCallManager;
|
private PhoneCallManager phoneCallManager;
|
||||||
private PhoneCallService.CallType callType;
|
private PhoneCallService.CallType callType;
|
||||||
private String phoneNumber;
|
private String phoneNumber;
|
||||||
|
|
||||||
private Timer onGoingCallTimer;
|
private Timer onGoingCallTimer;
|
||||||
private int callingTime;
|
private int callingTime;
|
||||||
|
|
||||||
// ====================== 静态跳转方法区 ======================
|
public static void actionStart(Context context, String phoneNumber,
|
||||||
/**
|
PhoneCallService.CallType callType) {
|
||||||
* 启动通话界面
|
|
||||||
* @param context 上下文
|
|
||||||
* @param phoneNumber 通话号码
|
|
||||||
* @param callType 通话类型(来电/去电)
|
|
||||||
*/
|
|
||||||
public static void actionStart(Context context, String phoneNumber, PhoneCallService.CallType callType) {
|
|
||||||
if (context == null) {
|
|
||||||
LogUtils.e(TAG, "actionStart: 上下文为null,无法启动通话界面");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (phoneNumber == null || phoneNumber.isEmpty()) {
|
|
||||||
LogUtils.w(TAG, "actionStart: 通话号码为空");
|
|
||||||
}
|
|
||||||
if (callType == null) {
|
|
||||||
LogUtils.w(TAG, "actionStart: 通话类型为null");
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent intent = new Intent(context, PhoneCallActivity.class);
|
Intent intent = new Intent(context, PhoneCallActivity.class);
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
intent.putExtra(EXTRA_CALL_TYPE, callType);
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, callType);
|
||||||
intent.putExtra(EXTRA_PHONE_NUM, phoneNumber);
|
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, phoneNumber);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
LogUtils.d(TAG, "actionStart: 启动通话界面,号码=" + phoneNumber + ",类型=" + callType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== 生命周期方法区 ======================
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
LogUtils.d(TAG, "onCreate: 通话界面创建");
|
|
||||||
|
|
||||||
// Java7不支持@RequiresApi,此处补充系统版本校验
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
LogUtils.e(TAG, "onCreate: 系统版本低于Android 6.0,不支持该通话界面");
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_phone_call);
|
setContentView(R.layout.activity_phone_call);
|
||||||
|
|
||||||
ActivityStack.getInstance().addActivity(this);
|
ActivityStack.getInstance().addActivity(this);
|
||||||
initData();
|
initData();
|
||||||
initView();
|
initView();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
LogUtils.d(TAG, "onDestroy: 通话界面销毁");
|
|
||||||
stopTimer();
|
|
||||||
if (phoneCallManager != null) {
|
|
||||||
phoneCallManager.destroy();
|
|
||||||
}
|
|
||||||
ActivityStack.getInstance().removeActivity(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 初始化方法区 ======================
|
|
||||||
/**
|
|
||||||
* 初始化数据
|
|
||||||
*/
|
|
||||||
private void initData() {
|
private void initData() {
|
||||||
phoneCallManager = new PhoneCallManager(this);
|
phoneCallManager = new PhoneCallManager(this);
|
||||||
onGoingCallTimer = new Timer();
|
onGoingCallTimer = new Timer();
|
||||||
parseIntentData();
|
if (getIntent() != null) {
|
||||||
LogUtils.d(TAG, "initData: 数据初始化完成,通话类型=" + callType + ",号码=" + phoneNumber);
|
phoneNumber = getIntent().getStringExtra(Intent.EXTRA_PHONE_NUMBER);
|
||||||
|
callType = (PhoneCallService.CallType) getIntent().getSerializableExtra(Intent.EXTRA_MIME_TYPES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析Intent传递的数据
|
|
||||||
*/
|
|
||||||
private void parseIntentData() {
|
|
||||||
Intent intent = getIntent();
|
|
||||||
if (intent == null) {
|
|
||||||
LogUtils.w(TAG, "parseIntentData: 未获取到启动Intent");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
phoneNumber = intent.getStringExtra(EXTRA_PHONE_NUM);
|
|
||||||
callType = (PhoneCallService.CallType) intent.getSerializableExtra(EXTRA_CALL_TYPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化界面控件
|
|
||||||
*/
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
private void initView() {
|
private void initView() {
|
||||||
// 设置沉浸式导航栏
|
|
||||||
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar
|
||||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||||
getWindow().getDecorView().setSystemUiVisibility(uiOptions);
|
getWindow().getDecorView().setSystemUiVisibility(uiOptions);
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||||
LogUtils.d(TAG, "initView: 设置界面沉浸式样式");
|
|
||||||
|
|
||||||
// 绑定控件
|
|
||||||
tvCallNumberLabel = findViewById(R.id.tv_call_number_label);
|
tvCallNumberLabel = findViewById(R.id.tv_call_number_label);
|
||||||
tvCallNumber = findViewById(R.id.tv_call_number);
|
tvCallNumber = findViewById(R.id.tv_call_number);
|
||||||
tvPickUp = findViewById(R.id.tv_phone_pick_up);
|
tvPickUp = findViewById(R.id.tv_phone_pick_up);
|
||||||
tvCallingTime = findViewById(R.id.tv_phone_calling_time);
|
tvCallingTime = findViewById(R.id.tv_phone_calling_time);
|
||||||
tvHangUp = findViewById(R.id.tv_phone_hang_up);
|
tvHangUp = findViewById(R.id.tv_phone_hang_up);
|
||||||
|
|
||||||
// 设置控件事件与初始状态
|
tvCallNumber.setText(formatPhoneNumber(phoneNumber));
|
||||||
tvCallNumber.setText(CallListenerService.formatPhoneNumber(phoneNumber));
|
|
||||||
tvPickUp.setOnClickListener(this);
|
tvPickUp.setOnClickListener(this);
|
||||||
tvHangUp.setOnClickListener(this);
|
tvHangUp.setOnClickListener(this);
|
||||||
|
|
||||||
setCallTypeUi();
|
// 打进的电话
|
||||||
showOnLockScreen();
|
if (callType == PhoneCallService.CallType.CALL_IN) {
|
||||||
LogUtils.d(TAG, "initView: 界面控件初始化完成");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 界面交互方法区 ======================
|
|
||||||
/**
|
|
||||||
* 根据通话类型设置界面样式
|
|
||||||
*/
|
|
||||||
private void setCallTypeUi() {
|
|
||||||
if (callType == null) {
|
|
||||||
LogUtils.w(TAG, "setCallTypeUi: 通话类型为null,使用默认样式");
|
|
||||||
tvCallNumberLabel.setText("通话号码");
|
|
||||||
tvPickUp.setVisibility(View.GONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PhoneCallService.CallType.CALL_IN == callType) {
|
|
||||||
tvCallNumberLabel.setText("来电号码");
|
tvCallNumberLabel.setText("来电号码");
|
||||||
tvPickUp.setVisibility(View.VISIBLE);
|
tvPickUp.setVisibility(View.VISIBLE);
|
||||||
} else if (PhoneCallService.CallType.CALL_OUT == callType) {
|
} else if (callType == PhoneCallService.CallType.CALL_OUT) {
|
||||||
tvCallNumberLabel.setText("呼叫号码");
|
tvCallNumberLabel.setText("呼叫号码");
|
||||||
tvPickUp.setVisibility(View.GONE);
|
tvPickUp.setVisibility(View.GONE);
|
||||||
if (phoneCallManager != null) {
|
|
||||||
phoneCallManager.openSpeaker();
|
phoneCallManager.openSpeaker();
|
||||||
LogUtils.d(TAG, "setCallTypeUi: 去电模式,开启扬声器");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
showOnLockScreen();
|
||||||
* 设置界面锁屏显示
|
}
|
||||||
*/
|
|
||||||
private void showOnLockScreen() {
|
public void showOnLockScreen() {
|
||||||
getWindow().setFlags(
|
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
|
||||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
WindowManager.LayoutParams.FLAG_FULLSCREEN |
|
||||||
| WindowManager.LayoutParams.FLAG_FULLSCREEN
|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
|
||||||
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
|
||||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
|
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
|
||||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
WindowManager.LayoutParams.FLAG_FULLSCREEN |
|
||||||
| WindowManager.LayoutParams.FLAG_FULLSCREEN
|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
|
||||||
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
|
||||||
);
|
|
||||||
LogUtils.d(TAG, "showOnLockScreen: 设置界面支持锁屏显示");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== 事件点击与计时方法区 ======================
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (v == null) {
|
if (v.getId() == R.id.tv_phone_pick_up) {
|
||||||
LogUtils.w(TAG, "onClick: 点击空控件");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int id = v.getId();
|
|
||||||
if (id == R.id.tv_phone_pick_up) {
|
|
||||||
LogUtils.d(TAG, "onClick: 点击接听按钮");
|
|
||||||
answerCall();
|
|
||||||
} else if (id == R.id.tv_phone_hang_up) {
|
|
||||||
LogUtils.d(TAG, "onClick: 点击挂断按钮");
|
|
||||||
hangUpCall();
|
|
||||||
} else {
|
|
||||||
LogUtils.w(TAG, "onClick: 点击未知控件,ID=" + id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 接听通话
|
|
||||||
*/
|
|
||||||
private void answerCall() {
|
|
||||||
if (phoneCallManager != null) {
|
|
||||||
phoneCallManager.answer();
|
phoneCallManager.answer();
|
||||||
tvPickUp.setVisibility(View.GONE);
|
tvPickUp.setVisibility(View.GONE);
|
||||||
tvCallingTime.setVisibility(View.VISIBLE);
|
tvCallingTime.setVisibility(View.VISIBLE);
|
||||||
startCallTimer();
|
|
||||||
LogUtils.d(TAG, "answerCall: 接听通话成功");
|
|
||||||
} else {
|
|
||||||
LogUtils.e(TAG, "answerCall: 通话管理器为空,无法接听");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 挂断通话
|
|
||||||
*/
|
|
||||||
private void hangUpCall() {
|
|
||||||
if (phoneCallManager != null) {
|
|
||||||
phoneCallManager.disconnect();
|
|
||||||
} else {
|
|
||||||
LogUtils.e(TAG, "hangUpCall: 通话管理器为空,无法挂断");
|
|
||||||
}
|
|
||||||
stopTimer();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 启动通话计时器
|
|
||||||
*/
|
|
||||||
private void startCallTimer() {
|
|
||||||
stopTimer();
|
|
||||||
onGoingCallTimer = new Timer();
|
|
||||||
onGoingCallTimer.schedule(new TimerTask() {
|
onGoingCallTimer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -263,30 +130,32 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, 0, 1000);
|
}, 0, 1000);
|
||||||
LogUtils.d(TAG, "startCallTimer: 通话计时器启动");
|
} else if (v.getId() == R.id.tv_phone_hang_up) {
|
||||||
|
phoneCallManager.disconnect();
|
||||||
|
stopTimer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 停止通话计时器
|
|
||||||
*/
|
|
||||||
private void stopTimer() {
|
|
||||||
if (onGoingCallTimer != null) {
|
|
||||||
onGoingCallTimer.cancel();
|
|
||||||
onGoingCallTimer = null;
|
|
||||||
}
|
|
||||||
callingTime = 0;
|
|
||||||
LogUtils.d(TAG, "stopTimer: 通话计时器停止");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化通话时长
|
|
||||||
*/
|
|
||||||
private String getCallingTime() {
|
private String getCallingTime() {
|
||||||
int minute = callingTime / 60;
|
int minute = callingTime / 60;
|
||||||
int second = callingTime % 60;
|
int second = callingTime % 60;
|
||||||
String minuteStr = minute < 10 ? "0" + minute : String.valueOf(minute);
|
return (minute < 10 ? "0" + minute : minute) +
|
||||||
String secondStr = second < 10 ? "0" + second : String.valueOf(second);
|
":" +
|
||||||
return minuteStr + ":" + secondStr;
|
(second < 10 ? "0" + second : second);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopTimer() {
|
||||||
|
if (onGoingCallTimer != null) {
|
||||||
|
onGoingCallTimer.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
callingTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
//MainActivity.updateCallLogFragment();
|
||||||
|
phoneCallManager.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,139 +5,58 @@ import android.media.AudioManager;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.telecom.Call;
|
import android.telecom.Call;
|
||||||
import android.telecom.VideoProfile;
|
import android.telecom.VideoProfile;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
* @Date 2025/02/13 06:58:04
|
|
||||||
* @Describe 通话管理工具类,负责接听、挂断通话及免提控制,仅支持Android 6.0(API 23)及以上系统
|
|
||||||
*/
|
|
||||||
public class PhoneCallManager {
|
public class PhoneCallManager {
|
||||||
// ====================== 常量定义区 ======================
|
|
||||||
private static final String TAG = "PhoneCallManager";
|
|
||||||
// 音频通话模式常量,与VideoProfile.STATE_AUDIO_ONLY保持一致,增强可读性
|
|
||||||
private static final int VIDEO_PROFILE_AUDIO_ONLY = VideoProfile.STATE_AUDIO_ONLY;
|
|
||||||
|
|
||||||
// ====================== 成员变量区 ======================
|
public static Call call;
|
||||||
// 静态通话实例,用于全局关联当前通话
|
|
||||||
public static Call sCurrentCall;
|
private Context context;
|
||||||
// 上下文对象
|
private AudioManager audioManager;
|
||||||
private Context mContext;
|
|
||||||
// 音频管理器,用于控制免提等音频相关操作
|
|
||||||
private AudioManager mAudioManager;
|
|
||||||
|
|
||||||
// ====================== 构造方法区 ======================
|
|
||||||
/**
|
|
||||||
* 构造通话管理实例
|
|
||||||
* @param context 上下文
|
|
||||||
*/
|
|
||||||
public PhoneCallManager(Context context) {
|
public PhoneCallManager(Context context) {
|
||||||
this.mContext = context;
|
this.context = context;
|
||||||
initAudioManager();
|
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
LogUtils.d(TAG, "PhoneCallManager: 通话管理工具初始化");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== 初始化辅助方法区 ======================
|
|
||||||
/**
|
/**
|
||||||
* 初始化音频管理器
|
* 接听电话
|
||||||
*/
|
|
||||||
private void initAudioManager() {
|
|
||||||
if (mContext == null) {
|
|
||||||
LogUtils.e(TAG, "initAudioManager: 上下文为空,无法初始化音频管理器");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
|
||||||
if (mAudioManager == null) {
|
|
||||||
LogUtils.e(TAG, "initAudioManager: 获取音频管理器失败");
|
|
||||||
} else {
|
|
||||||
LogUtils.d(TAG, "initAudioManager: 音频管理器初始化成功");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 通话核心操作方法区 ======================
|
|
||||||
/**
|
|
||||||
* 接听当前通话
|
|
||||||
*/
|
*/
|
||||||
public void answer() {
|
public void answer() {
|
||||||
// 校验系统版本,Android6.0以下不支持telecom.Call的answer方法
|
if (call != null) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
call.answer(VideoProfile.STATE_AUDIO_ONLY);
|
||||||
LogUtils.e(TAG, "answer: 系统版本低于Android6.0,不支持接听操作");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sCurrentCall == null) {
|
|
||||||
LogUtils.w(TAG, "answer: 当前无通话实例,无法接听");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
sCurrentCall.answer(VIDEO_PROFILE_AUDIO_ONLY);
|
|
||||||
openSpeaker();
|
openSpeaker();
|
||||||
LogUtils.d(TAG, "answer: 成功执行接听操作,通话模式为纯音频");
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
LogUtils.e(TAG, "answer: 接听通话权限不足", e);
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
LogUtils.e(TAG, "answer: 通话状态异常,无法接听", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 断开当前通话(拒接或挂断)
|
* 断开电话,包括来电时的拒接以及接听后的挂断
|
||||||
*/
|
*/
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
if (call != null) {
|
||||||
LogUtils.e(TAG, "disconnect: 系统版本低于Android6.0,不支持挂断操作");
|
call.disconnect();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sCurrentCall == null) {
|
|
||||||
LogUtils.w(TAG, "disconnect: 当前无通话实例,无法挂断");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
sCurrentCall.disconnect();
|
|
||||||
LogUtils.d(TAG, "disconnect: 成功执行挂断操作");
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
LogUtils.e(TAG, "disconnect: 挂断通话权限不足", e);
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
LogUtils.e(TAG, "disconnect: 通话状态异常,无法挂断", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开通话免提
|
* 打开免提
|
||||||
*/
|
*/
|
||||||
public void openSpeaker() {
|
public void openSpeaker() {
|
||||||
if (mAudioManager == null) {
|
if (audioManager != null) {
|
||||||
LogUtils.w(TAG, "openSpeaker: 音频管理器为空,无法打开免提");
|
audioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||||
return;
|
audioManager.setSpeakerphoneOn(true);
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
|
||||||
mAudioManager.setSpeakerphoneOn(true);
|
|
||||||
LogUtils.d(TAG, "openSpeaker: 免提功能已开启");
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
LogUtils.e(TAG, "openSpeaker: 音频模式设置失败,无法开启免提", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== 资源释放方法区 ======================
|
|
||||||
/**
|
/**
|
||||||
* 销毁资源,释放引用
|
* 销毁资源
|
||||||
*/
|
*/
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
// 关闭免提后再释放资源,避免音频状态异常
|
call = null;
|
||||||
if (mAudioManager != null) {
|
context = null;
|
||||||
mAudioManager.setSpeakerphoneOn(false);
|
audioManager = null;
|
||||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
|
||||||
LogUtils.d(TAG, "destroy: 音频状态已恢复默认");
|
|
||||||
}
|
|
||||||
sCurrentCall = null;
|
|
||||||
mAudioManager = null;
|
|
||||||
mContext = null;
|
|
||||||
LogUtils.d(TAG, "destroy: 通话管理工具资源已释放");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,243 +1,216 @@
|
|||||||
package cc.winboll.studio.contacts.phonecallui;
|
package cc.winboll.studio.contacts.phonecallui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听电话通信状态的服务,实现该类的同时必须提供电话管理的 UI
|
||||||
|
*
|
||||||
|
* @author aJIEw
|
||||||
|
* @see PhoneCallActivity
|
||||||
|
* @see android.telecom.InCallService
|
||||||
|
*/
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.media.AudioManager;
|
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.Call;
|
||||||
import android.telecom.InCallService;
|
import android.telecom.InCallService;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
import cc.winboll.studio.contacts.ActivityStack;
|
import cc.winboll.studio.contacts.ActivityStack;
|
||||||
import cc.winboll.studio.contacts.dun.Rules;
|
import cc.winboll.studio.contacts.dun.Rules;
|
||||||
import cc.winboll.studio.contacts.fragments.CallLogFragment;
|
import cc.winboll.studio.contacts.fragments.CallLogFragment;
|
||||||
import cc.winboll.studio.contacts.model.RingTongBean;
|
import cc.winboll.studio.contacts.model.RingTongBean;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
* @Author aJIEw, ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
|
||||||
* @Date 2025/02/13 06:58:04
|
|
||||||
* @Describe 通话状态监听服务(需 Android 6.0+),负责通话状态回调、铃音控制及黑白名单校验
|
|
||||||
* @see PhoneCallActivity
|
|
||||||
* @see android.telecom.InCallService
|
|
||||||
*/
|
|
||||||
public class PhoneCallService extends InCallService {
|
public class PhoneCallService extends InCallService {
|
||||||
// ====================== 常量定义区 ======================
|
|
||||||
public static final String TAG = "PhoneCallService";
|
public static final String TAG = "PhoneCallService";
|
||||||
// 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
|
|
||||||
|
|
||||||
// ====================== 成员变量区 ======================
|
MediaRecorder mediaRecorder;
|
||||||
private Call.Callback mCallCallback;
|
|
||||||
|
|
||||||
// ====================== 内部枚举类 ======================
|
private final Call.Callback callback = new Call.Callback() {
|
||||||
public enum CallType {
|
|
||||||
CALL_IN,
|
|
||||||
CALL_OUT
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 生命周期方法区 ======================
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onStateChanged(Call call, int state) {
|
||||||
super.onCreate();
|
super.onStateChanged(call, state);
|
||||||
initCallCallback();
|
switch (state) {
|
||||||
LogUtils.d(TAG, "onCreate: 通话监听服务已创建");
|
case TelephonyManager.CALL_STATE_OFFHOOK:
|
||||||
|
{
|
||||||
|
long callId = getCurrentCallId();
|
||||||
|
if (callId != -1) {
|
||||||
|
// 在这里可以对获取到的通话记录ID进行处理
|
||||||
|
//System.out.println("当前通话记录ID: " + callId);
|
||||||
|
|
||||||
|
// 电话接通,开始录音
|
||||||
|
startRecording(callId);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TelephonyManager.CALL_STATE_IDLE:
|
||||||
|
// 电话挂断,停止录音
|
||||||
|
stopRecording();
|
||||||
|
break;
|
||||||
|
case Call.STATE_ACTIVE: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Call.STATE_DISCONNECTED: {
|
||||||
|
ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCallAdded(Call call) {
|
public void onCallAdded(Call call) {
|
||||||
super.onCallAdded(call);
|
super.onCallAdded(call);
|
||||||
if (call == null) {
|
|
||||||
LogUtils.w(TAG, "onCallAdded: 新增通话为null,跳过处理");
|
call.registerCallback(callback);
|
||||||
|
PhoneCallManager.call = call;
|
||||||
|
CallType callType = null;
|
||||||
|
|
||||||
|
if (call.getState() == Call.STATE_RINGING) {
|
||||||
|
callType = CallType.CALL_IN;
|
||||||
|
} else if (call.getState() == Call.STATE_CONNECTING) {
|
||||||
|
callType = CallType.CALL_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callType != null) {
|
||||||
|
Call.Details details = call.getDetails();
|
||||||
|
String phoneNumber = details.getHandle().getSchemeSpecificPart();
|
||||||
|
|
||||||
|
// 记录原始铃声音量
|
||||||
|
//
|
||||||
|
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
|
||||||
|
int ringerVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
|
||||||
|
// 恢复铃声音量,预防其他意外条件导致的音量变化问题
|
||||||
|
//
|
||||||
|
|
||||||
|
// 读取应用配置,未配置就初始化配置文件
|
||||||
|
RingTongBean bean = RingTongBean.loadBean(this, RingTongBean.class);
|
||||||
|
if (bean == null) {
|
||||||
|
// 初始化配置
|
||||||
|
bean = new RingTongBean();
|
||||||
|
RingTongBean.saveBean(this, bean);
|
||||||
|
}
|
||||||
|
// 如果当前音量和应用保存的不一致就恢复为应用设定值
|
||||||
|
// 恢复铃声音量
|
||||||
|
try {
|
||||||
|
if (ringerVolume != bean.getStreamVolume()) {
|
||||||
|
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
|
||||||
|
//audioManager.setMode(AudioManager.RINGER_MODE_NORMAL);
|
||||||
|
}
|
||||||
|
} catch (java.lang.SecurityException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查电话接收规则
|
||||||
|
if (!Rules.getInstance(this).isAllowed(phoneNumber)) {
|
||||||
|
// 调低音量
|
||||||
|
try {
|
||||||
|
audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
|
||||||
|
//audioManager.setMode(AudioManager.RINGER_MODE_SILENT);
|
||||||
|
} catch (java.lang.SecurityException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
// 断开电话
|
||||||
|
call.disconnect();
|
||||||
|
// 停顿1秒,预防第一声铃声响动
|
||||||
|
try {
|
||||||
|
Thread.sleep(500);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LogUtils.d(TAG, "");
|
||||||
|
}
|
||||||
|
// 恢复铃声音量
|
||||||
|
try {
|
||||||
|
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
|
||||||
|
//audioManager.setMode(AudioManager.RINGER_MODE_NORMAL);
|
||||||
|
} catch (java.lang.SecurityException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
// 屏蔽电话结束
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
call.registerCallback(mCallCallback);
|
// 正常接听电话
|
||||||
PhoneCallManager.sCurrentCall = call;
|
PhoneCallActivity.actionStart(this, phoneNumber, callType);
|
||||||
LogUtils.d(TAG, "onCallAdded: 新增通话已注册回调 | 状态码=" + call.getState() + " | 描述=" + getCallStateDesc(call.getState()));
|
|
||||||
|
|
||||||
CallType callType = judgeCallType(call.getState());
|
|
||||||
if (callType == null) {
|
|
||||||
LogUtils.w(TAG, "onCallAdded: 无法识别通话类型 | 状态码=" + call.getState());
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String phoneNumber = getPhoneNumberFromCall(call);
|
|
||||||
if (phoneNumber == null || phoneNumber.isEmpty()) {
|
|
||||||
LogUtils.w(TAG, "onCallAdded: 通话号码为空");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LogUtils.d(TAG, "onCallAdded: 通话类型=" + callType.name() + " | 号码=" + phoneNumber);
|
|
||||||
|
|
||||||
handleRingerAndRuleCheck(phoneNumber, callType, call);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCallRemoved(Call call) {
|
public void onCallRemoved(Call call) {
|
||||||
super.onCallRemoved(call);
|
super.onCallRemoved(call);
|
||||||
if (call != null) {
|
call.unregisterCallback(callback);
|
||||||
call.unregisterCallback(mCallCallback);
|
PhoneCallManager.call = null;
|
||||||
LogUtils.d(TAG, "onCallRemoved: 通话回调已注销");
|
|
||||||
}
|
|
||||||
PhoneCallManager.sCurrentCall = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
CallLogFragment.updateCallLogFragment();
|
CallLogFragment.updateCallLogFragment();
|
||||||
LogUtils.d(TAG, "onDestroy: 通话服务已销毁,资源已释放");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== 核心:通话状态回调 ======================
|
public enum CallType {
|
||||||
private void initCallCallback() {
|
CALL_IN,
|
||||||
mCallCallback = new Call.Callback() {
|
CALL_OUT,
|
||||||
@Override
|
|
||||||
public void onStateChanged(Call call, int state) {
|
|
||||||
super.onStateChanged(call, state);
|
|
||||||
LogUtils.d(TAG, "onStateChanged: 状态变更 | 状态码=" + state + " | 描述=" + getCallStateDesc(state));
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
// 通话活跃状态(仅监听,无录音)
|
|
||||||
case CALL_STATE_ACTIVE:
|
|
||||||
LogUtils.d(TAG, "onStateChanged: 通话已接通/活跃");
|
|
||||||
break;
|
|
||||||
// 响铃状态
|
|
||||||
case CALL_STATE_RINGING:
|
|
||||||
LogUtils.d(TAG, "onStateChanged: 通话处于响铃状态");
|
|
||||||
break;
|
|
||||||
// 空闲状态(通话挂断后)
|
|
||||||
case CALL_STATE_IDLE:
|
|
||||||
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;
|
|
||||||
default:
|
|
||||||
LogUtils.w(TAG, "onStateChanged: 未处理的状态码=" + state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== 核心业务方法区(铃音控制+黑白名单) ======================
|
|
||||||
private CallType judgeCallType(int callState) {
|
|
||||||
switch (callState) {
|
|
||||||
case CALL_STATE_RINGING:
|
|
||||||
return CallType.CALL_IN;
|
|
||||||
case CALL_STATE_CONNECTING:
|
|
||||||
return CallType.CALL_OUT;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPhoneNumberFromCall(Call call) {
|
private void startRecording(long callId) {
|
||||||
|
LogUtils.d(TAG, "startRecording(...)");
|
||||||
|
mediaRecorder = new MediaRecorder();
|
||||||
|
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
|
||||||
|
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
|
||||||
|
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
|
||||||
|
mediaRecorder.setOutputFile(getOutputFilePath(callId));
|
||||||
try {
|
try {
|
||||||
Call.Details details = call.getDetails();
|
mediaRecorder.prepare();
|
||||||
if (details == null || details.getHandle() == null) {
|
mediaRecorder.start();
|
||||||
return null;
|
} catch (IOException e) {
|
||||||
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getOutputFilePath(long callId) {
|
||||||
|
LogUtils.d(TAG, "getOutputFilePath(...)");
|
||||||
|
// 设置录音文件的保存路径
|
||||||
|
File file = new File(getExternalFilesDir(TAG), String.format("call_%d.mp4", callId));
|
||||||
|
return file.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopRecording() {
|
||||||
|
LogUtils.d(TAG, "stopRecording()");
|
||||||
|
if (mediaRecorder != null) {
|
||||||
|
mediaRecorder.stop();
|
||||||
|
mediaRecorder.release();
|
||||||
|
mediaRecorder = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getCurrentCallId() {
|
||||||
|
LogUtils.d(TAG, "getCurrentCallId()");
|
||||||
|
ContentResolver contentResolver = getApplicationContext().getContentResolver();
|
||||||
|
Uri callLogUri = Uri.parse("content://call_log/calls");
|
||||||
|
String[] projection = {"_id", "number", "call_type", "date"};
|
||||||
|
String selection = "call_type = " + CallLog.Calls.OUTGOING_TYPE + " OR call_type = " + CallLog.Calls.INCOMING_TYPE;
|
||||||
|
String sortOrder = "date DESC";
|
||||||
|
|
||||||
|
try {
|
||||||
|
Cursor cursor = contentResolver.query(callLogUri, projection, selection, null, sortOrder);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
return cursor.getLong(cursor.getColumnIndex("_id"));
|
||||||
}
|
}
|
||||||
return details.getHandle().getSchemeSpecificPart();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.e(TAG, "getPhoneNumberFromCall: 解析号码失败", e);
|
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleRingerAndRuleCheck(String phoneNumber, CallType callType, Call call) {
|
return -1;
|
||||||
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
|
|
||||||
if (audioManager == null) {
|
|
||||||
LogUtils.e(TAG, "handleRingerAndRuleCheck: 音频管理器获取失败");
|
|
||||||
startPhoneCallActivity(phoneNumber, callType);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RingTongBean ringTongBean = RingTongBean.loadBean(this, RingTongBean.class);
|
|
||||||
if (ringTongBean == null) {
|
|
||||||
ringTongBean = new RingTongBean();
|
|
||||||
RingTongBean.saveBean(this, ringTongBean);
|
|
||||||
LogUtils.d(TAG, "handleRingerAndRuleCheck: 铃音配置已初始化");
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreRingerVolume(audioManager, ringTongBean);
|
|
||||||
|
|
||||||
if (!Rules.getInstance(this).isAllowed(phoneNumber)) {
|
|
||||||
handleForbiddenCall(audioManager, ringTongBean, call);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
startPhoneCallActivity(phoneNumber, callType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreRingerVolume(AudioManager audioManager, RingTongBean bean) {
|
|
||||||
try {
|
|
||||||
int currentVol = audioManager.getStreamVolume(AudioManager.STREAM_RING);
|
|
||||||
int configVol = bean.getStreamVolume();
|
|
||||||
if (currentVol != configVol) {
|
|
||||||
audioManager.setStreamVolume(AudioManager.STREAM_RING, configVol, 0);
|
|
||||||
LogUtils.d(TAG, "restoreRingerVolume: 音量恢复 | 配置值=" + configVol + " | 当前值=" + currentVol);
|
|
||||||
}
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
LogUtils.e(TAG, "restoreRingerVolume: 音量设置失败(权限不足)", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleForbiddenCall(AudioManager audioManager, RingTongBean bean, Call call) {
|
|
||||||
try {
|
|
||||||
audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
|
|
||||||
call.disconnect();
|
|
||||||
Thread.sleep(500);
|
|
||||||
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
|
|
||||||
LogUtils.d(TAG, "handleForbiddenCall: 禁止通话处理完成");
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
LogUtils.e(TAG, "handleForbiddenCall: 音量操作失败", e);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LogUtils.e(TAG, "handleForbiddenCall: 延迟线程中断", e);
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startPhoneCallActivity(String phoneNumber, CallType callType) {
|
|
||||||
PhoneCallActivity.actionStart(this, phoneNumber, callType);
|
|
||||||
LogUtils.d(TAG, "startPhoneCallActivity: 通话界面已启动");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 辅助工具方法区 ======================
|
|
||||||
private String getCallStateDesc(int state) {
|
|
||||||
switch (state) {
|
|
||||||
case CALL_STATE_RINGING:
|
|
||||||
return "响铃";
|
|
||||||
case CALL_STATE_CONNECTING:
|
|
||||||
return "连接中";
|
|
||||||
case CALL_STATE_ACTIVE:
|
|
||||||
return "活跃";
|
|
||||||
case CALL_STATE_IDLE:
|
|
||||||
return "空闲";
|
|
||||||
case CALL_STATE_DISCONNECTED:
|
|
||||||
return "已断开";
|
|
||||||
default:
|
|
||||||
return "未知状态";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 静态内部类:PhoneCallManager(避免编译报错) ======================
|
|
||||||
public static class PhoneCallManager {
|
|
||||||
public static Call sCurrentCall;
|
|
||||||
|
|
||||||
private PhoneCallManager() {
|
|
||||||
// 私有构造,禁止实例化
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Call getCurrentCall() {
|
|
||||||
return sCurrentCall;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user