android13#settings#display size and text

76 阅读9分钟

1.简介

  • 位置在settings下的display选项,我们学习下红线这个设置 image.png
  • font size,字体大小的修改,影响文本内容的缩放,修改的是Settings.System.FONT_SCALE,见1.7
  • display size,修改的是density,影响所有尺寸的大小,简单点说就是 dp和像素的转化比例发生了变化。见1.6,最终走到6.1保存在本地文件里的。

1.1.display_settings.xml

    <PreferenceCategory
        android:title="@string/category_name_appearance">

        <com.android.settings.display.darkmode.DarkModePreference
            android:key="dark_ui_mode"
            android:title="@string/dark_ui_mode"
            android:fragment="com.android.settings.display.darkmode.DarkModeSettingsFragment"
            android:widgetLayout="@null"
            settings:widgetLayout="@null"
            settings:controller="com.android.settings.display.DarkUIPreferenceController"
            settings:keywords="@string/keywords_dark_ui_mode"/>
    <!--图上对应的就是这个-->
        <Preference
            android:fragment="com.android.settings.accessibility.TextReadingPreferenceFragment"
            android:key="text_reading_options"
            android:persistent="false"
            android:title="@string/accessibility_text_reading_options_title"
            settings:controller="com.android.settings.accessibility.TextReadingFragmentForDisplaySettingsController"/>
    </PreferenceCategory>

可以看到跳转的是TextReadingPreferenceFragment

1.2.TextReadingPreferenceFragment

效果图如下

image.png

>1.accessibility_text_reading_options.xml

参照上边的图就知道都是啥了

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:persistent="false"
    android:title="@string/accessibility_text_reading_options_title">

    <com.android.settings.accessibility.TextReadingPreviewPreference
        android:key="preview"
        android:selectable="false"/>

    <com.android.settings.widget.LabeledSeekBarPreference
        android:key="font_size"
        android:summary="@string/short_summary_font_size"
        android:title="@string/title_font_size"
        settings:iconEnd="@drawable/ic_add_24dp"
        settings:iconEndContentDescription="@string/font_size_make_larger_desc"
        settings:iconStart="@drawable/ic_remove_24dp"
        settings:iconStartContentDescription="@string/font_size_make_smaller_desc"
        settings:keywords="@string/keywords_font_size" />

    <com.android.settings.widget.LabeledSeekBarPreference
        android:key="display_size"
        android:summary="@string/screen_zoom_short_summary"
        android:title="@string/screen_zoom_title"
        settings:iconEnd="@drawable/ic_add_24dp"
        settings:iconEndContentDescription="@string/screen_zoom_make_larger_desc"
        settings:iconStart="@drawable/ic_remove_24dp"
        settings:iconStartContentDescription="@string/screen_zoom_make_smaller_desc"
        settings:keywords="@string/keywords_display_size" />

    <SwitchPreference
        android:key="toggle_force_bold_text"
        android:persistent="false"
        android:title="@string/force_bold_text"
        settings:keywords="@string/keywords_bold_text" />

    <SwitchPreference
        android:key="toggle_high_text_contrast_preference"
        android:persistent="false"
        android:summary="@string/accessibility_toggle_high_text_contrast_preference_summary"
        android:title="@string/accessibility_toggle_high_text_contrast_preference_title" />

    <com.android.settingslib.widget.LayoutPreference
        android:key="reset"
        android:layout="@layout/accessibility_text_reading_reset_button"
        android:persistent="false"
        android:selectable="false" />
</PreferenceScreen>

>2.createPreferenceControllers

    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
        updateEntryPoint();

        final List<AbstractPreferenceController> controllers = new ArrayList<>();
        final FontSizeData fontSizeData = new FontSizeData(context);
        final DisplaySizeData displaySizeData = createDisplaySizeData(context);
//预览控制器,见1.5
        final TextReadingPreviewController previewController = new TextReadingPreviewController(
                context, PREVIEW_KEY, fontSizeData, displaySizeData);
        previewController.setEntryPoint(mEntryPoint);
        controllers.add(previewController);
