android framework13-settings【05 Dream和dark mode】

568 阅读7分钟

1.Dream

就是常说的屏保,根据图片简单说明下

  • 第一个是切换按钮是屏保开关
  • 下边5个是屏保程序
  • when to start 字面意思,屏保启动的条件 image.png

1.1.display_settings

来源 display_settings.xml

        <Preference
            android:key="screensaver"
            android:title="@string/screensaver_settings_title"
            android:fragment="com.android.settings.dream.DreamSettings"
            settings:keywords="@string/keywords_screensaver"
            settings:controller="com.android.settings.display.ScreenSaverPreferenceController"/>

2.DreamSettings.java

屏保程序相关

2.1.dream_fragment_overview.xml

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:key="screensaver_settings_screen"
    android:title="@string/screensaver_settings_title"
    settings:keywords="@string/keywords_screensaver">
    <!--屏保开关-->
    <com.android.settingslib.widget.MainSwitchPreference
        android:key="dream_main_settings_switch"
        android:title="@string/screensaver_settings_toggle_title"
        settings:controller="com.android.settings.dream.DreamMainSwitchPreferenceController"
        settings:searchable="false"/>

    <!--设置屏保触发的条件,点击进入小节3-->
    <Preference
        android:key="when_to_start"
        android:title="@string/screensaver_settings_when_to_dream"
        android:fragment="com.android.settings.dream.WhenToDreamPicker"/>

    <PreferenceCategory
        android:title="@string/dream_picker_category">
        <!--屏保程序选择列表-->
        <com.android.settingslib.widget.LayoutPreference
            android:key="dream_picker"
            android:selectable="false"
            android:layout="@layout/dream_picker_layout"
            settings:controller="com.android.settings.dream.DreamPickerController"/>
    </PreferenceCategory>
    <!--好像没用到-->
    <SwitchPreference
        android:key="dream_complications_toggle"
        android:title="@string/dream_complications_toggle_title"
        android:summary="@string/dream_complications_toggle_summary"
        settings:controller="com.android.settings.dream.DreamComplicationPreferenceController"/>

    <com.android.settings.applications.SpacePreference
        android:layout_height="16dp" />

</PreferenceScreen>

>dream_picker_layout.xml

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/dream_list"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:clipToPadding="true"
        android:nestedScrollingEnabled="false"
        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
        android:paddingBottom="@dimen/dream_preference_card_padding"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

2.2.onCreate

    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        final DreamBackend dreamBackend = DreamBackend.getInstance(getContext());

        mMainSwitchPreference = findPreference(MAIN_SWITCH_PREF_KEY);
        if (mMainSwitchPreference != null) {
            mMainSwitchPreference.addOnSwitchChangeListener(this);
        }
        //如果屏保开关关闭的话,其他的选项也不能操作
        setAllPreferencesEnabled(dreamBackend.isEnabled());
    }

>onSwitchChanged

    public void onSwitchChanged(Switch switchView, boolean isChecked) {
        //同步修改其他选项的是否可用
        setAllPreferencesEnabled(isChecked);
        //预览按钮是否可见
        mPreviewButton.setVisibility(isChecked ? View.VISIBLE : View.GONE);
        
        updatePaddingForPreviewButton();
    }

>updatePaddingForPreviewButton

和底部有个间隔

    private void updatePaddingForPreviewButton() {
        mPreviewButton.post(() -> {
            mRecyclerView.setPadding(0, 0, 0, mPreviewButton.getMeasuredHeight());
        });
    }

2.3.getSummaryTextWithDreamName

获取summary,给外部的选项用

    public static CharSequence getSummaryTextWithDreamName(Context context) {
        DreamBackend backend = DreamBackend.getInstance(context);
        return getSummaryTextFromBackend(backend, context);
    }

    static CharSequence getSummaryTextFromBackend(DreamBackend backend, Context context) {
        if (backend.isEnabled()) {
            return context.getString(R.string.screensaver_settings_summary_on,// On/%s
                    backend.getActiveDreamName());// 见 4.3
        } else {
            return context.getString(R.string.screensaver_settings_summary_off);//Off
        }
    }

