Android8.0 Do not disturb(DND)/Zenmode 添加一个新的模式

本文介绍了如何在Android系统中增加自定义的请勿打扰(DND)模式,并详细解释了实现过程,包括修改代码以添加新的DND选项到快速设置(QS)、实现新增模式的功能以及调整音量设置界面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里添加的模式可能涉及的地方不全面,目前把已知的地方都加上。可以搜索ZEN_MODE_ALARMS来看看哪里还需要加代码。目前就改了这些地方,有些地方不知道有什么用,可能只是log信息,有些是显示的图标,根据不同模式显示不同icon。
这里写图片描述

1.在QS中添加选项
打开关闭DND是在quick settings中的,当然也可以按音量下。我们这要在QS中添加,可以按照其他dnd模式来添加代码。

先加数据库。

frameworks/base/core/java/android/provider/Settings.java

        /**
         * Defines global zen mode.  ZEN_MODE_OFF, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
         * or ZEN_MODE_NO_INTERRUPTIONS.
         *
         * @hide
         */
        public static final String ZEN_MODE = "zen_mode";

        /** @hide */ public static final int ZEN_MODE_OFF = 0;
        /** @hide */ public static final int ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
        /** @hide */ public static final int ZEN_MODE_NO_INTERRUPTIONS = 2;
        /** @hide */ public static final int ZEN_MODE_ALARMS = 3;
        /** @hide */ public static final int ZEN_MODE_GAME = 4;

        /** @hide */ public static String zenModeToString(int mode) {
            if (mode == ZEN_MODE_IMPORTANT_INTERRUPTIONS) return "ZEN_MODE_IMPORTANT_INTERRUPTIONS";
            if (mode == ZEN_MODE_ALARMS) return "ZEN_MODE_ALARMS";
            if (mode == ZEN_MODE_NO_INTERRUPTIONS) return "ZEN_MODE_NO_INTERRUPTIONS";
            if (mode == ZEN_MODE_GAME) return "ZEN_MODE_GAME";
            return "ZEN_MODE_OFF";
        }

        /** @hide */ public static boolean isValidZenMode(int value) {
            switch (value) {
                case Global.ZEN_MODE_OFF:
                case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                case Global.ZEN_MODE_ALARMS:
                case Global.ZEN_MODE_NO_INTERRUPTIONS:
                case Global.ZEN_MODE_GAME:
                    return true;
                default:
                    return false;
            }
        }

然后添加QS
frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java

    @Override
    protected void handleUpdateState(BooleanState state, Object arg) {
        final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
        final boolean newValue = zen != ZEN_MODE_OFF;
        final boolean valueChanged = state.value != newValue;
        state.dualTarget = true;
        state.value = newValue;
        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
        checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
        switch (zen) {
            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
                state.label = mContext.getString(R.string.quick_settings_dnd_priority_label);
                state.contentDescription = mContext.getString(
                        R.string.accessibility_quick_settings_dnd_priority_on);
                break;
            case Global.ZEN_MODE_NO_INTERRUPTIONS:
                state.icon = TOTAL_SILENCE;
                state.label = mContext.getString(R.string.quick_settings_dnd_none_label);
                state.contentDescription = mContext.getString(
                        R.string.accessibility_quick_settings_dnd_none_on);
                break;
            case ZEN_MODE_ALARMS:
                state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
                state.label = mContext.getString(R.string.quick_settings_dnd_alarms_label);
                state.contentDescription = mContext.getString(
                        R.string.accessibility_quick_settings_dnd_alarms_on);
                break;
            case Global.ZEN_MODE_GAME:
                state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
                state.label = mContext.getString(R.string.quick_settings_dnd_game_label);
                state.contentDescription = mContext.getString(
                        R.string.accessibility_quick_settings_dnd_game_on);
                break;
            default:
                state.icon = TOTAL_SILENCE.equals(state.icon) ? mDisableTotalSilence : mDisable;
                state.label = mContext.getString(R.string.quick_settings_dnd_label);
                state.contentDescription = mContext.getString(
                        R.string.accessibility_quick_settings_dnd);
                break;
        }
        if (valueChanged) {
            fireToggleStateChanged(state.value);
        }
        state.dualLabelContentDescription = mContext.getResources().getString(
                R.string.accessibility_quick_settings_open_settings, getTileLabel());
        state.expandedAccessibilityClassName = Switch.class.getName();
    }

这里添加的是QS中显示的,也就是dnd没有展开时的。

这里写图片描述

当你点击dnd下面的那个箭头就会展开,和蓝牙类似。下面就是展开的画面。最上面的按钮就是mZenButtons,下面是第一次使用会有个介绍mZenIntroduction,就是绿色的框边上有个x关闭。
这里写图片描述

代码位置在
frameworks/base/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
这里只需要模仿其他dnd模式来添加代码。

public class ZenModePanel extends FrameLayout {
    private static final String TAG = "ZenModePanel";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    public static final int STATE_MODIFY = 0;
    public static final int STATE_AUTO_RULE = 1;
    public static final int STATE_OFF = 2;

    private static final int SECONDS_MS = 1000;
    private static final int MINUTES_MS = 60 * SECONDS_MS;

    private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
    private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
    private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
    private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
    private static final int FOREVER_CONDITION_INDEX = 0;
    private static final int COUNTDOWN_CONDITION_INDEX = 1;
    private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
    private static final int COUNTDOWN_CONDITION_COUNT = 2;

