最近在看Android 5.0的代码,发现可以通过音量键来控制情景模式,而且在此次升级的静音模式可谓是完全静音了,就连闹钟都不会发音。
我要做一个可以在静音模式下选择闹钟是否仍然响铃的开关。
本文仅为学习笔记,大神勿喷。
在DeskClock源码中,com.android.deskclock.SettingsActivity.java 中
有如下字段来记录在静音模式下是否响闹钟铃音,不过这个在4.4开始功能就被移除了。
public static final String KEY_ALARM_IN_SILENT_MODE = "alarm_in_silent_mode";
private void refresh() {
....
//这个在5.0中已经被移除
final CheckBoxPreference alarmInSilentModePref =
(CheckBoxPreference) findPreference(KEY_ALARM_IN_SILENT_MODE);
final int silentModeStreams =
Settings.System.getInt (getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
alarmInSilentModePref.setChecked(
(silentModeStreams & ALARM_STREAM_TYPE_BIT) == 0);
}
是由一个CheckBox开关来控制如下。
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
if (KEY_ALARM_IN_SILENT_MODE.equals(preference.getKey())) {
CheckBoxPreference pref = (CheckBoxPreference) preference;
int ringerModeStreamTypes = Settings.System.getInt(
getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
if (pref.isChecked()) {
ringerModeStreamTypes &= ~ALARM_STREAM_TYPE_BIT;
} else {
ringerModeStreamTypes |= ALARM_STREAM_TYPE_BIT;
}
Settings.System.putInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
ringerModeStreamTypes);
return true;
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
//原来这个通过音量键控制的情景模式窗口实在SystemUI中实现的
com.android.systemui.volume.ZenModePanel.java
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
//在这里创建了三个按钮,接下来看下addButton方法
mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
mZenButtons.addButton(R.string.interruption_level_priority,
Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
//先注意下这里绑定了一个回调函数
mZenButtons.setCallback(mZenButtonsCallback);
....
}
接下来看下SegmentedButtons这个extends LinearLayout的自定义控件
package com.android.systemui.volume.SegmentedButtons.java
public void addButton(int labelResId, Object value) {
final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
...
addView(b);
//在这里给每个新建的button设置了一个Tag
b.setTag(value);
//并注册点击事件
b.setOnClickListener(mClick);
Interaction.register(b, new Interaction.Callback() {
@Override
public void onInteraction() {
fireInteraction();
}
});
}
private final View.OnClickListener mClick = new View.OnClickListener() {
@Override
public void onClick(View v) {
//调用方法将Button的Tag传入
setSelectedValue(v.getTag());
}
};
public void setSelectedValue(Object value) {
if (Objects.equals(value, mSelectedValue)) return;
//将Button的Tag传递给mSelectedValue
mSelectedValue = value;
...
fireOnSelected();
}
private void fireOnSelected() {
if (mCallback != null) {
//在这里用了一个回调
mCallback.onSelected(mSelectedValue);
}
}
回到之前设置回调处继续看
com.android.systemui.volume.ZenModePanel.java
private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
@Override
public void onSelected(Object value) {
if (value != null && mZenButtons.isShown()) {
//这里继续将这个标识传递到ZenModeController mController中。
//然而public interface ZenModeController是个接口
//实现类在ZenModeControllerImpl.java
mController.setZen((Integer) value);
}
}
@Override
public void onInteraction() {
fireInteraction();
}
};
com.android.systemui.statusbar.policy.ZenModeControllerImpl.java
//在这个对象的构造方法中就有参数Global.ZEN_MODE
//罗嗦这么多
//实质就是在数据库中加入一个Global.ZEN_MODE字段来标识状态
//GlobalSetting mModeSetting;
public ZenModeControllerImpl(Context context, Handler handler) {
mContext = context;
mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
@Override
protected void handleValueChanged(int value) {
fireZenChanged(value);
}
};
...
}
public void setZen(int zen) {
mModeSetting.setValue(zen);
}
com.android.systemui.qs.GlobalSetting.java
public abstract class GlobalSetting extends ContentObserver implements Listenable {
private final Context mContext;
private final String mSettingName;
protected abstract void handleValueChanged(int value);
public GlobalSetting(Context context, Handler handler, String settingName) {
super(handler);
mContext = context;
mSettingName = settingName;
}
...
public void setValue(int value) {
Global.putInt(mContext.getContentResolver(), mSettingName, value);
}
...
}
//既然是给Button状态加监听,那么这个数据库字段的变化也就一定会有人来监听其改变
//果然在Framework中有监听此字段
com.android.server.notification.ZenModeHelper.java
private class SettingsObserver extends ContentObserver {
private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE);
public SettingsObserver(Handler handler) {
super(handler);
}
public void observe() {
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(ZEN_MODE, false /*notifyForDescendents*/, this);
update(null);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
android.util.Log.i("song","ZenModeHelper -- onChange()");
update(uri);
}
public void update(Uri uri) {
if (ZEN_MODE.equals(uri)) {
android.util.Log.i("song","ZenModeHelper -- update()");
updateZenMode();
}
}
}
public void updateZenMode() {
android.util.Log.i("song","ZenModeHelper -- updateZenMode()");
final int mode = Global.getInt(mContext.getContentResolver(),
Global.ZEN_MODE, Global.ZEN_MODE_OFF);
if (mode != mZenMode) {
ZenLog.traceUpdateZenMode(mZenMode, mode);
}
mZenMode = mode;
final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
//这里有个排除包,猜测就是这里将静音模式下某些应用排除掉,让其可以响铃
//final String[] exceptionPackages = null; // none (for now)
//于是注掉上面,给其赋个值,排除掉闹钟试试。
final String[] exceptionPackages = new String[2];
exceptionPackages[0] = "com.android.deskclock";
// call restrictions
final boolean muteCalls = zen && !mConfig.allowCalls;
//这里是对振动的控制
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, USAGE_NOTIFICATION_RINGTONE,
muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
//这里就是对响铃的控制了AppOpsManager mAppOps;
//继续查看AppOpsManager
mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, USAGE_NOTIFICATION_RINGTONE,
muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
// alarm restrictions
///M: Operator customization to mute alarm or not. @{
final boolean muteAlarms;
if (mZenModeHelperExt != null) {
muteAlarms = mZenModeHelperExt
.customizeMuteAlarm(mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS);
} else {
muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
}
///M: Operator customization to mute alarm or not. @}
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, USAGE_ALARM,
muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, USAGE_ALARM,
muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
// force ringer mode into compliance
if (mAudioManager != null) {
int ringerMode = mAudioManager.getRingerMode();
int forcedRingerMode = -1;
if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
mPreviousRingerMode = ringerMode;
if (DEBUG) Slog.d(TAG, "Silencing ringer");
forcedRingerMode = AudioManager.RINGER_MODE_SILENT;
}
} else {
if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
if (DEBUG) Slog.d(TAG, "Unsilencing ringer");
forcedRingerMode = mPreviousRingerMode != -1 ? mPreviousRingerMode
: AudioManager.RINGER_MODE_NORMAL;
mPreviousRingerMode = -1;
}
}
if (forcedRingerMode != -1) {
mAudioManager.setRingerMode(forcedRingerMode, false /*checkZen*/);
ZenLog.traceSetRingerMode(forcedRingerMode);
}
}
dispatchOnZenModeChanged();
}
android.app.AppOpsManager.java
//使用了aidl服务
//它的实现在这里com.android.server.AppOpsService extends IAppOpsService.Stub
IAppOpsService mService;
public void setRestriction(int code, @AttributeUsage int usage, int mode,
String[] exceptionPackages) {
android.util.Log.i("song","AppOpsManager -- setRestriction()");
try {
final int uid = Binder.getCallingUid();
mService.setAudioRestriction(code, usage, uid, mode, exceptionPackages);
} catch (RemoteException e) {
}
}
@Override
public void setAudioRestriction(int code, int usage, int uid, int mode,
String[] exceptionPackages) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
synchronized (this) {
SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
if (usageRestrictions == null) {
usageRestrictions = new SparseArray<Restriction>();
mAudioRestrictions.put(code, usageRestrictions);
}
usageRestrictions.remove(usage);
if (mode != AppOpsManager.MODE_ALLOWED) {
final Restriction r = new Restriction();
r.mode = mode;
if (exceptionPackages != null) {
final int N = exceptionPackages.length;
r.exceptionPackages = new ArraySet<String>(N);
for (int i = 0; i < N; i++) {
final String pkg = exceptionPackages[i];
if (pkg != null) {
//最终将他们放入一个排除列表中
r.exceptionPackages.add(pkg.trim());
}
}
}
usageRestrictions.put(usage, r);
}
}
}
好了我要做的就是让在静音模式下继续可以闹钟响铃,接下来这个排除列表如何执行就交个大神们分析吧。
2110

被折叠的 条评论
为什么被折叠?