//font size控制器,见1.4
        final PreviewSizeSeekBarController fontSizeController = new PreviewSizeSeekBarController(
                context, FONT_SIZE_KEY, fontSizeData);
        //seekbar的改变监听,回调就是上边的预览控制器
        fontSizeController.setInteractionListener(previewController);
        controllers.add(fontSizeController);
//display size控制器,见1.4
        final PreviewSizeSeekBarController displaySizeController = new PreviewSizeSeekBarController(
                context, DISPLAY_SIZE_KEY, displaySizeData);
        //seekbar的改变监听,回调就是上边的预览控制器
        displaySizeController.setInteractionListener(previewController);
        controllers.add(displaySizeController);

        mFontWeightAdjustmentController =
                new FontWeightAdjustmentPreferenceController(context, BOLD_TEXT_KEY);
        mFontWeightAdjustmentController.setEntryPoint(mEntryPoint);
        controllers.add(mFontWeightAdjustmentController);

        final HighTextContrastPreferenceController highTextContrastController =
                new HighTextContrastPreferenceController(context, HIGH_TEXT_CONTRAST_KEY);
        highTextContrastController.setEntryPoint(mEntryPoint);
        controllers.add(highTextContrastController);

        final TextReadingResetController resetController =
                new TextReadingResetController(context, RESET_KEY,
                        v -> showDialog(DialogEnums.DIALOG_RESET_SETTINGS));
        resetController.setEntryPoint(mEntryPoint);
        controllers.add(resetController);

        return controllers;
    }

1.3.LabeledSeekBarPreference

这个就是个自定义的,显示布局数据用的,具体的操作处理是在PreviewSizeSeekBarController里的

>1.preference_labeled_slider.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:gravity="center_vertical"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
    android:paddingTop="16dp"
    android:paddingBottom="8dp">
<!--标题,显示在顶部-->
    <TextView
        android:id="@android:id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:ellipsize="marquee"
        android:fadingEdge="horizontal"
        android:singleLine="true"
        android:textAppearance="?android:attr/textAppearanceListItem"
        android:hyphenationFrequency="normalFast"
        android:textColor="?android:attr/textColorPrimary" />
<!--描述,显示在标题下边-->
    <TextView
        android:id="@android:id/summary"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@android:id/title"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:textAlignment="viewStart"
        android:hyphenationFrequency="normalFast"
        android:textColor="?android:attr/textColorSecondary" />
<!--拖动条,见补充2,显示在描述下边-->
    <include
        layout="@layout/icon_discrete_slider"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@android:id/summary" />

<!--拖动条下边显示的额外提示,默认隐藏的-->
    <LinearLayout
        android:id="@+id/label_frame"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/seekbar_frame"
        android:orientation="horizontal"
        android:visibility="gone">

        <TextView
            android:id="@android:id/text1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="start|top"
            android:gravity="start"
            android:layout_weight="1"/>

        <TextView
            android:id="@android:id/text2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="end|top"
            android:gravity="end"
            android:layout_weight="1"/>

    </LinearLayout>

</RelativeLayout>

>2.icon_discrete_slider.xml

两边有个图标,中间就是seekbar

<LinearLayout 
    android:id="@+id/seekbar_frame"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clipChildren="false"
    android:gravity="center_vertical">

    <FrameLayout
        android:id="@+id/icon_start_frame"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:clipChildren="false"
        android:focusable="true"
        android:visibility="gone">

        <ImageView
            android:id="@+id/icon_start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="start|center_vertical"
            android:background="?android:attr/selectableItemBackgroundBorderless"
            android:adjustViewBounds="true"
            android:focusable="false"
            android:tint="?android:attr/textColorPrimary"
            android:tintMode="src_in" />
    </FrameLayout>

    <SeekBar
        android:id="@*android:id/seekbar"
        style="@android:style/Widget.Material.SeekBar.Discrete"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:paddingEnd="12dp"
        android:paddingStart="0dp" />

    <FrameLayout
        android:id="@+id/icon_end_frame"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:clipChildren="false"
        android:focusable="true"
        android:visibility="gone">

        <ImageView
            android:id="@+id/icon_end"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end|center_vertical"
            android:background="?android:attr/selectableItemBackgroundBorderless"
            android:adjustViewBounds="true"
            android:focusable="false"
            android:tint="?android:attr/textColorPrimary"
            android:tintMode="src_in" />
    </FrameLayout>