    public static final Intent ZEN_SETTINGS
            = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
    public static final Intent ZEN_PRIORITY_SETTINGS
            = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);

    private static final long TRANSITION_DURATION = 300;

    private final Context mContext;
    protected final LayoutInflater mInflater;
    private final H mHandler = new H();
    private final ZenPrefs mPrefs;
    private final TransitionHelper mTransitionHelper = new TransitionHelper();
    private final Uri mForeverId;
    private final ConfigurableTexts mConfigurableTexts;

    private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));

    protected SegmentedButtons mZenButtons;
    private View mZenIntroduction;
    private TextView mZenIntroductionMessage;
    private View mZenIntroductionConfirm;
    private TextView mZenIntroductionCustomize;
    protected LinearLayout mZenConditions;
    private TextView mZenAlarmWarning;
    private RadioGroup mZenRadioGroup;
    private LinearLayout mZenRadioGroupContent;

    private Callback mCallback;
    private ZenModeController mController;
    private boolean mCountdownConditionSupported;
    private boolean mRequestingConditions;
    private Condition mExitCondition;
    private int mBucketIndex = -1;
    private boolean mExpanded;
    private boolean mHidden;
    private int mSessionZen;
    private int mAttachedZen;
    private boolean mAttached;
    private Condition mSessionExitCondition;
    private Condition[] mConditions;
    private Condition mTimeCondition;
    private boolean mVoiceCapable;

    protected int mZenModeConditionLayoutId;
    protected int mZenModeButtonLayoutId;
    private View mEmpty;
    private TextView mEmptyText;
    private ImageView mEmptyIcon;
    private View mAutoRule;
    private TextView mAutoTitle;
    private int mState = STATE_MODIFY;
    private ViewGroup mEdit;

    public ZenModePanel(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        mPrefs = new ZenPrefs();
        mInflater = LayoutInflater.from(mContext.getApplicationContext());
        mForeverId = Condition.newId(mContext).appendPath("forever").build();
        mConfigurableTexts = new ConfigurableTexts(mContext);
        mVoiceCapable = Util.isVoiceCapable(mContext);
        mZenModeConditionLayoutId = R.layout.zen_mode_condition;
        mZenModeButtonLayoutId = R.layout.zen_mode_button;
        if (DEBUG) Log.d(mTag, "new ZenModePanel");
    }

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("ZenModePanel state:");
        pw.print("  mCountdownConditionSupported="); pw.println(mCountdownConditionSupported);
        pw.print("  mRequestingConditions="); pw.println(mRequestingConditions);
        pw.print("  mAttached="); pw.println(mAttached);
        pw.print("  mHidden="); pw.println(mHidden);
        pw.print("  mExpanded="); pw.println(mExpanded);
        pw.print("  mSessionZen="); pw.println(mSessionZen);
        pw.print("  mAttachedZen="); pw.println(mAttachedZen);
        pw.print("  mConfirmedPriorityIntroduction=");
        pw.println(mPrefs.mConfirmedPriorityIntroduction);
        pw.print("  mConfirmedSilenceIntroduction=");
        pw.println(mPrefs.mConfirmedSilenceIntroduction);
        pw.print("  mVoiceCapable="); pw.println(mVoiceCapable);
        mTransitionHelper.dump(fd, pw, args);
    }

    protected void createZenButtons() {
        mZenButtons = findViewById(R.id.zen_buttons);
        mZenButtons.addButton(R.string.interruption_level_none_twoline,
                R.string.interruption_level_none_with_warning,
                Global.ZEN_MODE_NO_INTERRUPTIONS);
        mZenButtons.addButton(R.string.interruption_level_game_twoline,
                R.string.interruption_level_game,
                Global.ZEN_MODE_GAME);
        mZenButtons.addButton(R.string.interruption_level_alarms_twoline,
                R.string.interruption_level_alarms,
                Global.ZEN_MODE_ALARMS);
        mZenButtons.addButton(R.string.interruption_level_priority_twoline,
                R.string.interruption_level_priority,
                Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
        mZenButtons.setCallback(mZenButtonsCallback);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        createZenButtons();
        mZenIntroduction = findViewById(R.id.zen_introduction);
        mZenIntroductionMessage = findViewById(R.id.zen_introduction_message);
        mZenIntroductionConfirm = findViewById(R.id.zen_introduction_confirm);
        mZenIntroductionConfirm.setOnClickListener(v -> confirmZenIntroduction());
        mZenIntroductionCustomize = findViewById(R.id.zen_introduction_customize);
        mZenIntroductionCustomize.setOnClickListener(v -> {
            confirmZenIntroduction();
            if (mCallback != null) {
                mCallback.onPrioritySettings();
            }
        });
        mConfigurableTexts.add(mZenIntroductionCustomize, R.string.zen_priority_customize_button);

        mZenConditions = findViewById(R.id.zen_conditions);
        mZenAlarmWarning = findViewById(R.id.zen_alarm_warning);
        mZenRadioGroup = findViewById(R.id.zen_radio_buttons);
        mZenRadioGroupContent = findViewById(R.id.zen_radio_buttons_content);

        mEdit = findViewById(R.id.edit_container);

        mEmpty = findViewById(android.R.id.empty);
        mEmpty.setVisibility(INVISIBLE);
        mEmptyText = mEmpty.findViewById(android.R.id.title);
        mEmptyIcon = mEmpty.findViewById(android.R.id.icon);

        mAutoRule = findViewById(R.id.auto_rule);
        mAutoTitle = mAutoRule.findViewById(android.R.id.title);
        mAutoRule.setVisibility(INVISIBLE);
    }

    private View getView(int state) {
        switch (state) {
            case STATE_AUTO_RULE:
                return mAutoRule;
            case STATE_OFF:
                return mEmpty;
            default:
                return mEdit;
        }
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mConfigurableTexts.update();
        if (mZenButtons != null) {
            mZenButtons.update();
        }
    }

    private void confirmZenIntroduction() {
        final String prefKey = prefKeyForConfirmation(getSelectedZen(Global.ZEN_MODE_OFF));
        if (prefKey == null) return;
        if (DEBUG) Log.d(TAG, "confirmZenIntroduction " + prefKey);
        Prefs.putBoolean(mContext, prefKey, true);
        mHandler.sendEmptyMessage(H.UPDATE_WIDGETS);
    }

    private static String prefKeyForConfirmation(int zen) {
        switch (zen) {
            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                return Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION;
            case Global.ZEN_MODE_NO_INTERRUPTIONS:
                return Prefs.Key.DND_CONFIRMED_SILENCE_INTRODUCTION;
            case Global.ZEN_MODE_ALARMS:
                return Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION;
            case Global.ZEN_MODE_GAME:
                return Prefs.Key.DND_CONFIRMED_GAME_INTRODUCTION;
            default:
                return null;
        }
    }

    private void onAttach() {
        setExpanded(true);
        mAttached = true;
        mAttachedZen = mController.getZen();
        ZenRule manualRule = mController.getManualRule();
        mExitCondition = manualRule != null ? manualRule.condition : null;
        if (DEBUG) Log.d(mTag, "onAttach " + mAttachedZen + " " + manualRule);
        handleUpdateManualRule(manualRule);
        mZenButtons.setSelectedValue(mAttachedZen, false);
        mSessionZen = mAttachedZen;
        mTransitionHelper.clear();
        mController.addCallback(mZenCallback);
        setSessionExitCondition(copy(mExitCondition));
        updateWidgets();
        setRequestingConditions(!mHidden);
        ensureSelection();
    }

    private void onDetach() {
        if (DEBUG) Log.d(mTag, "onDetach");
        setExpanded(false);
        checkForAttachedZenChange();
        mAttached = false;
        mAttachedZen = -1;
        mSessionZen = -1;
        mController.removeCallback(mZenCallback);
        setSessionExitCondition(null);
        setRequestingConditions(false);
        mTransitionHelper.clear();
    }

    @Override
    public void onVisibilityAggregated(boolean isVisible) {
        super.onVisibilityAggregated(isVisible);
        if (isVisible == mAttached) return;
        if (isVisible) {
            onAttach();
        } else {
            onDetach();
        }
    }

    private void setSessionExitCondition(Condition condition) {
        if (Objects.equals(condition, mSessionExitCondition)) return;
        if (DEBUG) Log.d(mTag, "mSessionExitCondition=" + getConditionId(condition));
        mSessionExitCondition = condition;
    }

    public void setHidden(boolean hidden) {
        if (mHidden == hidden) return;
        if (DEBUG) Log.d(mTag, "hidden=" + hidden);
        mHidden = hidden;
        setRequestingConditions(mAttached && !mHidden);
        updateWidgets();
    }

    private void checkForAttachedZenChange() {
        final int selectedZen = getSelectedZen(-1);
        if (DEBUG) Log.d(mTag, "selectedZen=" + selectedZen);
        if (selectedZen != mAttachedZen) {
            if (DEBUG) Log.d(mTag, "attachedZen: " + mAttachedZen + " -> " + selectedZen);
            if (selectedZen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
                mPrefs.trackNoneSelected();
            }
        }
    }

    private void setExpanded(boolean expanded) {
        if (expanded == mExpanded) return;
        if (DEBUG) Log.d(mTag, "setExpanded " + expanded);
        mExpanded = expanded;
        updateWidgets();
        fireExpanded();
    }

    /** Start or stop requesting relevant zen mode exit conditions */
    private void setRequestingConditions(final boolean requesting) {
        if (mRequestingConditions == requesting) return;
        if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting);
        mRequestingConditions = requesting;
        if (mRequestingConditions) {
            mTimeCondition = parseExistingTimeCondition(mContext, mExitCondition);
            if (mTimeCondition != null) {
                mBucketIndex = -1;
            } else {
                mBucketIndex = DEFAULT_BUCKET_INDEX;
                mTimeCondition = ZenModeConfig.toTimeCondition(mContext,
                        MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
            }
            if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);

            mConditions = null; // reset conditions
            handleUpdateConditions();
        } else {
            hideAllConditions();
        }
    }

    protected void addZenConditions(int count) {
        for (int i = 0; i < count; i++) {
            final View rb = mInflater.inflate(mZenModeButtonLayoutId, mEdit, false);
            rb.setId(i);
            mZenRadioGroup.addView(rb);
            final View rbc = mInflater.inflate(mZenModeConditionLayoutId, mEdit, false);
            rbc.setId(i + count);
            mZenRadioGroupContent.addView(rbc);
        }
    }

    public void init(ZenModeController controller) {
        mController = controller;
        mCountdownConditionSupported = mController.isCountdownConditionSupported();
        final int countdownDelta = mCountdownConditionSupported ? COUNTDOWN_CONDITION_COUNT : 0;
        final int minConditions = 1 /*forever*/ + countdownDelta;
        addZenConditions(minConditions);
        mSessionZen = getSelectedZen(-1);
        handleUpdateManualRule(mController.getManualRule());
        if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition);
        hideAllConditions();
    }

    private void setExitCondition(Condition exitCondition) {
        if (Objects.equals(mExitCondition, exitCondition)) return;
        mExitCondition = exitCondition;
        if (DEBUG) Log.d(mTag, "mExitCondition=" + getConditionId(mExitCondition));
        updateWidgets();
    }

    private static Uri getConditionId(Condition condition) {
        return condition != null ? condition.id : null;
    }

    private Uri getRealConditionId(Condition condition) {
        return isForever(condition) ? null : getConditionId(condition);
    }

    private static boolean sameConditionId(Condition lhs, Condition rhs) {
        return lhs == null ? rhs == null : rhs != null && lhs.id.equals(rhs.id);
    }

    private static Condition copy(Condition condition) {
        return condition == null ? null : condition.copy();
    }

    public void setCallback(Callback callback) {
        mCallback = callback;
    }

    private void handleUpdateManualRule(ZenRule rule) {
        final int zen = rule != null ? rule.zenMode : Global.ZEN_MODE_OFF;
        handleUpdateZen(zen);
        final Condition c = rule == null ? null
                : rule.condition != null ? rule.condition
                : createCondition(rule.conditionId);
        handleExitConditionChanged(c);
    }

    private Condition createCondition(Uri conditionId) {
        if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
            long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
            int mins = (int) ((time - System.currentTimeMillis() + DateUtils.MINUTE_IN_MILLIS / 2)
                    / DateUtils.MINUTE_IN_MILLIS);
            Condition c = ZenModeConfig.toTimeCondition(mContext, time, mins,
                    ActivityManager.getCurrentUser(), false);
            return c;
        }
        // If there is a manual rule, but it has no condition listed then it is forever.
        return forever();
    }

    private void handleUpdateZen(int zen) {
        if (mSessionZen != -1 && mSessionZen != zen) {
            mSessionZen = zen;
        }
        mZenButtons.setSelectedValue(zen, false /* fromClick */);
        updateWidgets();
        handleUpdateConditions();
        if (mExpanded) {
            final Condition selected = getSelectedCondition();
            if (!Objects.equals(mExitCondition, selected)) {
                select(selected);
            }
        }
    }

    private void handleExitConditionChanged(Condition exitCondition) {
        setExitCondition(exitCondition);
        if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitCondition);
        if (exitCondition == null) return;
        final int N = getVisibleConditions();
        for (int i = 0; i < N; i++) {
            final ConditionTag tag = getConditionTagAt(i);
            if (tag != null && sameConditionId(tag.condition, mExitCondition)) {
                bind(exitCondition, mZenRadioGroupContent.getChildAt(i), i);
                tag.rb.setChecked(true);
                return;
            }
        }
        if (mCountdownConditionSupported && ZenModeConfig.isValidCountdownConditionId(
                exitCondition.id)) {
            bind(exitCondition, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
                    COUNTDOWN_CONDITION_INDEX);
            getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
        }
    }

    private Condition getSelectedCondition() {
        final int N = getVisibleConditions();
        for (int i = 0; i < N; i++) {
            final ConditionTag tag = getConditionTagAt(i);
            if (tag != null && tag.rb.isChecked()) {
                return tag.condition;
            }
        }
        return null;
    }

    private int getSelectedZen(int defValue) {
        final Object zen = mZenButtons.getSelectedValue();
        return zen != null ? (Integer) zen : defValue;
    }

    private void updateWidgets() {
        if (mTransitionHelper.isTransitioning()) {
            mTransitionHelper.pendingUpdateWidgets();
            return;
        }
        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
        final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
        final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
        final boolean zenAlarm = zen == Global.ZEN_MODE_ALARMS;
        final boolean zenGame = zen == Global.ZEN_MODE_GAME;
        final boolean introduction = (zenImportant && !mPrefs.mConfirmedPriorityIntroduction
                || zenNone && !mPrefs.mConfirmedSilenceIntroduction
                || zenAlarm && !mPrefs.mConfirmedAlarmIntroduction
                || zenGame && !mPrefs.mConfirmedGameIntroduction);

        mZenButtons.setVisibility(mHidden ? GONE : VISIBLE);
        mZenIntroduction.setVisibility(introduction ? VISIBLE : GONE);
        if (introduction) {
            int message = zenImportant
                    ? R.string.zen_priority_introduction
                    : zenAlarm
                            ? R.string.zen_alarms_introduction
                            : zenGame
                            ? R.string.zen_game_introduction
                                    : mVoiceCapable
                                    ? R.string.zen_silence_introduction_voice
                                                : R.string.zen_silence_introduction;
            mConfigurableTexts.add(mZenIntroductionMessage, message);
            mConfigurableTexts.update();
            mZenIntroductionCustomize.setVisibility(zenImportant ? VISIBLE : GONE);
        }
        final String warning = computeAlarmWarningText(zenNone);
        mZenAlarmWarning.setVisibility(warning != null ? VISIBLE : GONE);
        mZenAlarmWarning.setText(warning);
    }

    private String computeAlarmWarningText(boolean zenNone) {
        if (!zenNone) {
            return null;
        }
        final long now = System.currentTimeMillis();
        final long nextAlarm = mController.getNextAlarm();
        if (nextAlarm < now) {
            return null;
        }
        int warningRes = 0;
        if (mSessionExitCondition == null || isForever(mSessionExitCondition)) {
            warningRes = R.string.zen_alarm_warning_indef;
        } else {
            final long time = ZenModeConfig.tryParseCountdownConditionId(mSessionExitCondition.id);
            if (time > now && nextAlarm < time) {
                warningRes = R.string.zen_alarm_warning;
            }
        }
        if (warningRes == 0) {
            return null;
        }
        final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000;
        final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser());
        final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma");
        final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
        final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm);
        final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far;
        final String template = getResources().getString(templateRes, formattedTime);
        return getResources().getString(warningRes, template);
    }

    private static Condition parseExistingTimeCondition(Context context, Condition condition) {
        if (condition == null) return null;
        final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id);
        if (time == 0) return null;
        final long now = System.currentTimeMillis();
        final long span = time - now;
        if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null;
        return ZenModeConfig.toTimeCondition(context,
                time, Math.round(span / (float) MINUTES_MS), ActivityManager.getCurrentUser(),
                false /*shortVersion*/);
    }

    private void handleUpdateConditions() {
        if (mTransitionHelper.isTransitioning()) {
            return;
        }
        final int conditionCount = mConditions == null ? 0 : mConditions.length;
        if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount);
        // forever
        bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX),
                FOREVER_CONDITION_INDEX);
        // countdown
        if (mCountdownConditionSupported && mTimeCondition != null) {
            bind(mTimeCondition, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
                    COUNTDOWN_CONDITION_INDEX);
        }
        // countdown until alarm
        if (mCountdownConditionSupported) {
            Condition nextAlarmCondition = getTimeUntilNextAlarmCondition();
            if (nextAlarmCondition != null) {
                mZenRadioGroup.getChildAt(
                        COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(View.VISIBLE);
                mZenRadioGroupContent.getChildAt(
                        COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(View.VISIBLE);
                bind(nextAlarmCondition,
                        mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX),
                        COUNTDOWN_ALARM_CONDITION_INDEX);
            } else {
                mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(View.GONE);
                mZenRadioGroupContent.getChildAt(
                        COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(View.GONE);
            }
        }
        // ensure something is selected
        if (mExpanded) {
            ensureSelection();
        }
        mZenConditions.setVisibility(mSessionZen != Global.ZEN_MODE_OFF ? View.VISIBLE : View.GONE);
    }

    private Condition forever() {
        return new Condition(mForeverId, foreverSummary(mContext), "", "", 0 /*icon*/,
                Condition.STATE_TRUE, 0 /*flags*/);
    }

    private static String foreverSummary(Context context) {
        return context.getString(com.android.internal.R.string.zen_mode_forever);
    }

    // Returns a time condition if the next alarm is within the next week.
    private Condition getTimeUntilNextAlarmCondition() {
        GregorianCalendar weekRange = new GregorianCalendar();
        final long now = weekRange.getTimeInMillis();
        setToMidnight(weekRange);
        weekRange.add(Calendar.DATE, 6);
        final long nextAlarmMs = mController.getNextAlarm();
        if (nextAlarmMs > 0) {
            GregorianCalendar nextAlarm = new GregorianCalendar();
            nextAlarm.setTimeInMillis(nextAlarmMs);
            setToMidnight(nextAlarm);

            if (weekRange.compareTo(nextAlarm) >= 0) {
                return ZenModeConfig.toTimeCondition(mContext, nextAlarmMs,
                        Math.round((nextAlarmMs - now) / (float) MINUTES_MS),
                        ActivityManager.getCurrentUser(), true);
            }
        }
        return null;
    }

    private void setToMidnight(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
    }

    private ConditionTag getConditionTagAt(int index) {
        return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag();
    }

    private int getVisibleConditions() {
        int rt = 0;
        final int N = mZenRadioGroupContent.getChildCount();
        for (int i = 0; i < N; i++) {
            rt += mZenRadioGroupContent.getChildAt(i).getVisibility() == VISIBLE ? 1 : 0;
        }
        return rt;
    }

    private void hideAllConditions() {
        final int N = mZenRadioGroupContent.getChildCount();
        for (int i = 0; i < N; i++) {
            mZenRadioGroupContent.getChildAt(i).setVisibility(GONE);
        }
    }

    private void ensureSelection() {
        // are we left without anything selected?  if so, set a default
        final int visibleConditions = getVisibleConditions();
        if (visibleConditions == 0) return;
        for (int i = 0; i < visibleConditions; i++) {
            final ConditionTag tag = getConditionTagAt(i);
            if (tag != null && tag.rb.isChecked()) {
                if (DEBUG) Log.d(mTag, "Not selecting a default, checked=" + tag.condition);
                return;
            }
        }
        final ConditionTag foreverTag = getConditionTagAt(FOREVER_CONDITION_INDEX);
        if (foreverTag == null) return;
        if (DEBUG) Log.d(mTag, "Selecting a default");
        final int favoriteIndex = mPrefs.getMinuteIndex();
        if (mExitCondition != null && mExitCondition.equals(mTimeCondition)) {
            getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
        } else if (favoriteIndex == -1 || !mCountdownConditionSupported ||
                mAttachedZen != Global.ZEN_MODE_OFF) {
            foreverTag.rb.setChecked(true);
        } else {
            mTimeCondition = ZenModeConfig.toTimeCondition(mContext,
                    MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser());
            mBucketIndex = favoriteIndex;
            bind(mTimeCondition, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
                    COUNTDOWN_CONDITION_INDEX);
            getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
        }
    }

    private static boolean isCountdown(Condition c) {
        return c != null && ZenModeConfig.isValidCountdownConditionId(c.id);
    }

    private boolean isForever(Condition c) {
        return c != null && mForeverId.equals(c.id);
    }

    private void bind(final Condition condition, final View row, final int rowId) {
        if (condition == null) throw new IllegalArgumentException("condition must not be null");
        final boolean enabled = condition.state == Condition.STATE_TRUE;
        final ConditionTag tag =
                row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag();
        row.setTag(tag);
        final boolean first = tag.rb == null;
        if (tag.rb == null) {
            tag.rb = (RadioButton) mZenRadioGroup.getChildAt(rowId);
        }
        tag.condition = condition;
        final Uri conditionId = getConditionId(tag.condition);
        if (DEBUG) Log.d(mTag, "bind i=" + mZenRadioGroupContent.indexOfChild(row) + " first="
                + first + " condition=" + conditionId);
        tag.rb.setEnabled(enabled);
        tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (mExpanded && isChecked) {
                    tag.rb.setChecked(true);
                    if (DEBUG) Log.d(mTag, "onCheckedChanged " + conditionId);
                    MetricsLogger.action(mContext, MetricsEvent.QS_DND_CONDITION_SELECT);
                    select(tag.condition);
                    announceConditionSelection(tag);
                }
            }
        });

        if (tag.lines == null) {
            tag.lines = row.findViewById(android.R.id.content);
        }
        if (tag.line1 == null) {
            tag.line1 = (TextView) row.findViewById(android.R.id.text1);
            mConfigurableTexts.add(tag.line1);
        }
        if (tag.line2 == null) {
            tag.line2 = (TextView) row.findViewById(android.R.id.text2);
            mConfigurableTexts.add(tag.line2);
        }
        final String line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1
                : condition.summary;
        final String line2 = condition.line2;
        tag.line1.setText(line1);
        if (TextUtils.isEmpty(line2)) {
            tag.line2.setVisibility(GONE);
        } else {
            tag.line2.setVisibility(VISIBLE);
            tag.line2.setText(line2);
        }
        tag.lines.setEnabled(enabled);
        tag.lines.setAlpha(enabled ? 1 : .4f);

        final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
        button1.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                onClickTimeButton(row, tag, false /*down*/, rowId);
            }
        });

        final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
        button2.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                onClickTimeButton(row, tag, true /*up*/, rowId);
            }
        });
        tag.lines.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                tag.rb.setChecked(true);
            }
        });

        final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
        if (rowId != COUNTDOWN_ALARM_CONDITION_INDEX && time > 0) {
            button1.setVisibility(VISIBLE);
            button2.setVisibility(VISIBLE);
            if (mBucketIndex > -1) {
                button1.setEnabled(mBucketIndex > 0);
                button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1);
            } else {
                final long span = time - System.currentTimeMillis();
                button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS);
                final Condition maxCondition = ZenModeConfig.toTimeCondition(mContext,
                        MAX_BUCKET_MINUTES, ActivityManager.getCurrentUser());
                button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary));
            }

            button1.setAlpha(button1.isEnabled() ? 1f : .5f);
            button2.setAlpha(button2.isEnabled() ? 1f : .5f);
        } else {
            button1.setVisibility(GONE);
            button2.setVisibility(GONE);
        }
        // wire up interaction callbacks for newly-added condition rows
        if (first) {
            Interaction.register(tag.rb, mInteractionCallback);
            Interaction.register(tag.lines, mInteractionCallback);
            Interaction.register(button1, mInteractionCallback);
            Interaction.register(button2, mInteractionCallback);
        }
        row.setVisibility(VISIBLE);
    }

    private void announceConditionSelection(ConditionTag tag) {
        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
        String modeText;
        switch(zen) {
            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                modeText = mContext.getString(R.string.interruption_level_priority);
                break;
            case Global.ZEN_MODE_NO_INTERRUPTIONS:
                modeText = mContext.getString(R.string.interruption_level_none);
                break;
            case Global.ZEN_MODE_ALARMS:
                modeText = mContext.getString(R.string.interruption_level_alarms);
                break;
            case Global.ZEN_MODE_GAME:
                modeText = mContext.getString(R.string.interruption_level_game);
                break;
            default:
                return;
        }
        announceForAccessibility(mContext.getString(R.string.zen_mode_and_condition, modeText,
                tag.line1.getText()));
    }

    private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
        MetricsLogger.action(mContext, MetricsEvent.QS_DND_TIME, up);
        Condition newCondition = null;
        final int N = MINUTE_BUCKETS.length;
        if (mBucketIndex == -1) {
            // not on a known index, search for the next or prev bucket by time
            final Uri conditionId = getConditionId(tag.condition);
            final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
            final long now = System.currentTimeMillis();
            for (int i = 0; i < N; i++) {
                int j = up ? i : N - 1 - i;
                final int bucketMinutes = MINUTE_BUCKETS[j];
                final long bucketTime = now + bucketMinutes * MINUTES_MS;
                if (up && bucketTime > time || !up && bucketTime < time) {
                    mBucketIndex = j;
                    newCondition = ZenModeConfig.toTimeCondition(mContext,
                            bucketTime, bucketMinutes, ActivityManager.getCurrentUser(),
                            false /*shortVersion*/);
                    break;
                }
            }
            if (newCondition == null) {
                mBucketIndex = DEFAULT_BUCKET_INDEX;
                newCondition = ZenModeConfig.toTimeCondition(mContext,
                        MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
            }
        } else {
            // on a known index, simply increment or decrement
            mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
            newCondition = ZenModeConfig.toTimeCondition(mContext,
                    MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
        }
        mTimeCondition = newCondition;
        bind(mTimeCondition, row, rowId);
        tag.rb.setChecked(true);
        select(mTimeCondition);
        announceConditionSelection(tag);
    }

    private void select(final Condition condition) {
        if (DEBUG) Log.d(mTag, "select " + condition);
        if (mSessionZen == -1 || mSessionZen == Global.ZEN_MODE_OFF) {
            if (DEBUG) Log.d(mTag, "Ignoring condition selection outside of manual zen");
            return;
        }
        final Uri realConditionId = getRealConditionId(condition);
        if (mController != null) {
            AsyncTask.execute(new Runnable() {
                @Override
                public void run() {
                    mController.setZen(mSessionZen, realConditionId, TAG + ".selectCondition");
                }
            });
        }
        setExitCondition(condition);
        if (realConditionId == null) {
            mPrefs.setMinuteIndex(-1);
        } else if (isCountdown(condition) && mBucketIndex != -1) {
            mPrefs.setMinuteIndex(mBucketIndex);
        }
        setSessionExitCondition(copy(condition));
    }

    private void fireInteraction() {
        if (mCallback != null) {
            mCallback.onInteraction();
        }
    }

    private void fireExpanded() {
        if (mCallback != null) {
            mCallback.onExpanded(mExpanded);
        }
    }

    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
        @Override
        public void onManualRuleChanged(ZenRule rule) {
            mHandler.obtainMessage(H.MANUAL_RULE_CHANGED, rule).sendToTarget();
        }
    };

    private final class H extends Handler {
        private static final int MANUAL_RULE_CHANGED = 2;
        private static final int UPDATE_WIDGETS = 3;

        private H() {
            super(Looper.getMainLooper());
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MANUAL_RULE_CHANGED: handleUpdateManualRule((ZenRule) msg.obj); break;
                case UPDATE_WIDGETS: updateWidgets(); break;
            }
        }
    }

    public interface Callback {
        void onPrioritySettings();
        void onInteraction();
        void onExpanded(boolean expanded);
    }

    // used as the view tag on condition rows
    private static class ConditionTag {
        RadioButton rb;
        View lines;
        TextView line1;
        TextView line2;
        Condition condition;
    }

    private final class ZenPrefs implements OnSharedPreferenceChangeListener {
        private final int mNoneDangerousThreshold;

        private int mMinuteIndex;
        private int mNoneSelected;
        private boolean mConfirmedPriorityIntroduction;
        private boolean mConfirmedSilenceIntroduction;
        private boolean mConfirmedAlarmIntroduction;
        private boolean mConfirmedGameIntroduction;

        private ZenPrefs() {
            mNoneDangerousThreshold = mContext.getResources()
                    .getInteger(R.integer.zen_mode_alarm_warning_threshold);
            Prefs.registerListener(mContext, this);
            updateMinuteIndex();
            updateNoneSelected();
            updateConfirmedPriorityIntroduction();
            updateConfirmedSilenceIntroduction();
            updateConfirmedAlarmIntroduction();
            updateConfirmedGameIntroduction();
        }

        public void trackNoneSelected() {
            mNoneSelected = clampNoneSelected(mNoneSelected + 1);
            if (DEBUG) Log.d(mTag, "Setting none selected: " + mNoneSelected + " threshold="
                    + mNoneDangerousThreshold);
            Prefs.putInt(mContext, Prefs.Key.DND_NONE_SELECTED, mNoneSelected);
        }

        public int getMinuteIndex() {
            return mMinuteIndex;
        }

        public void setMinuteIndex(int minuteIndex) {
            minuteIndex = clampIndex(minuteIndex);
            if (minuteIndex == mMinuteIndex) return;
            mMinuteIndex = clampIndex(minuteIndex);
            if (DEBUG) Log.d(mTag, "Setting favorite minute index: " + mMinuteIndex);
            Prefs.putInt(mContext, Prefs.Key.DND_FAVORITE_BUCKET_INDEX, mMinuteIndex);
        }

        @Override
        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
            updateMinuteIndex();
            updateNoneSelected();
            updateConfirmedPriorityIntroduction();
            updateConfirmedSilenceIntroduction();
            updateConfirmedAlarmIntroduction();
            updateConfirmedGameIntroduction();
        }

        private void updateMinuteIndex() {
            mMinuteIndex = clampIndex(Prefs.getInt(mContext,
                    Prefs.Key.DND_FAVORITE_BUCKET_INDEX, DEFAULT_BUCKET_INDEX));
            if (DEBUG) Log.d(mTag, "Favorite minute index: " + mMinuteIndex);
        }

        private int clampIndex(int index) {
            return MathUtils.constrain(index, -1, MINUTE_BUCKETS.length - 1);
        }

        private void updateNoneSelected() {
            mNoneSelected = clampNoneSelected(Prefs.getInt(mContext,
                    Prefs.Key.DND_NONE_SELECTED, 0));
            if (DEBUG) Log.d(mTag, "None selected: " + mNoneSelected);
        }

        private int clampNoneSelected(int noneSelected) {
            return MathUtils.constrain(noneSelected, 0, Integer.MAX_VALUE);
        }

        private void updateConfirmedPriorityIntroduction() {
            final boolean confirmed =  Prefs.getBoolean(mContext,
                    Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION, false);
            if (confirmed == mConfirmedPriorityIntroduction) return;
            mConfirmedPriorityIntroduction = confirmed;
            if (DEBUG) Log.d(mTag, "Confirmed priority introduction: "
                    + mConfirmedPriorityIntroduction);
        }

        private void updateConfirmedSilenceIntroduction() {
            final boolean confirmed =  Prefs.getBoolean(mContext,
                    Prefs.Key.DND_CONFIRMED_SILENCE_INTRODUCTION, false);
            if (confirmed == mConfirmedSilenceIntroduction) return;
            mConfirmedSilenceIntroduction = confirmed;
            if (DEBUG) Log.d(mTag, "Confirmed silence introduction: "
                    + mConfirmedSilenceIntroduction);
        }

        private void updateConfirmedAlarmIntroduction() {
            final boolean confirmed =  Prefs.getBoolean(mContext,
                    Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION, false);
            if (confirmed == mConfirmedAlarmIntroduction) return;
            mConfirmedAlarmIntroduction = confirmed;
            if (DEBUG) Log.d(mTag, "Confirmed alarm introduction: "
                    + mConfirmedAlarmIntroduction);
        }

        private void updateConfirmedGameIntroduction() {
            final boolean confirmed =  Prefs.getBoolean(mContext,
                    Prefs.Key.DND_CONFIRMED_GAME_INTRODUCTION, false);
            if (confirmed == mConfirmedGameIntroduction) return;
            mConfirmedGameIntroduction = confirmed;
            if (DEBUG) Log.d(mTag, "Confirmed game introduction: "
                    + mConfirmedGameIntroduction);
        }
    }

    protected final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
        @Override
        public void onSelected(final Object value, boolean fromClick) {
            if (value != null && mZenButtons.isShown() && isAttachedToWindow()) {
                final int zen = (Integer) value;
                if (fromClick) {
                    MetricsLogger.action(mContext, MetricsEvent.QS_DND_ZEN_SELECT, zen);
                }
                if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + zen);
                final Uri realConditionId = getRealConditionId(mSessionExitCondition);
                AsyncTask.execute(new Runnable() {
                    @Override
                    public void run() {
                        mController.setZen(zen, realConditionId, TAG + ".selectZen");
                        if (zen != Global.ZEN_MODE_OFF) {
                            Prefs.putInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, zen);
                        }
                    }
                });
            }
        }

        @Override
        public void onInteraction() {
            fireInteraction();
        }
    };

    private final Interaction.Callback mInteractionCallback = new Interaction.Callback() {
        @Override
        public void onInteraction() {
            fireInteraction();
        }
    };

    private final class TransitionHelper implements TransitionListener, Runnable {
        private final ArraySet<View> mTransitioningViews = new ArraySet<View>();

        private boolean mTransitioning;
        private boolean mPendingUpdateWidgets;

        public void clear() {
            mTransitioningViews.clear();
            mPendingUpdateWidgets = false;
        }

        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            pw.println("  TransitionHelper state:");
            pw.print("    mPendingUpdateWidgets="); pw.println(mPendingUpdateWidgets);
            pw.print("    mTransitioning="); pw.println(mTransitioning);
            pw.print("    mTransitioningViews="); pw.println(mTransitioningViews);
        }

        public void pendingUpdateWidgets() {
            mPendingUpdateWidgets = true;
        }

        public boolean isTransitioning() {
            return !mTransitioningViews.isEmpty();
        }

        @Override
        public void startTransition(LayoutTransition transition,
                ViewGroup container, View view, int transitionType) {
            mTransitioningViews.add(view);
            updateTransitioning();
        }

        @Override
        public void endTransition(LayoutTransition transition,
                ViewGroup container, View view, int transitionType) {
            mTransitioningViews.remove(view);
            updateTransitioning();
        }

        @Override
        public void run() {
            if (DEBUG) Log.d(mTag, "TransitionHelper run"
                    + " mPendingUpdateWidgets=" + mPendingUpdateWidgets);
            if (mPendingUpdateWidgets) {
                updateWidgets();
            }
            mPendingUpdateWidgets = false;
        }

        private void updateTransitioning() {
            final boolean transitioning = isTransitioning();
            if (mTransitioning == transitioning) return;
            mTransitioning = transitioning;
            if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning);
            if (!mTransitioning) {
                if (mPendingUpdateWidgets) {
                    mHandler.post(this);
                } else {
                    mPendingUpdateWidgets = false;
                }
            }
        }
    }
}

