android13-settings[language]

80 阅读7分钟

1.简介

简单学习语言切换页面相关的功能

1.1.预览图

这里学习下第一个即可,也就是系统语言的修改。 image.png 布局如下

1.2.language_and_input.xml

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:title="@string/language_settings">
    <PreferenceCategory
        android:key="languages_category"
        android:title="@string/locale_picker_category_title">
        <Preference
            android:key="phone_language"
            android:title="@string/phone_language"
            android:fragment="com.android.settings.localepicker.LocaleListEditor" />

        <Preference
            android:key="apps_language"
            android:title="@string/app_locales_picker_menu_title"
            android:summary="@string/app_locale_picker_summary"
            android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
            settings:controller="com.android.settings.applications.appinfo.ManageAppLocalePreferenceController">
            <extra
                android:name="classname"
                android:value="com.android.settings.applications.appinfo.AppLocaleDetails" />
        </Preference>
    </PreferenceCategory>

    <PreferenceCategory
        android:key="keyboards_category"
        android:title="@string/keyboard_and_input_methods_category">
        <Preference
            android:key="virtual_keyboard_pref"
            android:title="@string/virtual_keyboard_category"
            android:fragment="com.android.settings.inputmethod.AvailableVirtualKeyboardFragment"
            settings:keywords="@string/keywords_virtual_keyboard"/>

        <Preference
            android:key="physical_keyboard_pref"
            android:title="@string/physical_keyboard_title"
            android:summary="@string/summary_placeholder"
            android:fragment="com.android.settings.inputmethod.PhysicalKeyboardFragment"/>
    </PreferenceCategory>

    <PreferenceCategory
        android:key="speech_category"
        android:title="@string/speech_category_title">
        <com.android.settings.widget.GearPreference
            android:key="voice_input_settings"
            android:title="@string/voice_input_settings_title"
            android:fragment="com.android.settings.language.DefaultVoiceInputPicker" />

        <Preference
            android:key="tts_settings_summary"
            android:title="@string/tts_settings_title"
            android:fragment="com.android.settings.tts.TextToSpeechSettings"
            settings:searchable="false"/>
    </PreferenceCategory>

    <PreferenceCategory
        android:key="input_assistance_category"
        android:title="@string/input_assistance">
        <!-- Spell checker preference title, summary and fragment will be set programmatically. -->
        <!-- Note: Mark this as persistent="false" to remove unnecessarily saved shared preference.
             See: InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference. -->
        <Preference
            android:key="spellcheckers_settings"
            android:title="@string/spellcheckers_settings_title"
            android:persistent="false"
            android:fragment="com.android.settings.inputmethod.SpellCheckersSettings" />

        <!-- User dictionary preference title and fragment will be set programmatically. -->
        <Preference
            android:key="key_user_dictionary_settings"
            android:title="@string/user_dict_settings_title"
            android:summary="@string/user_dict_settings_summary"
            android:fragment="com.android.settings.inputmethod.UserDictionaryList"
            settings:controller="com.android.settings.language.UserDictionaryPreferenceController" />
    </PreferenceCategory>

    <PreferenceCategory
        android:key="pointer_category"
        android:layout="@layout/preference_category_no_label">
        <com.android.settings.PointerSpeedPreference
            android:key="pointer_speed"
            android:title="@string/pointer_speed"
            android:dialogTitle="@string/pointer_speed" />
    </PreferenceCategory>

    <SwitchPreference
        android:key="vibrate_input_devices"
        android:title="@string/vibrate_input_devices"
        android:summary="@string/vibrate_input_devices_summary"
        settings:controller="com.android.settings.inputmethod.GameControllerPreferenceController" />

    <com.android.settings.widget.WorkOnlyCategory
        android:key="language_and_input_for_work_category"
        android:title="@string/language_and_input_for_work_category_title"
        settings:searchable="false">

        <Preference
            android:key="spellcheckers_settings_for_work_pref"
            android:title="@string/spellcheckers_settings_for_work_title"
            android:fragment="com.android.settings.inputmethod.SpellCheckersSettings"
            settings:forWork="true"
            settings:controller="com.android.settings.core.WorkPreferenceController" />

        <Preference
            android:key="user_dictionary_settings_for_work_pref"
            android:title="@string/user_dict_settings_for_work_title"
            android:fragment="com.android.settings.inputmethod.UserDictionaryList"
            settings:forWork="true"
            settings:controller="com.android.settings.inputmethod.SpellCheckerForWorkPreferenceController" />
    </com.android.settings.widget.WorkOnlyCategory>