2.4.onCreateRecyclerView

    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
            Bundle bundle) {
        final DreamBackend dreamBackend = DreamBackend.getInstance(getContext());

        final ViewGroup root = getActivity().findViewById(android.R.id.content);
        //预览按钮是在这里手动添加的
        mPreviewButton = (Button) getActivity().getLayoutInflater().inflate(
                R.layout.dream_preview_button, root, false);
        mPreviewButton.setVisibility(dreamBackend.isEnabled() ? View.VISIBLE : View.GONE);
        root.addView(mPreviewButton);
        mPreviewButton.setOnClickListener(v -> dreamBackend.preview(dreamBackend.getActiveDream()));

        mRecyclerView = super.onCreateRecyclerView(inflater, parent, bundle);
        updatePaddingForPreviewButton();
        return mRecyclerView;
    }

2.5.key转换

DreamBackend用的key是int,这里选项用的key是string,所以需要转化下

    static int getSettingFromPrefKey(String key) {
        switch (key) {
            case WHILE_CHARGING_ONLY:
                return WHILE_CHARGING;
            case WHILE_DOCKED_ONLY:
                return WHILE_DOCKED;
            case EITHER_CHARGING_OR_DOCKED:
                return EITHER;
            case NEVER_DREAM:
            default:
                return NEVER;
        }
    }

    static String getKeyFromSetting(@WhenToDream int dreamSetting) {
        switch (dreamSetting) {
            case WHILE_CHARGING:
                return WHILE_CHARGING_ONLY;
            case WHILE_DOCKED:
                return WHILE_DOCKED_ONLY;
            case EITHER:
                return EITHER_CHARGING_OR_DOCKED;
            case NEVER:
            default:
                return NEVER_DREAM;
        }
    }

2.5.DreamPickerController

>getAvailabilityStatus

    public DreamPickerController(Context context, String key, DreamBackend backend) {
        super(context, key);
        mBackend = backend;
        //可用的屏保集合
        mDreamInfos = mBackend.getDreamInfos();
        //选中的屏保
        mActiveDream = getActiveDreamInfo(mDreamInfos);
    }

    @Override
    public int getAvailabilityStatus() {
        //可用屏保的个数大于0
        return mDreamInfos.size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
    }

>displayPreference

设置adapter

    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);

        mAdapter = new DreamAdapter(R.layout.dream_preference_layout,
                mDreamInfos.stream()
                        .map(DreamItem::new)
                        .collect(Collectors.toList()));

        mAdapter.setEnabled(mBackend.isEnabled());

        final LayoutPreference pref = screen.findPreference(getPreferenceKey());
        if (pref == null) {
            return;
        }
        final RecyclerView recyclerView = pref.findViewById(R.id.dream_list);
        recyclerView.setLayoutManager(new AutoFitGridLayoutManager(mContext));
        recyclerView.addItemDecoration(
                new GridSpacingItemDecoration(mContext, R.dimen.dream_preference_card_padding));
        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(mAdapter);
    }

>onItemClicked

    private class DreamItem implements IDreamItem {
    
        public void onItemClicked() {
            mActiveDream = mDreamInfo;
            mBackend.setActiveDream(mDreamInfo.componentName);
        }

2.6.DreamMainSwitchPreferenceController.java

屏保开关的控制,最终交给DreamBackend处理

public class DreamMainSwitchPreferenceController extends
        SettingsMainSwitchPreferenceController implements LifecycleObserver {
    static final String MAIN_SWITCH_PREF_KEY = "dream_main_settings_switch";

    public DreamMainSwitchPreferenceController(Context context, String key) {
        super(context, key);
        mBackend = DreamBackend.getInstance(context);
    }

>isChecked

见 4.1

    public boolean isChecked() {
        return mBackend.isEnabled();
    }
    public boolean setChecked(boolean isChecked) {
        mBackend.setEnabled(isChecked);
        return true;
    }    

>onStart

    void onStart() {
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.SCREENSAVER_ENABLED),
                /* notifyForDescendants= */ false, mObserver);
    }

3.WhenToDreamPicker.java

public class WhenToDreamPicker extends RadioButtonPickerFragment {

image.png

3.1.onAttach

    public void onAttach(Context context) {
        super.onAttach(context);

        mBackend = DreamBackend.getInstance(context);
        //非充电状态下是否支持屏保
        mDreamsSupportedOnBattery = getResources().getBoolean(
                com.android.internal.R.bool.config_dreamsEnabledOnBattery);
    }

    @Override
    protected int getPreferenceScreenResId() {
        return R.xml.when_to_dream_settings;
    }

3.2.getCandidates