2.实现新加的模式
dnd实现的地方在ZenModeHelper.java,这里会根据不同的模式来做不同的处理,还会监听audiomanager模式的改变,就是按音量键打开dnd。

在打开某个模式的时候,首先会看是否要设置applyZenToRingerMode,然后在去applyRestrictions。我们这里加的模式不会去设置RINGER_MODE_SILENT,而是直接applyRestrictions去设置。

frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java

    private boolean evaluateZenMode(String reason, boolean setRingerMode) {
        if (DEBUG) Log.d(TAG, "evaluateZenMode");
        final int zenBefore = mZenMode;
        final int zen = computeZenMode();
        ZenLog.traceSetZenMode(zen, reason);
        mZenMode = zen;
        updateRingerModeAffectedStreams();
        setZenModeSetting(mZenMode);
        if (setRingerMode) {
            applyZenToRingerMode();
        }
        applyRestrictions();
        if (zen != zenBefore) {
            mHandler.postDispatchOnZenModeChanged();
        }
        return true;
    }
    
    private void applyZenToRingerMode() {
        if (mAudioManager == null) return;
        // force the ringer mode into compliance
        final int ringerModeInternal = mAudioManager.getRingerModeInternal();
        int newRingerModeInternal = ringerModeInternal;
        switch (mZenMode) {
            case Global.ZEN_MODE_NO_INTERRUPTIONS:
            case Global.ZEN_MODE_ALARMS:
            //case Global.ZEN_MODE_GAME:
                if (ringerModeInternal != AudioManager.RINGER_MODE_SILENT) {
                    setPreviousRingerModeSetting(ringerModeInternal);
                    newRingerModeInternal = AudioManager.RINGER_MODE_SILENT;
                }
                break;
            case Global.ZEN_MODE_GAME:
            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
            case Global.ZEN_MODE_OFF:
                if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) {
                    newRingerModeInternal = getPreviousRingerModeSetting();
                    setPreviousRingerModeSetting(null);
                }
                break;
        }
        if (newRingerModeInternal != -1) {
            mAudioManager.setRingerModeInternal(newRingerModeInternal, TAG);
        }
    }