</LinearLayout>

1.4.PreviewSizeSeekBarController

这个控制器就是监听seekbar的变化

    PreviewSizeSeekBarController(Context context, String preferenceKey,
            @NonNull PreviewSizeData<? extends Number> sizeData) {
        super(context, preferenceKey);
        //就是1.6和1.7的对象
        mSizeData = sizeData;
    }

>1.mSeekBarChangeListener

seekbar的改变监听,可以看到结果交给外部listener处理的,就是1.2.2里设置的预览控制器

    private final SeekBar.OnSeekBarChangeListener mSeekBarChangeListener =
            new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    mInteractionListener.notifyPreferenceChanged();

                    if (!mSeekByTouch && mInteractionListener != null) {
                        mInteractionListener.onProgressChanged();
                    }

                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                    mSeekByTouch = true;
                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    mSeekByTouch = false;

                    if (mInteractionListener != null) {
                        mInteractionListener.onEndTrackingTouch();
                    }
                }
            };

>2.displayPreference

//根据key找到xml里的preference,设置listener

    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        //见1.6或者1.7
        final int dataSize = mSizeData.getValues().size();
        final int initialIndex = mSizeData.getInitialIndex();
        mSeekBarPreference = screen.findPreference(getPreferenceKey());
        mSeekBarPreference.setMax(dataSize - 1);
        mSeekBarPreference.setProgress(initialIndex);
        mSeekBarPreference.setContinuousUpdates(true);
        //设置监听器
        mSeekBarPreference.setOnSeekBarChangeListener(mSeekBarChangeListener);
    }

1.5.TextReadingPreviewController

    private static final long MIN_COMMIT_INTERVAL_MS = 800;
    private static final long CHANGE_BY_SEEKBAR_DELAY_MS = 100;
    private static final long CHANGE_BY_BUTTON_DELAY_MS = 300;

>1.seekbar最终的回调处理

    //拖动中
    public void onProgressChanged() {
        postCommitDelayed(CHANGE_BY_BUTTON_DELAY_MS);//300ms
    }

    //拖动结束
    public void onEndTrackingTouch() {
        postCommitDelayed(CHANGE_BY_SEEKBAR_DELAY_MS);//100ms
    }

>2.postCommitDelayed

延迟执行mCommit

    void postCommitDelayed(long commitDelayMs) {
        //两次间隔时间小于800ms
        if (SystemClock.elapsedRealtime() - mLastCommitTime < MIN_COMMIT_INTERVAL_MS) {
            //加上800ms
            commitDelayMs += MIN_COMMIT_INTERVAL_MS;
        }

        final Choreographer choreographer = Choreographer.getInstance();
        choreographer.removeFrameCallback(mCommit);
        choreographer.postFrameCallbackDelayed(mCommit, commitDelayMs);
    }

>3.mCommit

    private final Choreographer.FrameCallback mCommit = f -> {
        //检查是否变化,是的话进行处理
        tryCommitFontSizeConfig();
        tryCommitDisplaySizeConfig();
        //记录下最后一次提交的时间
        mLastCommitTime = SystemClock.elapsedRealtime();
    };

//
    private void tryCommitFontSizeConfig() {
        final int fontProgress = mFontSizePreference.getProgress();
        if (fontProgress != mLastFontProgress) {
        //见1.7
            mFontSizeData.commit(fontProgress);
            mLastFontProgress = fontProgress;
        }
    }
//
    private void tryCommitDisplaySizeConfig() {
        final int displayProgress = mDisplaySizePreference.getProgress();
        if (displayProgress != mLastDisplayProgress) {
        //见1.6
            mDisplaySizeData.commit(displayProgress);
            mLastDisplayProgress = displayProgress;

        }
    }

1.6.DisplaySizeData

看下commit方法,可以看到是修改configuration了,具体看小节3

class DisplaySizeData extends PreviewSizeData<Integer> {
    DisplaySizeData(Context context) {
        super(context);

        final DisplayDensityUtils density = new DisplayDensityUtils(getContext());
        final int initialIndex = density.getCurrentIndex();
        if (initialIndex < 0) {
            //异常情况,不看
        } else {
        //数据来源见1.8.1
            setDefaultValue(density.getDefaultDensity());
            setInitialIndex(initialIndex);
            setValues(Arrays.stream(density.getValues()).boxed().collect(Collectors.toList()));
        }
    }