</PreferenceScreen>

2.LocaleListEditor

2.1.locale_order_list.xml

加载的布局如下,一个列表,底部有个按钮

<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layoutDirection="locale"
            android:textDirection="locale">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="true"
        android:clipChildren="true"
        android:orientation="vertical">

        <com.android.settings.localepicker.LocaleRecyclerView
            android:id="@+id/dragList"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="vertical"/>

        <Button
            android:id="@+id/add_language"
            android:layout_width="match_parent"
            android:layout_height="?android:listPreferredItemHeight"
            android:paddingStart="?android:attr/listPreferredItemPaddingStart"
            android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
            android:drawableStart="@drawable/ic_add_24dp"
            android:drawablePadding="32dp"
            android:textAlignment="textStart"
            android:text="@string/add_a_language"
            style="@style/Base.Widget.AppCompat.Button.Borderless"
            android:textAppearance="?android:attr/textAppearanceListItem"/>

    </LinearLayout>

</androidx.core.widget.NestedScrollView>

>1.效果图

image.png

2.2.onCreateView

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);

        LocaleStore.fillCache(this.getContext());
        final List<LocaleStore.LocaleInfo> feedsList = getUserLocaleList();
        //adapter
        mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState) {
        final View result = super.onCreateView(inflater, container, savedInstState);
        //加载布局
        final View myLayout = inflater.inflate(R.layout.locale_order_list, (ViewGroup) result);

        configureDragAndDrop(myLayout);//补充1
        return result;
    }

>1.configureDragAndDrop

    private void configureDragAndDrop(View view) {
        final RecyclerView list = view.findViewById(R.id.dragList);
        final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter);
        llm.setAutoMeasureEnabled(true);
        list.setLayoutManager(llm);

        list.setHasFixedSize(true);
        mAdapter.setRecyclerView(list);
        list.setAdapter(mAdapter);
        //老代码是自定义的rv,里边重写的touch方法,这里是新代码
        list.setOnTouchListener(this);//列表的触摸事件见2.3
        
        mAddLanguage = view.findViewById(R.id.add_language);
        mAddLanguage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            //跳转添加语言页面
                final Intent intent = new Intent(getActivity(),
                        LocalePickerWithRegionActivity.class);
                        //回调见补充2
                startActivityForResult(intent, REQUEST_LOCALE_PICKER);
            }
        });
    }

>2.onActivityResult

添加了新的语言的回调

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_LOCALE_PICKER && resultCode == Activity.RESULT_OK
                && data != null) {
            final LocaleStore.LocaleInfo locale =
                    (LocaleStore.LocaleInfo) data.getSerializableExtra(
                            INTENT_LOCALE_KEY);
            mAdapter.addLocale(locale);//见3.1刷新列表
            updateVisibilityOfRemoveMenu();
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

2.3.onTouch

可以看到,点击列表就会进行数据更新,或者弹框提示语言不匹配

    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP
                || event.getAction() == MotionEvent.ACTION_CANCEL) {
            showConfirmDialog(false, mAdapter.getFeedItemList().get(0));
        }
        return false;
    }

    public void showConfirmDialog(boolean isFirstRemoved, LocaleStore.LocaleInfo localeInfo) {
    //数据见3.2.2
        Locale currentSystemLocale = LocalePicker.getLocales().get(0);
        //判断列表的第一个数据是否和当前系统的一样,不一样给与提示
        if (!localeInfo.getLocale().equals(currentSystemLocale)) {
            final LocaleDialogFragment localeDialogFragment =
                    LocaleDialogFragment.newInstance();
            Bundle args = new Bundle();
            args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
            args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE,
                    isFirstRemoved ? LocaleStore.getLocaleInfo(currentSystemLocale) : localeInfo);
            localeDialogFragment.setArguments(args);
            localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
        } else {
        //一样的话进行更新,见3.4
            mAdapter.doTheUpdate();
        }
    }

3.LocaleDragAndDropAdapter.java

3.1.addLocale

    void addLocale(LocaleStore.LocaleInfo li) {
        mFeedItemList.add(li);
        notifyItemInserted(mFeedItemList.size() - 1);//刷新数据
        doTheUpdate();//更新local,见补充1
    }