根据不同模式,设置哪些声音有效,哪些无效。

    private void applyRestrictions() {
        final boolean zen = mZenMode != Global.ZEN_MODE_OFF;

        // notification restrictions
        final boolean muteNotifications =
                (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
        // call restrictions
        final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
                || (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
        // total silence restrictions
        final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;

        final boolean mGameMode = mZenMode == Global.ZEN_MODE_GAME;

        for (int usage : AudioAttributes.SDK_USAGES) {
            final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
            if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NEVER) {
                applyRestrictions(false /*mute*/, usage);
            } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NOTIFICATION) {
                applyRestrictions(muteNotifications || muteEverything||mGameMode, usage);
            } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) {
                applyRestrictions(muteCalls || muteEverything||mGameMode, usage);
            } else if(usage==AudioAttributes.SDK_USAGES[4]) {
                //Alarm
                applyRestrictions(mGameMode, usage);
            }
            else {
                applyRestrictions(muteEverything, usage);
            }
        }
    }
    private static int zenSeverity(int zen) {
        switch (zen) {
            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return 1;
            case Global.ZEN_MODE_ALARMS: return 2;
            case Global.ZEN_MODE_NO_INTERRUPTIONS: return 3;
            case Global.ZEN_MODE_GAME: return 4;
            default: return 0;
        }
    }

