Android Settings ——language / locale 修改语言

本文探讨了Android系统中修改语言的过程,涉及关键组件如AndroidManifest.xml、InputMethodAndLanguageSettings.java、LocalePicker及配置文件。从UI到系统底层的ActivityManagerService,详细解析了更新配置和locale文件的路径,包括persit.sys.locale和locale_config.xml的使用。

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

看过就忘!为了照顾自己的痴呆脑,劳累双手记录一下。


packages/apps/Settings/AndroidManifest.xml

        <activity android:name="Settings$InputMethodAndLanguageSettingsActivity"
            android:label="@string/language_keyboard_settings_title"
            android:icon="@drawable/ic_settings_language"
            android:taskAffinity="com.android.settings"
            android:parentActivityName="Settings">

            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                android:value="com.android.settings.inputmethod.InputMethodAndLanguageSettings" />

        </activity>

 

[UI 主view]

packages/apps/Settings/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java

public void onCreate(Bundle icicle) {
        addPreferencesFromResource(R.xml.language_settings);
}

packages/apps/Settings/res/xml/language_settings.xml/language_settings

   <PreferenceScreen
            android:key="phone_language"
            android:title="@string/phone_language"
            android:fragment="com.android.settings.localepicker.LocaleListEditor"
            />

packages/apps/Settings/src/com/android/settings/localepicker/LocaleListEditor.java

    @Override
    public void onLocaleSelected(LocaleStore.LocaleInfo locale) {
        mAdapter.addLocale(locale);
        updateVisibilityOfRemoveMenu();
    }

packages/apps/Settings/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java

    void addLocale(LocaleStore.LocaleInfo li) {
        mFeedItemList.add(li);
        notifyItemInserted(mFeedItemList.size() - 1);
        doTheUpdate();
    }
    public void updateLocalesWhenAnimationStops(final LocaleList localeList) {
    
        final RecyclerView.ItemAnimator itemAnimator = mParentView.getItemAnimator();
        itemAnimator.isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
            @Override
            public void onAnimationsFinished() {

                LocalePicker.updateLocales(mLocalesToSetNext);

            }
        });
    }

 

frameworks/base/core/java/com/android/internalapp/LocalePicker.java

    public static void updateLocales(LocaleList locales) {
        try {
            final IActivityManager am = ActivityManagerNative.getDefault();
            final Configuration config = am.getConfiguration();

            config.setLocales(locales);
            config.userSetLocale = true;

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

 

frameworks/base/core/java/android/app/ActivityManagerNative .java

    static public IActivityManager getDefault() {
        return gDefault.get();
    }

ActivityManagerNative 的 getDefault() ,返回的是远程服务对象 ActivityManagerServices.java 在本地的一个代理, 最终调用的是ActivityManagerServices.java 中的 updateCofiguration() 函数。

 

frameworks/base/core/java/android/app/IActivityManager.java

public void updatePersistentConfiguration(Configuration values) throws RemoteException;

 

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
 

  private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
  
        if (values != null) {
            Configuration newConfig = new Configuration(mConfiguration);
            changes = newConfig.updateFrom(values);
            if (changes != 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);
                    mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
                            locales.get(bestLocaleIndex)));
                }

              
        }
        return kept;
    }

 

frameworks/base/core/java/android/content/res/Configuration.java

public @Config int updateFrom(@NonNull Configuration delta) {
        int changed = 0;

        fixUpLocaleList();
        delta.fixUpLocaleList();
        if (!delta.mLocaleList.isEmpty() && !mLocaleList.equals(delta.mLocaleList)) {
            changed |= ActivityInfo.CONFIG_LOCALE;
            mLocaleList = delta.mLocaleList;
            // delta.locale can't be null, since delta.mLocaleList is not empty.
            if (!delta.locale.equals(locale)) {
                locale = (Locale) delta.locale.clone();
                // If locale has changed, then layout direction is also changed ...
                changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
                // ... and we need to update the layout direction (represented by the first
                // 2 most significant bits in screenLayout).
                setLayoutDirection(locale);
            }
        }

        if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
        {
            changed |= ActivityInfo.CONFIG_LOCALE;
            userSetLocale = true;
        }

        return changed;
    }

 

target locale file : "persist.sys.locale"       (data/property/persist.sys.locale)

 

2) 所有语言 文件

 

packages/apps/Settings/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java

  import android.os.LocaleList (全部 语言)

  import com.android.internal.app.LocaleHelper

  import com.android.internal.app.LocalePicker (已选 语言)

  import java.util.Locale


    private static String getLocaleNames(Context context) {
        final LocaleList locales = LocalePicker.getLocales();
        final Locale displayLocale = Locale.getDefault();
        return LocaleHelper.toSentenceCase(
                LocaleHelper.getDisplayLocaleList(
                        locales, displayLocale, 2 /* Show up to two locales from the list */),
                displayLocale);
    }