  • mDreamsSupportedOnBattery 这个值影响选项的个数
    protected List<? extends CandidateInfo> getCandidates() {
        final String[] entries = entries();
        final String[] values = keys();
        final List<WhenToDreamCandidateInfo> candidates = new ArrayList<>();

        if (entries == null || entries.length <= 0) return null;
        if (values == null || values.length != entries.length) {
            throw new IllegalArgumentException("Entries and values must be of the same length.");
        }

        for (int i = 0; i < entries.length; i++) {
            candidates.add(new WhenToDreamCandidateInfo(entries[i], values[i]));
        }

        return candidates;
    }

    private String[] entries() {
        if (mDreamsSupportedOnBattery) {
            return getResources().getStringArray(R.array.when_to_start_screensaver_entries);
        }
        return getResources().getStringArray(R.array.when_to_start_screensaver_entries_no_battery);
    }

    private String[] keys() {
        if (mDreamsSupportedOnBattery) {
            return getResources().getStringArray(R.array.when_to_start_screensaver_values);
        }
        return getResources().getStringArray(R.array.when_to_start_screensaver_values_no_battery);
    }

>keys

    <string-array name="when_to_start_screensaver_values" translatable="false">
        <item>while_charging_only</item>
        <item>while_docked_only</item>
        <item>either_charging_or_docked</item>
    </string-array>
    
    <!-- Values for screensaver "When to start" for devices that do not support screensavers
         while on battery -->
    <string-array name="when_to_start_screensaver_values_no_battery" translatable="false">
        <item>while_charging_only</item>
        <item>while_docked_only</item>
    </string-array>    

>entries

    <string-array name="when_to_start_screensaver_entries" translatable="false">
        <item>@string/screensaver_settings_summary_sleep</item>
        <item>@string/screensaver_settings_summary_dock</item>
        <item>@string/screensaver_settings_summary_either_long</item>
    </string-array>

    <!-- Options for screensaver "When to start" for devices that do not support screensavers
         while on battery -->
    <string-array name="when_to_start_screensaver_entries_no_battery" translatable="false">
        <item>@string/screensaver_settings_summary_sleep</item>
        <item>@string/screensaver_settings_summary_dock_and_charging</item>
    </string-array>
    
    <string name="screensaver_settings_summary_either_long" msgid="371949139331896271">"While charging or docked"</string>
    <string name="screensaver_settings_summary_sleep" msgid="6555922932643037432">"While charging"</string>
    <string name="screensaver_settings_summary_dock" msgid="6997766385189369733">"While docked"</string>    

3.3.父类的实现类

    //获取已保存的key值
    protected String getDefaultKey() {
        return DreamSettings.getKeyFromSetting(mBackend.getWhenToDreamSetting());
    }

    //保存选中的key值
    protected boolean setDefaultKey(String key) {
        mBackend.setWhenToDream(DreamSettings.getSettingFromPrefKey(key));
        return true;
    }

    //选项点击以后的最终操作
    protected void onSelectionPerformed(boolean success) {
        super.onSelectionPerformed(success);

        getActivity().finish();
    }

3.4.RadioButtonPickerFragment.java

看下父类,简单看下单选功能如何实现的

>updateCandidates

根据数据动态添加列表选项

    public void updateCandidates() {
        mCandidates.clear();
        final List<? extends CandidateInfo> candidateList = getCandidates();//数据获取子类实现
        //..
        final String defaultKey = getDefaultKey();
        final String systemDefaultKey = getSystemDefaultKey();
        final PreferenceScreen screen = getPreferenceScreen();
        screen.removeAll();
        //...
        if (candidateList != null) {
            for (CandidateInfo info : candidateList) {
            //for循环动态创建preference
                SelectorWithWidgetPreference pref =
                        new SelectorWithWidgetPreference(getPrefContext());
                if (customLayoutResId > 0) {
                    pref.setLayoutResource(customLayoutResId);
                }
                //绑定数据
                bindPreference(pref, info.getKey(), info, defaultKey);
               //添加
                screen.addPreference(pref);
            }
        }
//..
    }

>bindPreference

    public SelectorWithWidgetPreference bindPreference(SelectorWithWidgetPreference pref,
            String key, CandidateInfo info, String defaultKey) {
        pref.setTitle(info.loadLabel());
        pref.setIcon(Utils.getSafeIcon(info.loadIcon()));
        pref.setKey(key);
        //pref的key跟默认的key比较,一样就是选中状态
        if (TextUtils.equals(defaultKey, key)) {
            pref.setChecked(true);
        }
        pref.setEnabled(info.enabled);
        pref.setOnClickListener(this);
        return pref;
    }

>onRadioButtonClicked