注意RingerModeDelegate这个内部类,当执行applyZenToRingerMode改变了ringermode,就会回调这个内部类里的方法,按音量键也会走这里,所以这里也要加入对我们新加模式的一些处理,否则会发生逻辑错误,导致QS那点击的按钮切换错误。

    private final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate {
        @Override
        public String toString() {
            return TAG;
        }

        @Override
        public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, String caller,
                int ringerModeExternal, VolumePolicy policy) {
            final boolean isChange = ringerModeOld != ringerModeNew;

            int ringerModeExternalOut = ringerModeNew;

            int newZen = -1;
            switch (ringerModeNew) {
                case AudioManager.RINGER_MODE_SILENT:
                    if (isChange && policy.doNotDisturbWhenSilent) {
                        if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS
                                && mZenMode != Global.ZEN_MODE_ALARMS
                                && mZenMode != Global.ZEN_MODE_GAME) {
                            newZen = Global.ZEN_MODE_ALARMS;
                        }
                        setPreviousRingerModeSetting(ringerModeOld);
                    }
                    break;
                case AudioManager.RINGER_MODE_VIBRATE:
                case AudioManager.RINGER_MODE_NORMAL:
                    if (isChange && ringerModeOld == AudioManager.RINGER_MODE_SILENT
                            && (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
                                    || mZenMode == Global.ZEN_MODE_ALARMS
                                    /*|| mZenMode == Global.ZEN_MODE_GAME*/)) {
                        newZen = Global.ZEN_MODE_OFF;
                    } else if (mZenMode != Global.ZEN_MODE_OFF) {
                        ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
                    }
                    break;
            }
            if (newZen != -1) {
                setManualZenMode(newZen, null, "ringerModeInternal", null,
                        false /*setRingerMode*/);
            }

            if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
                ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller,
                        ringerModeExternal, ringerModeExternalOut);
            }
            return ringerModeExternalOut;
        }

        @Override
        public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
                int ringerModeInternal, VolumePolicy policy) {
            int ringerModeInternalOut = ringerModeNew;
            final boolean isChange = ringerModeOld != ringerModeNew;
            final boolean isVibrate = ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
            int newZen = -1;
            switch (ringerModeNew) {
                case AudioManager.RINGER_MODE_SILENT:
                    if (isChange) {
                        if (mZenMode == Global.ZEN_MODE_OFF) {
                            newZen = Global.ZEN_MODE_ALARMS;
                        }
                        ringerModeInternalOut = isVibrate ? AudioManager.RINGER_MODE_VIBRATE
                                : AudioManager.RINGER_MODE_SILENT;
                    } else {
                        ringerModeInternalOut = ringerModeInternal;
                    }
                    break;
                case AudioManager.RINGER_MODE_VIBRATE:
                case AudioManager.RINGER_MODE_NORMAL:
                    if (mZenMode != Global.ZEN_MODE_OFF) {
                        newZen = Global.ZEN_MODE_OFF;
                    }
                    break;
            }
            if (newZen != -1) {
                setManualZenMode(newZen, null, "ringerModeExternal", caller,
                        false /*setRingerMode*/);
            }

            ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller,
                    ringerModeInternal, ringerModeInternalOut);
            return ringerModeInternalOut;
        }

        @Override
        public boolean canVolumeDownEnterSilent() {
            return mZenMode == Global.ZEN_MODE_OFF;
        }

        @Override
        public int getRingerModeAffectedStreams(int streams) {
            // ringtone, notification and system streams are always affected by ringer mode
            streams |= (1 << AudioSystem.STREAM_RING) |
                       (1 << AudioSystem.STREAM_NOTIFICATION) |
                       (1 << AudioSystem.STREAM_SYSTEM);

            // alarm and music streams are only affected by ringer mode when in total silence
            if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
                streams |= (1 << AudioSystem.STREAM_ALARM) |
                           (1 << AudioSystem.STREAM_MUSIC);
            } else {
                streams &= ~((1 << AudioSystem.STREAM_ALARM) |
                             (1 << AudioSystem.STREAM_MUSIC));
            }
            return streams;
        }
    }