    @Override
    void commit(int currentProgress) {
        final int densityDpi = getValues().get(currentProgress);
        if (densityDpi == getDefaultValue()) {
        //见3.2,最终和3.3一样,设置的是默认值
            DisplayDensityConfiguration.clearForcedDisplayDensity(Display.DEFAULT_DISPLAY);
        } else {
        //见3.3,设置新的densityDpi
            DisplayDensityConfiguration.setForcedDisplayDensity(Display.DEFAULT_DISPLAY,
                    densityDpi);
        }
    }
}

1.7.FontSizeData

看下commit方法,就是修改了Settings的值

final class FontSizeData extends PreviewSizeData<Float> {
    private static final float FONT_SCALE_DEF_VALUE = 1.0f;

    FontSizeData(Context context) {
        super(context);

        final Resources resources = getContext().getResources();
        final ContentResolver resolver = getContext().getContentResolver();
        //配置里读取,见补充1
        final List<String> strEntryValues =
                Arrays.asList(resources.getStringArray(R.array.entryvalues_font_size));
        //默认值是1
        setDefaultValue(FONT_SCALE_DEF_VALUE);
        //读取settings的scale值
        final float currentScale =
                Settings.System.getFloat(resolver, Settings.System.FONT_SCALE, getDefaultValue());
        //设置初始化的索引,索引获取见补充2
        setInitialIndex(fontSizeValueToIndex(currentScale, strEntryValues.toArray(new String[0])));
        //设置集合数据
        setValues(strEntryValues.stream().map(Float::valueOf).collect(Collectors.toList()));
    }

    @Override
    void commit(int currentProgress) {
        final ContentResolver resolver = getContext().getContentResolver();
        Settings.System.putFloat(resolver, Settings.System.FONT_SCALE,
                getValues().get(currentProgress));
    }
}

>1.entryvalues_font_size

    <string-array name="entryvalues_font_size" translatable="false">
        <item>0.85</item>
        <item>1.0</item>
        <item>1.15</item>
        <item>1.30</item>
    </string-array>

>2.fontSizeValueToIndex

找出当前值在数组里的index

    public static int fontSizeValueToIndex(float val, String[] indices) {
        float lastVal = Float.parseFloat(indices[0]);
        for (int i = 1; i < indices.length; i++) {
            float thisVal = Float.parseFloat(indices[i]);
            //小于两个值的一半,则认为是匹配小的那个
            if (val < (lastVal + (thisVal - lastVal) * .5f)) {
                return i - 1;
            }
            lastVal = thisVal;
        }
        //没找到,那默认是最后一个
        return indices.length - 1;
    }

1.8.DisplayDensityUtils.java