    public void onRadioButtonClicked(SelectorWithWidgetPreference selected) {
        final String selectedKey = selected.getKey();
        onRadioButtonConfirmed(selectedKey);
    }
    protected void onRadioButtonConfirmed(String selectedKey) {
    //保存这个点击的选项的key
        final boolean success = setDefaultKey(selectedKey);
        if (success) {
        //更新选中状态,这时候点击的key和保存的key一样,所以会设置为选中状态
            updateCheckedState(selectedKey);
        }
        //点击以后的操作,子类实现,关闭页面
        onSelectionPerformed(success);
    }

4.DreamBackend.java

4.1.enable

可以看到,修改的是Settings的值

    public boolean isEnabled() {
        return getBoolean(Settings.Secure.SCREENSAVER_ENABLED, mDreamsEnabledByDefault);
    }

    public void setEnabled(boolean value) {
        setBoolean(Settings.Secure.SCREENSAVER_ENABLED, value);
    }
    private boolean getBoolean(String key, boolean def) {
        return Settings.Secure.getInt(mContext.getContentResolver(), key, def ? 1 : 0) == 1;
    }

    private void setBoolean(String key, boolean value) {
        Settings.Secure.putInt(mContext.getContentResolver(), key, value ? 1 : 0);
    }

4.2.when to drean

    public int getWhenToDreamSetting() {
        return isActivatedOnDock() && isActivatedOnSleep() ? EITHER
                : isActivatedOnDock() ? WHILE_DOCKED
                        : isActivatedOnSleep() ? WHILE_CHARGING
                                : NEVER;
    }

    public void setWhenToDream(@WhenToDream int whenToDream) {
        setEnabled(whenToDream != NEVER);

        switch (whenToDream) {
            case WHILE_CHARGING:
                setActivatedOnDock(false);
                setActivatedOnSleep(true);
                break;

            case WHILE_DOCKED:
                setActivatedOnDock(true);
                setActivatedOnSleep(false);
                break;

            case EITHER:
                setActivatedOnDock(true);
                setActivatedOnSleep(true);
                break;

            case NEVER:
            default:
                break;
        }
    }

4.3.getActiveDreamName

获取dream组件的名字

    public CharSequence getActiveDreamName() {
        ComponentName cn = getActiveDream();
        if (cn != null) {
            PackageManager pm = mContext.getPackageManager();
            try {
                ServiceInfo ri = pm.getServiceInfo(cn, 0);
                if (ri != null) {
                    return ri.loadLabel(pm);
                }
            }
        }
        return null;
    }

>getActiveDream

    public ComponentName getActiveDream() {
        if (mDreamManager == null) {
            return null;
        }
        try {
            ComponentName[] dreams = mDreamManager.getDreamComponents();
            return dreams != null && dreams.length > 0 ? dreams[0] : null;
        }
    }

4.4.setActiveDream

    public void setActiveDream(ComponentName dream) {
        if (mDreamManager == null) {
            return;
        }
        try {
            ComponentName[] dreams = {dream};
            mDreamManager.setDreamComponents(dream == null ? null : dreams);
        }
    }

4.5.getDreamInfos

  • 可以看到,写个屏保很简单,service里的action是DreamService.SERVICE_INTERFACE就行了。
    public List<DreamInfo> getDreamInfos() {
        ComponentName activeDream = getActiveDream();
        PackageManager pm = mContext.getPackageManager();
        //查找对应action的service
        Intent dreamIntent = new Intent(DreamService.SERVICE_INTERFACE);
        List<ResolveInfo> resolveInfos = pm.queryIntentServices(dreamIntent,
                PackageManager.GET_META_DATA);
        List<DreamInfo> dreamInfos = new ArrayList<>(resolveInfos.size());
        for (ResolveInfo resolveInfo : resolveInfos) {
            final ComponentName componentName = getDreamComponentName(resolveInfo);
            //mDisabledDreams数组是本地配置的,可以过滤自己不想要的
            if (componentName == null || mDisabledDreams.contains(componentName)) {
                continue;
            }

            DreamInfo dreamInfo = new DreamInfo();
            dreamInfo.caption = resolveInfo.loadLabel(pm);
            dreamInfo.icon = resolveInfo.loadIcon(pm);
            dreamInfo.description = getDescription(resolveInfo, pm);
            dreamInfo.componentName = componentName;
            dreamInfo.isActive = dreamInfo.componentName.equals(activeDream);

            final DreamService.DreamMetadata dreamMetadata = DreamService.getDreamMetadata(mContext,
                    resolveInfo.serviceInfo);
            if (dreamMetadata != null) {
                dreamInfo.settingsComponentName = dreamMetadata.settingsActivity;
                dreamInfo.previewImage = dreamMetadata.previewImage;
            }
            dreamInfos.add(dreamInfo);
        }
        dreamInfos.sort(mComparator);
        return dreamInfos;
    }

4.6.setActivatedOnDock