>1.doTheUpdate

刷新数据,有4处调用。除了上边的addLocal,还有拖拽以后,

    public void doTheUpdate() {
        int count = mFeedItemList.size();
        final Locale[] newList = new Locale[count];

        for (int i = 0; i < count; i++) {
            final LocaleStore.LocaleInfo li = mFeedItemList.get(i);
            newList[i] = li.getLocale();
        }

        final LocaleList ll = new LocaleList(newList);
        updateLocalesWhenAnimationStops(ll);//补充2
    }

>2.updateLocalesWhenAnimationStops

    public void updateLocalesWhenAnimationStops(final LocaleList localeList) {
        if (localeList.equals(mLocalesToSetNext)) {
            return;
        }

        // This will only update the Settings application to make things feel more responsive,
        // the system will be updated later, when animation stopped.
        //设置默认的localList
        LocaleList.setDefault(localeList);

        mLocalesToSetNext = localeList;
        final RecyclerView.ItemAnimator itemAnimator = mParentView.getItemAnimator();
        itemAnimator.isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
            @Override
            public void onAnimationsFinished() {
                if (mLocalesToSetNext == null || mLocalesToSetNext.equals(mLocalesSetLast)) {
                    // All animations finished, but the locale list did not change
                    return;
                }
                //更新localList,参考3.2.1
                LocalePicker.updateLocales(mLocalesToSetNext);
                mLocalesSetLast = mLocalesToSetNext;
                //更新快捷方式,语言变了,名字也变了,参考补充3
                new ShortcutsUpdateTask(mContext).execute();

                mLocalesToSetNext = null;

                mNumberFormatter = NumberFormat.getNumberInstance(Locale.getDefault());
            }
        });
    }

>3.ShortcutsUpdateTask

    static final String SHORTCUT_ID_PREFIX = "component-shortcut-";
    static final Intent SHORTCUT_PROBE = new Intent(Intent.ACTION_MAIN)
            .addCategory("com.android.settings.SHORTCUT")
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    static final String SHORTCUT_ID_PREFIX = "component-shortcut-";
    public Void doInBackground(Void... params) {
        ShortcutManager sm = mContext.getSystemService(ShortcutManager.class);
        PackageManager pm = mContext.getPackageManager();

        List<ShortcutInfo> updates = new ArrayList<>();
        //就是我们长按settings的图标,弹出的可选的快捷方式,你拖动到桌面以后这里就有数据了
        //好像返回的数据只有settings的,其他app的快捷方式不算?
        for (ShortcutInfo info : sm.getPinnedShortcuts()) {
        //只处理特殊的id
            if (!info.getId().startsWith(SHORTCUT_ID_PREFIX)) {
                continue;
            }
            ComponentName cn = ComponentName.unflattenFromString(
                    info.getId().substring(SHORTCUT_ID_PREFIX.length()));
            ResolveInfo ri = pm.resolveActivity(new Intent(SHORTCUT_PROBE).setComponent(cn), 0);
            if (ri == null) {
                continue;
            }
            updates.add(new ShortcutInfo.Builder(mContext, info.getId())
                    .setShortLabel(ri.loadLabel(pm)).build());
        }
        if (!updates.isEmpty()) {
            sm.updateShortcuts(updates);
        }
        return null;
    }

3.2.LocalePicker.java

>1.updateLocales

    public static void updateLocales(LocaleList locales) {
        if (locales != null) {
            locales = removeExcludedLocales(locales);
        }
        try {
            final IActivityManager am = ActivityManager.getService();
            final Configuration config = new Configuration();
            config.setLocales(locales);
            config.userSetLocale = true;

            am.updatePersistentConfigurationWithAttribution(config,
                    ActivityThread.currentOpPackageName(), null);
            // Trigger the dirty bit for the Settings Provider.
            BackupManager.dataChanged("com.android.providers.settings");
        } catch (RemoteException e) {
            // Intentionally left blank
        }
    }

>2.getLocales

    public static LocaleList getLocales() {
        try {
            return ActivityManager.getService()
                    .getConfiguration().getLocales();
        } catch (RemoteException e) {
            return LocaleList.getDefault();
        }
    }

3.3.mItemTouchHelper