3.音量界面的处理
当你进去某个dnd模式下,在settigns中的sound里面你可以发现,有些音量会滑动条会被disable掉。所以我们在这里也要做些处理。

这里写图片描述

这个界面在
packages/apps/Settings/src/com/android/settings/notification/SoundSettings.java,但是对应的音量滑动条在frameworks/base/core/java/android/preferences/SeekBarVolumizer.java,每个音量条都会对应new一个SeekBarVolumizer,然后SeekBarVolumizer会根据对应音量的stream和zenmode来判断是否要disable掉。

SeekBarVolumizer.java

    private boolean isZenMuted() {
        return mNotificationOrRing && mZenMode == Global.ZEN_MODE_ALARMS
                || mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
                || (mZenMode == Global.ZEN_MODE_GAME && mStreamType == AudioManager.STREAM_ALARM);
    }

    protected void updateSeekBar() {
        final boolean zenMuted = isZenMuted();
        mSeekBar.setEnabled(!zenMuted);
        if (zenMuted) {
            mSeekBar.setProgress(mLastAudibleStreamVolume, true);
        } else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
            mSeekBar.setProgress(0, true);
        } else if (mMuted) {
            mSeekBar.setProgress(0, true);
        } else {
            mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume, true);
        }
    }

除了settings中有音量,还有个地方也有,就是当你按下音量键时候,从屏幕上方弹下来的界面。