>1.构造方法

    public DisplayDensityUtils(Context context) {
    //参考3.1
        final int defaultDensity = DisplayDensityUtils.getDefaultDisplayDensity(
                Display.DEFAULT_DISPLAY);
        if (defaultDensity <= 0) {
            mEntries = null;
            mValues = null;
            mDefaultDensity = 0;
            mCurrentIndex = -1;
            return;
        }

        final Resources res = context.getResources();
        DisplayInfo info = new DisplayInfo();
        context.getDisplayNoVerify().getDisplayInfo(info);
        //系统当前的density
        final int currentDensity = info.logicalDensityDpi;
        int currentDensityIndex = -1;

        //计算设备的最大,最小scale值
        //宽高的最小像素大小
        final int minDimensionPx = Math.min(info.logicalWidth, info.logicalHeight);
        //160*min/320
        final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
        //读取配置里的最大scale值,见补充2
        final float maxScaleDimen = context.getResources().getFraction(
                R.fraction.display_density_max_scale, 1, 1);
        // maxDensity /defaultDensity算出来的scale,根上边配置里读取的比较,取最小值
        final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity);
        //读取配置里的最小scale值,见补充2
        final float minScale = context.getResources().getFraction(
                R.fraction.display_density_min_scale, 1, 1);
        //读取配置里的最小scale间隔值,见补充2        
        final float minScaleInterval = context.getResources().getFraction(
                R.fraction.display_density_min_scale_interval, 1, 1);
        //计算下支持的放大次数
        final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval,
                0, SUMMARIES_LARGER.length);//见补充3,这里是3
        //计算下支持的缩小次数        
        final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval,
                0, SUMMARIES_SMALLER.length);//见补充3,这里是1

        //放大次数+缩小次数+默认值
        String[] entries = new String[1 + numSmaller + numLarger];
        int[] values = new int[entries.length];
        int curIndex = 0;
        //下边就是计算数据了
        //缩小的
        if (numSmaller > 0) {
            final float interval = (1 - minScale) / numSmaller;
            for (int i = numSmaller - 1; i >= 0; i--) {
                // Round down to a multiple of 2 by truncating the low bit.
                final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
                if (currentDensity == density) {
                    currentDensityIndex = curIndex;
                }
                entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
                values[curIndex] = density;
                curIndex++;
            }
        }
        //默认值
        if (currentDensity == defaultDensity) {
            currentDensityIndex = curIndex;
        }
        values[curIndex] = defaultDensity;
        entries[curIndex] = res.getString(SUMMARY_DEFAULT);
        curIndex++;
        //放大的
        if (numLarger > 0) {
            final float interval = (maxScale - 1) / numLarger;
            for (int i = 0; i < numLarger; i++) {
                // Round down to a multiple of 2 by truncating the low bit.
                final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
                if (currentDensity == density) {
                    currentDensityIndex = curIndex;
                }
                values[curIndex] = density;
                entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
                curIndex++;
            }
        }

        final int displayIndex;
        if (currentDensityIndex >= 0) {
        //系统当前的density有和我们数组里的数据匹配,那这里就设置对应的index
            displayIndex = currentDensityIndex;
        } else {
            //走到这里,说明系统当前的density没有找到匹配的。
            int newLength = values.length + 1;
            values = Arrays.copyOf(values, newLength);
            //数组长度加一,把系统当前的density放到数组末尾
            values[curIndex] = currentDensity;
            //同上,这里加的是summary
            entries = Arrays.copyOf(entries, newLength);
            entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);

            displayIndex = curIndex;
        }
        //默认的density
        mDefaultDensity = defaultDensity;
        //当前density的索引
        mCurrentIndex = displayIndex;
        //数组density对应的描述
        mEntries = entries;
        //支持的density数组
        mValues = values;
    }

>2.density最大最小值以及间隔

    <!-- Minimum increment between density scales. -->
    <fraction name="display_density_min_scale_interval">9%</fraction>
    <!-- Maximum density scale. The actual scale used depends on the device. -->
    <fraction name="display_density_max_scale">150%</fraction>
    <!-- Minimum density scale. This is available on all devices. -->
    <fraction name="display_density_min_scale">85%</fraction>

>3.常量

    /**
     * Summaries for scales smaller than "default" in order of smallest to
     * largest. 缩小,最多支持下边一个
     */
    private static final int[] SUMMARIES_SMALLER = new int[] {
            R.string.screen_zoom_summary_small
    };

    /**
     * Summaries for scales larger than "default" in order of smallest to
     * largest. 放大,最多支持下边3个
     */
    private static final int[] SUMMARIES_LARGER = new int[] {
            R.string.screen_zoom_summary_large,
            R.string.screen_zoom_summary_very_large,
            R.string.screen_zoom_summary_extremely_large,
    };

3.DisplayDensityConfiguration

3.1.getDefaultDisplayDensity

    static int getDefaultDisplayDensity(int displayId) {
        try {
            final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
            return wm.getInitialDisplayDensity(displayId);
        } catch (RemoteException exc) {
            return -1;
        }
    }

>1.WindowManagerService.java

    public int getInitialDisplayDensity(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                return displayContent.mInitialDisplayDensity;
            }
        }
        return -1;
    }

3.2.clearForcedDisplayDensity

    public static void clearForcedDisplayDensity(final int displayId) {
        final int userId = UserHandle.myUserId();
        AsyncTask.execute(
                () -> {
                    try {
                        final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
                        //见4.1
                        wm.clearForcedDisplayDensityForUser(displayId, userId);
                    } 
                });
    }