    public void setActivatedOnDock(boolean value) {
        setBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, value);
    }

>setActivatedOnSleep

    public void setActivatedOnSleep(boolean value) {
        setBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, value);
    }

4.7. get|put boolean

    private boolean getBoolean(String key, boolean def) {
        return Settings.Secure.getInt(mContext.getContentResolver(), key, def ? 1 : 0) == 1;
    }

    private void setBoolean(String key, boolean value) {
        Settings.Secure.putInt(mContext.getContentResolver(), key, value ? 1 : 0);
    }

4.8. set|get ActiveDream

    public void setActiveDream(ComponentName dream) {
        if (mDreamManager == null) {
            return;
        }
        try {
            ComponentName[] dreams = {dream};
            mDreamManager.setDreamComponents(dream == null ? null : dreams);
        }
    }

    public ComponentName getActiveDream() {
        if (mDreamManager == null) {
            return null;
        }
        try {
            ComponentName[] dreams = mDreamManager.getDreamComponents();
            return dreams != null && dreams.length > 0 ? dreams[0] : null;
        } 
    }

4.9.getWhenToDreamSetting

    public int getWhenToDreamSetting() {
        return isActivatedOnDock() && isActivatedOnSleep() ? EITHER
                : isActivatedOnDock() ? WHILE_DOCKED
                        : isActivatedOnSleep() ? WHILE_CHARGING
                                : NEVER;
    }

6.DarkModeActivationPreferenceController.java

以前的好像是点击弹个列表框选择,现在直接变成开关了,还可以配置dark模式的时间段,这里就简单研究下开关 image.png

6.1.onSwitchChanged

黑夜模式交给manager来处理了

    public void onSwitchChanged(Switch switchView, boolean isChecked) {
        mMetricsFeatureProvider.logClickedPreference(mPreference, getMetricsCategory());
        final boolean active = (mContext.getResources().getConfiguration().uiMode
                & Configuration.UI_MODE_NIGHT_YES) != 0;
        mUiModeManager.setNightModeActivated(!active);
    }

>setNightModeActivated

UiModeManager.java

    @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
    public boolean setNightModeActivated(boolean active) {
        if (mService != null) {
            try {
            //跨进程操作,最终实现是 小节7.1
                return mService.setNightModeActivated(active);
            } 
        }
        return false;
    }

7.UiModeManagerService.java

7.1.setNightModeActivated

修改mNightMode的方法一

    private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
    
        public boolean setNightModeActivated(boolean active) {
            return setNightModeActivatedForModeInternal(mNightModeCustomType, active);
        }

        private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) {
            if (getContext().checkCallingOrSelfPermission(
                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
                    != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
            final int user = Binder.getCallingUserHandle().getIdentifier();
            if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS)
                    != PackageManager.PERMISSION_GRANTED) {
                return false;

            }
            if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
                mLastBedtimeRequestedNightMode = active;
            }
            if (modeCustomType != mNightModeCustomType) {
                return false;
            }
            synchronized (mLock) {
                final long ident = Binder.clearCallingIdentity();
                try {
                    if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                        unregisterScreenOffEventLocked();
                        mOverrideNightModeOff = !active;
                        mOverrideNightModeOn = active;
                        mOverrideNightModeUser = mCurrentUser;
                        persistNightModeOverrides(mCurrentUser);
                    } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
                            && active) {
                        //默认是no,所以设置里开关应该是关闭的,打开以后会走这里,改为yes
                        mNightMode = UiModeManager.MODE_NIGHT_YES;
                    } else if (mNightMode == UiModeManager.MODE_NIGHT_YES
                            && !active) {
                        //再次关闭又走这里
                        mNightMode = UiModeManager.MODE_NIGHT_NO;
                    }
                    updateConfigurationLocked();
                    applyConfigurationExternallyLocked();
                    persistNightMode(mCurrentUser);
                    return true;
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
        }

7.2.setNightMode

