业务背景
定制需求实现
目标:通话中禁用移动网络设置中的网络模式和APN入口。
Google原生行为分析
在原生Android中:
-
飞行模式:
- 无法在通话中开启:系统会自动阻止,因飞行模式会断开通话所需的射频。
- APN/网络模式修改:通话中允许进入设置,但修改可能导致数据连接中断(取决于版本,部分版本会自动禁用)。
-
通话中设置菜单状态:
- 网络模式(如4G/3G切换):通常允许修改,但可能不立即生效。
- APN配置:允许进入,但保存新APN可能导致当前数据会话重置。
原生与定制的对比
场景 | Google原生行为 | 定制后行为 |
---|---|---|
通话中修改网络模式 | 允许但可能中断数据连接 | 禁止,菜单项置灰 |
通话中进入APN设置 | 允许,保存可能重置数据 | 禁止,Toast提示并拦截跳转 |
飞行模式下的移动网络 | 直接隐藏或禁用整个菜单 | 明确置灰,提示不可用 |
方案分析
代码关键点
- 通话状态判断 :通过
TelephonyManager.CALL_STATE
判断是否在通话中。 - Preference动态控制 :使用
setEnabled(false)
置灰,并通过点击事件拦截防止跳转。 - 广播监听飞行模式:实时响应飞行模式开关,禁用整个移动网络菜单。
方案逻辑
- 在相关的PreferenceFragment(如MobileNetworkSettings)中注册一个通话状态监听器。
- 当通话状态变为OFFHOOK(正在通话中),则禁用网络模式和APN的Preference。
- 当通话结束后,恢复这些Preference的可用状态。
- 可能需要覆盖Preference的点击事件,在通话中阻止跳转到APN设置页面。
针对代码实现的具体步骤:
首先,找到移动网络设置中的相关Preference的key,比如"network_mode"和"apn_settings"。
在MobileNetworkSettings.java中,可能需要添加如下逻辑:
- 在onCreate()中初始化这些Preference。
- 在onResume()中注册通话状态监听,比如通过TelephonyManager.listen()。
- 当通话状态改变时,更新Preference的enabled状态。
- 在onPause()中取消注册监听器,以避免资源泄漏。
另外,APN设置的入口可能是一个PreferenceScreen,点击后会跳转到ApnSettings。因此,需要禁用该Preference,或者在点击时检查通话状态,并弹出提示。
代码
实现步骤
1. 监听通话状态
在设置页面的Fragment(如MobileNetworkSettings.java
)中注册通话状态监听:
java
// 添加成员变量
private TelephonyManager mTelephonyManager;
private PhoneStateListener mPhoneStateListener;
private boolean mInCall = false;
// 在onCreate中初始化
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTelephonyManager = getSystemService(TelephonyManager.class);
mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
mInCall = (state != TelephonyManager.CALL_STATE_IDLE);
updatePreferenceStates();
}
};
}
// 在onResume中注册监听
@Override
public void onResume() {
super.onResume();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
// 在onPause中取消注册
@Override
public void onPause() {
super.onPause();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
2. 动态控制Preference状态
java
private void updatePreferenceStates() {
// 获取网络模式和APN的Preference
Preference networkModePref = findPreference("network_mode_key");
Preference apnPref = findPreference("apn_settings_key");
if (networkModePref != null) {
networkModePref.setEnabled(!mInCall);
}
if (apnPref != null) {
apnPref.setEnabled(!mInCall);
// 拦截点击事件
apnPref.setOnPreferenceClickListener(preference -> {
if (mInCall) {
Toast.makeText(getContext(), "Cannot modify APN during a call",
Toast.LENGTH_SHORT).show();
return true; // 阻止跳转
}
return false; // 允许正常跳转
});
}
}
3. 飞行模式下的处理
监听飞行模式变化并禁用相关菜单:
java
// 在Fragment中添加广播接收器
private BroadcastReceiver mAirplaneModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
updatePreferenceStatesForAirplaneMode(isAirplaneModeOn);
}
};
// 注册广播
@Override
public void onResume() {
super.onResume();
// 通话状态监听...
registerReceiver(mAirplaneModeReceiver,
new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
}
@Override
public void onPause() {
super.onPause();
unregisterReceiver(mAirplaneModeReceiver);
}
private void updatePreferenceStatesForAirplaneMode(boolean isAirplaneModeOn) {
Preference mobileNetworkPref = findPreference("mobile_network_key");
if (mobileNetworkPref != null) {
mobileNetworkPref.setEnabled(!isAirplaneModeOn);
}
}
相关类
Android提供了TelephonyManager,可以监听通话状态。例如,通过监听TelephonyManager.EXTRA_STATE,或者使用PhoneStateListener来监听通话状态的变化。
java
package android.telephony;
public class TelephonyManager {
public static final int CALL_COMPOSER_STATUS_BUSINESS_ONLY = 2;
public static final int CALL_COMPOSER_STATUS_OFF = 0;
public static final int CALL_COMPOSER_STATUS_ON = 1;
public static final int CALL_STATE_IDLE = 0;
public static final int CALL_STATE_OFFHOOK = 2;
public static final int CALL_STATE_RINGING = 1;
}
当检测到通话处于活动状态时(比如,来电接通时),需要禁用相关Preference。
总结
通过监听通话状态和飞行模式,动态控制Preference的可用性及点击事件拦截,实现符合运营商需求的设置菜单逻辑。