3.3.setForcedDisplayDensity

    public static void setForcedDisplayDensity(final int displayId, final int density) {
        final int userId = UserHandle.myUserId();
        AsyncTask.execute(
                () -> {
                    try {
                        final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
                        //见4.2
                        wm.setForcedDisplayDensityForUser(displayId, density, userId);
                    }
                });
    }

4.WindowManagerService

4.1.clearForcedDisplayDensityForUser

    public void clearForcedDisplayDensityForUser(int displayId, int userId) {
//..
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent != null) {
                //见5.1,可以看到,这里传了个默认值
                    displayContent.setForcedDensity(displayContent.mInitialDisplayDensity,
                            callingUserId);
                }
            }
//..
    }

4.2.setForcedDisplayDensityForUser

    public void setForcedDisplayDensityForUser(int displayId, int density, int userId) {
//..
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent != null) {
                //见5.1,这里传递的density是外部传递过来的
                    displayContent.setForcedDensity(density, targetUserId);
                }
            }
//..
    }

4.3.getInitialDisplayDensity

    public int getInitialDisplayDensity(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                return displayContent.mInitialDisplayDensity;
            }
        }
        return -1;
    }

5.DisplayContent

5.1.setForcedDensity

    void setForcedDensity(int density, int userId) {
        mIsDensityForced = density != mInitialDisplayDensity;
        final boolean updateCurrent = userId == UserHandle.USER_CURRENT;
        if (mWmService.mCurrentUserId == userId || updateCurrent) {
            mBaseDisplayDensity = density;//修改了这个值,
            //见5.2,重新config
            reconfigureDisplayLocked();
        }
        if (updateCurrent) {
            // We are applying existing settings so no need to save it again.
            return;
        }


        if (density == mInitialDisplayDensity) {
            density = 0;
        }
        mWmService.mDisplayWindowSettings.setForcedDensity(this, density, userId);
    }

5.2.reconfigureDisplayLocked

    void reconfigureDisplayLocked() {
        if (!isReady()) {
            return;
        }
        configureDisplayPolicy();
        setLayoutNeeded();

        boolean configChanged = updateOrientation();
        //获取旧的
        final Configuration currentDisplayConfig = getConfiguration();
        mTmpConfiguration.setTo(currentDisplayConfig);
        //获取新的
        computeScreenConfiguration(mTmpConfiguration);
        //比较是否发生变化
        final int changes = currentDisplayConfig.diff(mTmpConfiguration);
        configChanged |= changes != 0;

        if (configChanged) {
            mWaitingForConfig = true;
            if (mTransitionController.isShellTransitionsEnabled()) {
                requestChangeTransitionIfNeeded(changes, null /* displayChange */);
            } else if (mLastHasContent) {
                mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
            }
            sendNewConfiguration();
        }

        mWmService.mWindowPlacerLocked.performSurfacePlacement();
    }

>1.configureDisplayPolicy

    void configureDisplayPolicy() {
        mRootWindowContainer.updateDisplayImePolicyCache();
        mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
        mDisplayRotation.configure(mBaseDisplayWidth, mBaseDisplayHeight);
    }

>2.setLayoutNeeded

    void setLayoutNeeded() {
        mLayoutNeeded = true;
    }

>3.updateOrientation

    boolean updateOrientation() {
        return updateOrientation(false /* forceUpdate */);
    }
    private boolean updateOrientation(boolean forceUpdate) {
        final int orientation = getOrientation();
        // The last orientation source is valid only after getOrientation.
        final WindowContainer orientationSource = getLastOrientationSource();
        final ActivityRecord r =
                orientationSource != null ? orientationSource.asActivityRecord() : null;
        if (r != null) {
            final Task task = r.getTask();
            if (task != null && orientation != task.mLastReportedRequestedOrientation) {
                task.mLastReportedRequestedOrientation = orientation;
                mAtmService.getTaskChangeNotificationController()
                        .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation);
            }
            // Currently there is no use case from non-activity.
            if (handleTopActivityLaunchingInDifferentOrientation(r, true /* checkOpening */)) {
                // Display orientation should be deferred until the top fixed rotation is finished.
                return false;
            }
        }
        return mDisplayRotation.updateOrientation(orientation, forceUpdate);
    }