在这里插入图片描述
frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java

    private void updateVolumeRowH(VolumeRow row) {
        if (D.BUG) Log.d(TAG, "updateVolumeRowH s=" + row.stream);
        if (mState == null) return;
        final StreamState ss = mState.states.get(row.stream);
        if (ss == null) return;
        row.ss = ss;
        if (ss.level > 0) {
            row.lastAudibleLevel = ss.level;
        }
        if (ss.level == row.requestedLevel) {
            row.requestedLevel = -1;
        }
        final boolean isA11yStream = row.stream == AudioManager.STREAM_ACCESSIBILITY;
        final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
        final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
        final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
        final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC;
        final boolean isRingVibrate = isRingStream
                && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
        final boolean isRingSilent = isRingStream
                && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT;
        final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS;
        final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
        final boolean isZenGame = mState.zenMode == Global.ZEN_MODE_GAME;
        final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream)
                : isZenNone ? (isRingStream || isSystemStream || isAlarmStream || isMusicStream)
                : isZenGame ? (isAlarmStream)
                : false;
       ......
 }

isZenAlarms ? (isRingStream || isSystemStream) 如果是alarm模式,就disable掉ringstream和SystemStream。

注意:ZenModeFiltering这个类。
matchesCallFilter和shouldIntercept都要加我们的代码,模仿其他模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值