修改mNightMode的方法二

       public void setNightMode(int mode) {
            int customModeType = mode == MODE_NIGHT_CUSTOM
                    ? MODE_NIGHT_CUSTOM_TYPE_SCHEDULE
                    : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
            setNightModeInternal(mode, customModeType);
        }

        private void setNightModeInternal(int mode, int customModeType) {
            if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
                    != PackageManager.PERMISSION_GRANTED)) {
                return;
            }
            switch (mode) {
                case UiModeManager.MODE_NIGHT_NO:
                case UiModeManager.MODE_NIGHT_YES:
                case MODE_NIGHT_AUTO:
                    break;
                case MODE_NIGHT_CUSTOM:
                    if (SUPPORTED_NIGHT_MODE_CUSTOM_TYPES.contains(customModeType)) {
                        break;
                    }
                    throw new IllegalArgumentException(
                            "Can't set the custom type to " + customModeType);
                default:
                    throw new IllegalArgumentException("Unknown mode: " + mode);
            }

            final int user = UserHandle.getCallingUserId();
            final long ident = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    if (mNightMode != mode || mNightModeCustomType != customModeType) {
                        if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                            unregisterScreenOffEventLocked();
                            cancelCustomAlarm();
                        }
                        mNightModeCustomType = mode == MODE_NIGHT_CUSTOM
                                ? customModeType
                                : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
                        //设置为新的mode        
                        mNightMode = mode;
                        resetNightModeOverrideLocked();
                        persistNightMode(user);
                        // on screen off will update configuration instead
                        if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM)
                                || shouldApplyAutomaticChangesImmediately()) {
                            unregisterScreenOffEventLocked();
                            updateLocked(0, 0);
                        } else {
                            registerScreenOffEventLocked();
                        }
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

7.3.onStart

  • 看下mNightMode的默认值,配置里是1,也就是MODE_NIGHT_NO
  • 先直接读取资源文件里的默认配置,完事在开启线程读取settings里存储的值
    public void onStart() {
        final Context context = getContext();
        verifySetupWizardCompleted();

        final Resources res = context.getResources();
        mStartDreamImmediatelyOnDock = res.getBoolean(
                com.android.internal.R.bool.config_startDreamImmediatelyOnDock);
        mDreamsDisabledByAmbientModeSuppression = res.getBoolean(
                com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
        //默认值是1
        mNightMode = res.getInteger(
                com.android.internal.R.integer.config_defaultNightMode);
        mDefaultUiModeType = res.getInteger(
                com.android.internal.R.integer.config_defaultUiModeType);
        mCarModeKeepsScreenOn = (res.getInteger(
                com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
        mDeskModeKeepsScreenOn = (res.getInteger(
                com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
        mEnableCarDockLaunch = res.getBoolean(
                com.android.internal.R.bool.config_enableCarDockHomeLaunch);
        mUiModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockUiMode);
        mNightModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockDayNightMode);
        final PackageManager pm = context.getPackageManager();
        mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
                || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
        mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
        mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);

        // Update the initial, static configurations.
        SystemServerInitThreadPool.submit(() -> {
            synchronized (mLock) {
                TwilightManager twilightManager = getLocalService(TwilightManager.class);
                if (twilightManager != null) mTwilightManager = twilightManager;
                //重新拉去settings的值
                updateNightModeFromSettingsLocked(context, res, UserHandle.getCallingUserId());
                updateSystemProperties();
            }

        }, TAG + ".onStart");
        publishBinderService(Context.UI_MODE_SERVICE, mService);
        publishLocalService(UiModeManagerInternal.class, mLocalService);
    }

>config_defaultNightMode

    <!-- Control the default night mode to use when there is no other mode override set.
         One of the following values (see UiModeManager.java):
             0 - MODE_NIGHT_AUTO
             1 - MODE_NIGHT_NO
             2 - MODE_NIGHT_YES
    -->
    <integer name="config_defaultNightMode">1</integer>

7.3.updateNightModeFromSettingsLocked

    private void updateNightModeFromSettingsLocked(Context context, Resources res, int userId) {
        if (mCarModeEnabled || mCar) {
            return;
        }
        if (mSetupWizardComplete) {
        //获取Settings里存储的值
            mNightMode = Secure.getIntForUser(context.getContentResolver(),
                    Secure.UI_NIGHT_MODE, res.getInteger(
                            com.android.internal.R.integer.config_defaultNightMode), userId);

8.总结

  • 简单研究下设置里的屏保功能,包括总开关,屏保程序的选择,屏保触发的条件
  • 简单看了下dark mode选项开关的操作,最终保存的啥数据。