>4.computeScreenConfiguration

    /**
     * Compute display configuration based on display properties and policy settings.
     * Do not call if mDisplayReady == false.
     */
    void computeScreenConfiguration(Configuration config) {
        final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
        final int dw = displayInfo.logicalWidth;
        final int dh = displayInfo.logicalHeight;
        mTmpRect.set(0, 0, dw, dh);
        config.windowConfiguration.setBounds(mTmpRect);
        config.windowConfiguration.setMaxBounds(mTmpRect);
        config.windowConfiguration.setWindowingMode(getWindowingMode());
        config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());

        computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation, config.uiMode,
                displayInfo.displayCutout);

        config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
                | ((displayInfo.flags & Display.FLAG_ROUND) != 0
                ? Configuration.SCREENLAYOUT_ROUND_YES
                : Configuration.SCREENLAYOUT_ROUND_NO);

        config.densityDpi = displayInfo.logicalDensityDpi;

        config.colorMode =
                ((displayInfo.isHdr() && mWmService.hasHdrSupport())
                        ? Configuration.COLOR_MODE_HDR_YES
                        : Configuration.COLOR_MODE_HDR_NO)
                        | (displayInfo.isWideColorGamut() && mWmService.hasWideColorGamutSupport()
                        ? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
                        : Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);

        // Update the configuration based on available input devices, lid switch,
        // and platform configuration.
        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
        config.keyboard = Configuration.KEYBOARD_NOKEYS;
        config.navigation = Configuration.NAVIGATION_NONAV;

        int keyboardPresence = 0;
        int navigationPresence = 0;
        final InputDevice[] devices = mWmService.mInputManager.getInputDevices();
        final int len = devices != null ? devices.length : 0;
        for (int i = 0; i < len; i++) {
            InputDevice device = devices[i];
            // Ignore virtual input device.
            if (device.isVirtual()) {
                continue;
            }

            // Check if input device can dispatch events to current display.
            if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(), mDisplayId)) {
                continue;
            }

            final int sources = device.getSources();
            final int presenceFlag = device.isExternal()
                    ? WindowManagerPolicy.PRESENCE_EXTERNAL : WindowManagerPolicy.PRESENCE_INTERNAL;

            if (mWmService.mIsTouchDevice) {
                if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) {
                    config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
                }
            } else {
                config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
            }

            if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
                config.navigation = Configuration.NAVIGATION_TRACKBALL;
                navigationPresence |= presenceFlag;
            } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
                    && config.navigation == Configuration.NAVIGATION_NONAV) {
                config.navigation = Configuration.NAVIGATION_DPAD;
                navigationPresence |= presenceFlag;
            }

            if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
                config.keyboard = Configuration.KEYBOARD_QWERTY;
                keyboardPresence |= presenceFlag;
            }
        }

        if (config.navigation == Configuration.NAVIGATION_NONAV && mWmService.mHasPermanentDpad) {
            config.navigation = Configuration.NAVIGATION_DPAD;
            navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
        }

        // Determine whether a hard keyboard is available and enabled.
        // TODO(multi-display): Should the hardware keyboard be tied to a display or to a device?
        boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
        if (hardKeyboardAvailable != mWmService.mHardKeyboardAvailable) {
            mWmService.mHardKeyboardAvailable = hardKeyboardAvailable;
            mWmService.mH.removeMessages(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
            mWmService.mH.sendEmptyMessage(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
        }

        mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();

        // Let the policy update hidden states.
        config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
        config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
        config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
        mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
    }

6.DisplayWindowSettings.java

6.1.setForcedDensity

    void setForcedDensity(DisplayContent displayContent, int density, int userId) {
        if (displayContent.isDefaultDisplay) {
            final String densityString = density == 0 ? "" : Integer.toString(density);
            //修改
            Settings.Secure.putStringForUser(mService.mContext.getContentResolver(),
                    Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId);
        }

        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
        final SettingsProvider.SettingsEntry overrideSettings =
                mSettingsProvider.getOverrideSettings(displayInfo);
        overrideSettings.mForcedDensity = density;
        //这个最终就是把数据存储起来了
        mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
    }