拖拽功能利用的是系统的ItemTouchHelper

    public LocaleDragAndDropAdapter(Context context, List<LocaleStore.LocaleInfo> feedItemList) {
        mFeedItemList = feedItemList;
        mContext = context;

        final float dragElevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
                context.getResources().getDisplayMetrics());

        mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
                ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0 /* no swipe */) {
    public void setRecyclerView(RecyclerView rv) {
        mParentView = rv;
        mItemTouchHelper.attachToRecyclerView(rv);
    }

3.4.doTheUpdate

    public void doTheUpdate() {
    //获取当前页面的local数据
        int count = mFeedItemList.size();
        final Locale[] newList = new Locale[count];

        for (int i = 0; i < count; i++) {
            final LocaleStore.LocaleInfo li = mFeedItemList.get(i);
            newList[i] = li.getLocale();
        }
//更新数据
        final LocaleList ll = new LocaleList(newList);
        updateLocalesWhenAnimationStops(ll);//见补充1
    }

>1.updateLocalesWhenAnimationStops

    public void updateLocalesWhenAnimationStops(final LocaleList localeList) {
        if (localeList.equals(mLocalesToSetNext)) {
            return;
        }

        // This will only update the Settings application to make things feel more responsive,
        // the system will be updated later, when animation stopped.
        LocaleList.setDefault(localeList);

        mLocalesToSetNext = localeList;
        final RecyclerView.ItemAnimator itemAnimator = mParentView.getItemAnimator();
        itemAnimator.isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
            @Override
            public void onAnimationsFinished() {
                if (mLocalesToSetNext == null || mLocalesToSetNext.equals(mLocalesSetLast)) {
                    // All animations finished, but the locale list did not change
                    return;
                }

                LocalePicker.updateLocales(mLocalesToSetNext);
                mLocalesSetLast = mLocalesToSetNext;
                new ShortcutsUpdateTask(mContext).execute();

                mLocalesToSetNext = null;

                mNumberFormatter = NumberFormat.getNumberInstance(Locale.getDefault());
            }
        });
    }

3.5.整理

>1.settings里的代码

修改系统语言,最终就执行了下边的操作。

            LocaleList locales = xxx;
            LocaleList.setDefault(localeList);
                    
            final IActivityManager am = ActivityManager.getService();
            final Configuration config = new Configuration();
            config.setLocales(locales);
            config.userSetLocale = true;//这个很重要
//见4.1
            am.updatePersistentConfigurationWithAttribution(config,
                    ActivityThread.currentOpPackageName(), null);

>1.反射逻辑

另外需要是系统应用,并且有对应的权限

    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />

正常代码如下,用反射处理,毕竟settings里的代码里的类我们用不了

    public static void change(Locale locale) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {

            try {
            //开头这堆代码目的是为了保留旧的语言列表
                LocaleList old = LocaleList.getDefault();
                int size = old.size();
                Locale[] newLocales = new Locale[size + 1];
                newLocales[0] = locale;//把要改变的语言放在第一位
                for (int i = 0; i < size; i++) {
                    newLocales[i + 1] = old.get(i);//旧语言放在后边。
                }
//不用担心重复的语言数据,构造方法里会过滤的
                LocaleList localeList = new LocaleList(newLocales);

                Method method = ActivityManager.class.getDeclaredMethod("getService");
                method.setAccessible(true);
                Object obj = method.invoke(null);

                method = obj.getClass().getDeclaredMethod("getConfiguration");
                method.setAccessible(true);
                final Configuration config = (Configuration) method.invoke(obj);

                config.setLocales(localeList);
                Field field = Configuration.class.getDeclaredField("userSetLocale");
                field.setAccessible(true);
                //这个很重要,可以刷新默认语言
                field.set(config, true);

                method = obj.getClass().getDeclaredMethod("updatePersistentConfiguration", Configuration.class);
                method.setAccessible(true);
                method.invoke(obj, config);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

4.ActivityManagerService.java

public class ActivityManagerService extends IActivityManager.Stub

4.1.updatePersistentConfiguration

//        public static final String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";

    public void updatePersistentConfiguration(Configuration values) {
        updatePersistentConfigurationWithAttribution(values,
                Settings.getPackageNameForUid(mContext, Binder.getCallingUid()), null);
    }

    @Override
    public void updatePersistentConfigurationWithAttribution(Configuration values,
            String callingPackage, String callingAttributionTag) {
        //权限检查
        enforceCallingPermission(CHANGE_CONFIGURATION, "updatePersistentConfiguration()");
        enforceWriteSettingsPermission("updatePersistentConfiguration()", callingPackage,
                callingAttributionTag);
        if (values == null) {
            throw new NullPointerException("Configuration must not be null");
        }

        int userId = UserHandle.getCallingUserId();
    //交给5.1处理
        mActivityTaskManager.updatePersistentConfiguration(values, userId);
    }

5.ActivityTaskManagerService.java

5.1.updatePersistentConfiguration

    public void updatePersistentConfiguration(Configuration values, @UserIdInt int userId) {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                // Window configuration is unrelated to persistent configuration (e.g. font scale,
                // locale). Unset it to avoid affecting the current display configuration.
                values.windowConfiguration.setToDefaults();
                //补充1
                updateConfigurationLocked(values, null, false, true, userId,
                        false /* deferResume */);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

>1.updateConfigurationLocked

    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
            boolean initLocale, boolean persistent, int userId, boolean deferResume,
            ActivityTaskManagerService.UpdateConfigurationResult result) {
        int changes = 0;
        boolean kept = true;

        deferWindowLayout();
        try {
            if (values != null) {
            //见5.2
                changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId);
            }

            if (!deferResume) {
                kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
            }
        } finally {
            continueWindowLayout();
        }

        if (result != null) {
            result.changes = changes;
            result.activityRelaunched = !kept;
        }
        return kept;
    }

5.2.updateGlobalConfigurationLocked

    int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
            boolean persistent, int userId) {
        //先获取当前的配置,见补充1
        mTempConfig.setTo(getGlobalConfiguration());
        //更新配置并返回修改项的flag
        final int changes = mTempConfig.updateFrom(values);
        if (changes == 0) {
        //为0说明配置未发生变化
            return 0;
        }
        if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
            final LocaleList locales = values.getLocales();
            int bestLocaleIndex = 0;
            if (locales.size() > 1) {
                if (mSupportedSystemLocales == null) {
                    mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
                }
                bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
            }
            SystemProperties.set("persist.sys.locale",
                    locales.get(bestLocaleIndex).toLanguageTag());
            LocaleList.setDefault(locales, bestLocaleIndex);
        }

        mTempConfig.seq = increaseConfigurationSeqLocked();


        mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());

        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
        updateShouldShowDialogsLocked(mTempConfig);

        AttributeCache ac = AttributeCache.instance();
        if (ac != null) {
            ac.updateConfiguration(mTempConfig);
        }
        mTempConfig.seq = increaseConfigurationSeqLocked();

        Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
        // TODO(multi-display): Update UsageEvents#Event to include displayId.
        mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());

        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
        updateShouldShowDialogsLocked(mTempConfig);

        AttributeCache ac = AttributeCache.instance();
        if (ac != null) {
            ac.updateConfiguration(mTempConfig);
        }

        // Make sure all resources in our process are updated right now, so that anyone who is going
        // to retrieve resource values after we return will be sure to get the new ones. This is
        // especially important during boot, where the first config change needs to guarantee all
        // resources have that config before following boot code is executed.
        mSystemThread.applyConfigurationToResources(mTempConfig);

        if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
            final Message msg = PooledLambda.obtainMessage(
                    ActivityTaskManagerService::sendPutConfigurationForUserMsg,
                    this, userId, new Configuration(mTempConfig));
            mH.sendMessage(msg);
        }
        SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap();
        for (int i = pidMap.size() - 1; i >= 0; i--) {
            final int pid = pidMap.keyAt(i);
            final WindowProcessController app = pidMap.get(pid);
            app.onConfigurationChanged(mTempConfig);
        }

        final Message msg = PooledLambda.obtainMessage(
                ActivityManagerInternal::broadcastGlobalConfigurationChanged,
                mAmInternal, changes, initLocale);
        mH.sendMessage(msg);

        // Update stored global config and notify everyone about the change.
        mRootWindowContainer.onConfigurationChanged(mTempConfig);

        return changes;
    }        

>1.getGlobalConfiguration

    Configuration getGlobalConfiguration() {
        // Return default configuration before mRootWindowContainer initialized, which happens
        // while initializing process record for system, see {@link
        // ActivityManagerService#setSystemProcess}.
        return mRootWindowContainer != null ? mRootWindowContainer.getConfiguration()
                : new Configuration();
    }