libcore/ojluni/src/main/java/java/util/Locale.java

external/libgdx/backends/gdx/-backends-gwt/src/com/badlogic/gdx/backends/gwt/emu/jaba/util/Locale.java

frameworks/base/core/java/com/android/internalapp/LocalePicker.java

 

 

packages/apps/Settings/src/com/android/settings/localepicker/LocaleListEditor.java

    private void configureDragAndDrop(View view) {

        mAddLanguage = view.findViewById(R.id.add_language);
        mAddLanguage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final LocalePickerWithRegion selector = LocalePickerWithRegion.createLanguagePicker(
                        getContext(), LocaleListEditor.this, false /* translate only */);

            }
        });
    }

 

frameworks/base/core/java/com/android/internalapp/LocalePickerWithRegion.java
 

    public static LocalePickerWithRegion createLanguagePicker(Context context,
            LocaleSelectedListener listener, boolean translatedOnly) {
        LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
        localePicker.setListener(context, listener, /* parent */ null, translatedOnly);
        return localePicker;
    }

 

 private boolean setListener(Context context, LocaleSelectedListener listener,
            LocaleStore.LocaleInfo parent, boolean translatedOnly) {
      

        final HashSet<String> langTagsToIgnore = new HashSet<>();
        if (!translatedOnly) {
            final LocaleList userLocales = LocalePicker.getLocales();
            final String[] langTags = userLocales.toLanguageTags().split(",");
            Collections.addAll(langTagsToIgnore, langTags);
        }

        if (parent != null) {

        } else {
            mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore,
                    null /* no parent */, translatedOnly);
        }

        return true;
    }

 

frameworks/base/core/java/com/android/internalapp/LocaleStore.java
 

 public static Set<LocaleInfo> getLevelLocales(Context context, Set<String> ignorables,
            LocaleInfo parent, boolean translatedOnly) {
        fillCache(context);

        for (LocaleStore.LocaleInfo li : sLocaleCache.values()) {

        }
        return result;
    }
   public static void fillCache(Context context) {
   

        for (String localeId : LocalePicker.getSupportedLocales(context)) {
          
            LocaleInfo li = new LocaleInfo(localeId);
   
            sLocaleCache.put(li.getId(), li);
        }
    }

 

frameworks/base/core/java/com/android/internalapp/LocalePicker.java


    public static String[] getSupportedLocales(Context context) {
        return context.getResources().getStringArray(R.array.supported_locales);
    }

 

全部语言 file : frameworks/base/core/res/res/values/locale_config.xml

 

 

3) LocalePicker.getLocales()

frameworks/base/core/java/com/android/internalapp/LocalePicker.java

    public static LocaleList getLocales() {
        try {
            return ActivityManagerNative.getDefault()
                    .getConfiguration().getLocales();
        } catch (RemoteException e) {
            // If something went wrong
            return LocaleList.getDefault();
        }
    }

frameworks/base/core/java/android/content/res/Configuration.java

    public @NonNull LocaleList getLocales() {
        fixUpLocaleList();
        return mLocaleList;
    }
    private void fixUpLocaleList() {
        if ((locale == null && !mLocaleList.isEmpty()) ||
                (locale != null && !locale.equals(mLocaleList.get(0)))) {
            mLocaleList = locale == null ? LocaleList.getEmptyLocaleList() : new LocaleList(locale);
        }
    }

frameworks/base/core/java/android/os/LocaleList.java

private static final LocaleList sEmptyLocaleList = new LocaleList();




    public LocaleList(@NonNull Locale... list) {
        if (list.length == 0) {
            mList = sEmptyList;
            mStringRepresentation = "";
        } else {
            final Locale[] localeList = new Locale[list.length];
            final HashSet<Locale> seenLocales = new HashSet<Locale>();
            final StringBuilder sb = new StringBuilder();
            for (int i = 0; i < list.length; i++) {
                final Locale l = list[i];
                if (l == null) {
                    throw new NullPointerException("list[" + i + "] is null");
                } else if (seenLocales.contains(l)) {
                    throw new IllegalArgumentException("list[" + i + "] is a repetition");
                } else {
                    final Locale localeClone = (Locale) l.clone();
                    localeList[i] = localeClone;
                    sb.append(localeClone.toLanguageTag());
                    if (i < list.length - 1) {
                        sb.append(',');
                    }
                    seenLocales.add(localeClone);
                }
            }
            mList = localeList;
            mStringRepresentation = sb.toString();
        }
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值