Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe7b9a7e authored by Calvin Pan's avatar Calvin Pan
Browse files

Fixing system language is not in all languages

- Seperate System language and app language logic to different
  controller

- Fixing system langauge only show in suggested langage and not show in
  all languages.

Bug: 228596568
Test: set system/app languages
Change-Id: I3b7ab0a78a77a71794dfd4a59c807637fb7b912d
parent c226210c
Loading
Loading
Loading
Loading
+139 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.app;

import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;

import android.content.Context;
import android.os.Build;
import android.os.LocaleList;
import android.util.Log;

import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

/** The Locale data collector for per-app language. */
class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
    private static final String TAG = AppLocaleCollector.class.getSimpleName();
    private final Context mContext;
    private final String mAppPackageName;
    private final LocaleStore.LocaleInfo mAppCurrentLocale;

    AppLocaleCollector(Context context, String appPackageName) {
        mContext = context;
        mAppPackageName = appPackageName;
        mAppCurrentLocale = LocaleStore.getAppCurrentLocaleInfo(
                mContext, mAppPackageName);
    }

    @Override
    public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) {
        HashSet<String> langTagsToIgnore = new HashSet<>();

        LocaleList systemLangList = LocaleList.getDefault();
        for(int i = 0; i < systemLangList.size(); i++) {
            langTagsToIgnore.add(systemLangList.get(i).toLanguageTag());
        }

        if (mAppCurrentLocale != null) {
            langTagsToIgnore.add(mAppCurrentLocale.getLocale().toLanguageTag());
        }
        return langTagsToIgnore;
    }

    @Override
    public Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
            boolean translatedOnly, boolean isForCountryMode) {
        AppLocaleStore.AppLocaleResult result =
                AppLocaleStore.getAppSupportedLocales(mContext, mAppPackageName);
        Set<String> langTagsToIgnore = getIgnoredLocaleList(translatedOnly);
        Set<LocaleStore.LocaleInfo> appLocaleList = new HashSet<>();
        Set<LocaleStore.LocaleInfo> systemLocaleList;
        boolean shouldShowList =
                result.mLocaleStatus == GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG
                        || result.mLocaleStatus == GET_SUPPORTED_LANGUAGE_FROM_ASSET;

        // Get system supported locale list
        if (isForCountryMode) {
            systemLocaleList = LocaleStore.getLevelLocales(mContext,
                    langTagsToIgnore, parent, translatedOnly);
        } else {
            systemLocaleList = LocaleStore.getLevelLocales(mContext, langTagsToIgnore,
                    null /* no parent */, translatedOnly);
        }

        // Add current app locale
        if (mAppCurrentLocale != null && !isForCountryMode) {
            appLocaleList.add(mAppCurrentLocale);
        }

        // Add current system language into suggestion list
        for(LocaleStore.LocaleInfo localeInfo:
                LocaleStore.getSystemCurrentLocaleInfo()) {
            boolean isNotCurrentLocale = mAppCurrentLocale == null
                    || !localeInfo.getLocale().equals(mAppCurrentLocale.getLocale());
            if (!isForCountryMode && isNotCurrentLocale) {
                appLocaleList.add(localeInfo);
            }
        }

        // Add the languages that included in system supported locale
        if (shouldShowList) {
            appLocaleList.addAll(filterTheLanguagesNotIncludedInSystemLocale(
                    systemLocaleList, result.mAppSupportedLocales));
        }

        // Add "system language" option
        if (!isForCountryMode && shouldShowList) {
            appLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(
                    mAppCurrentLocale == null));
        }

        if (Build.isDebuggable()) {
            Log.d(TAG, "App locale list: " + appLocaleList);
        }

        return appLocaleList;
    }

    @Override
    public boolean hasSpecificPackageName() {
        return true;
    }

    private Set<LocaleStore.LocaleInfo> filterTheLanguagesNotIncludedInSystemLocale(
            Set<LocaleStore.LocaleInfo> systemLocaleList,
            HashSet<Locale> appSupportedLocales) {
        Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>();

        for(LocaleStore.LocaleInfo li: systemLocaleList) {
            if (appSupportedLocales.contains(li.getLocale())) {
                filteredList.add(li);
            } else {
                for(Locale l: appSupportedLocales) {
                    if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
                        filteredList.add(li);
                        break;
                    }
                }
            }
        }
        return filteredList;
    }
}
+46 −115
Original line number Original line Diff line number Diff line
@@ -16,16 +16,12 @@


package com.android.internal.app;
package com.android.internal.app;


import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus;

import android.app.FragmentManager;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.app.ListFragment;
import android.content.Context;
import android.content.Context;
import android.os.Bundle;
import android.os.Bundle;
import android.os.LocaleList;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem;
@@ -36,7 +32,6 @@ import android.widget.SearchView;


import com.android.internal.R;
import com.android.internal.R;


import java.util.Collections;
import java.util.HashSet;
import java.util.HashSet;
import java.util.Locale;
import java.util.Locale;
import java.util.Set;
import java.util.Set;
@@ -54,6 +49,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O


    private SuggestedLocaleAdapter mAdapter;
    private SuggestedLocaleAdapter mAdapter;
    private LocaleSelectedListener mListener;
    private LocaleSelectedListener mListener;
    private LocaleCollectorBase mLocalePickerCollector;
    private Set<LocaleStore.LocaleInfo> mLocaleList;
    private Set<LocaleStore.LocaleInfo> mLocaleList;
    private LocaleStore.LocaleInfo mParentLocale;
    private LocaleStore.LocaleInfo mParentLocale;
    private boolean mTranslatedOnly = false;
    private boolean mTranslatedOnly = false;
@@ -62,7 +58,6 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
    private boolean mPreviousSearchHadFocus = false;
    private boolean mPreviousSearchHadFocus = false;
    private int mFirstVisiblePosition = 0;
    private int mFirstVisiblePosition = 0;
    private int mTopDistance = 0;
    private int mTopDistance = 0;
    private String mAppPackageName;
    private CharSequence mTitle = null;
    private CharSequence mTitle = null;
    private OnActionExpandListener mOnActionExpandListener;
    private OnActionExpandListener mOnActionExpandListener;


@@ -79,31 +74,50 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
        void onLocaleSelected(LocaleStore.LocaleInfo locale);
        void onLocaleSelected(LocaleStore.LocaleInfo locale);
    }
    }


    private static LocalePickerWithRegion createCountryPicker(Context context,
    /**
     * The interface which provides the locale list.
     */
    interface LocaleCollectorBase {
        /** Gets the ignored locale list. */
        HashSet<String> getIgnoredLocaleList(boolean translatedOnly);

        /** Gets the supported locale list. */
        Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
                boolean translatedOnly, boolean isForCountryMode);

        /** Indicates if the class work for specific package. */
        boolean hasSpecificPackageName();
    }

    private static LocalePickerWithRegion createCountryPicker(
            LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
            LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
            boolean translatedOnly, String appPackageName,
            boolean translatedOnly, OnActionExpandListener onActionExpandListener,
            OnActionExpandListener onActionExpandListener) {
            LocaleCollectorBase localePickerCollector) {
        LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
        LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
        localePicker.setOnActionExpandListener(onActionExpandListener);
        localePicker.setOnActionExpandListener(onActionExpandListener);
        boolean shouldShowTheList = localePicker.setListener(context, listener, parent,
        boolean shouldShowTheList = localePicker.setListener(listener, parent,
                translatedOnly, appPackageName);
                translatedOnly, localePickerCollector);
        return shouldShowTheList ? localePicker : null;
        return shouldShowTheList ? localePicker : null;
    }
    }


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


    public static LocalePickerWithRegion createLanguagePicker(Context context,
    public static LocalePickerWithRegion createLanguagePicker(Context context,
            LocaleSelectedListener listener, boolean translatedOnly, String appPackageName,
            LocaleSelectedListener listener, boolean translatedOnly, String appPackageName,
            OnActionExpandListener onActionExpandListener) {
            OnActionExpandListener onActionExpandListener) {
        LocaleCollectorBase localePickerController;
        if (TextUtils.isEmpty(appPackageName)) {
            localePickerController = new SystemLocaleCollector(context);
        } else {
            localePickerController = new AppLocaleCollector(context, appPackageName);
        }
        LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
        LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
        localePicker.setOnActionExpandListener(onActionExpandListener);
        localePicker.setOnActionExpandListener(onActionExpandListener);
        localePicker.setListener(
        localePicker.setListener(listener, /* parent */ null, translatedOnly,
                context, listener, /* parent */ null, translatedOnly, appPackageName);
                localePickerController);
        return localePicker;
        return localePicker;
    }
    }


@@ -120,109 +134,23 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
     * In this case we don't even show the list, we call the listener with that locale,
     * In this case we don't even show the list, we call the listener with that locale,
     * "pretending" it was selected, and return false.</p>
     * "pretending" it was selected, and return false.</p>
     */
     */
    private boolean setListener(Context context, LocaleSelectedListener listener,
    private boolean setListener(LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
            LocaleStore.LocaleInfo parent, boolean translatedOnly, String appPackageName) {
            boolean translatedOnly, LocaleCollectorBase localePickerController) {
        this.mParentLocale = parent;
        this.mParentLocale = parent;
        this.mListener = listener;
        this.mListener = listener;
        this.mTranslatedOnly = translatedOnly;
        this.mTranslatedOnly = translatedOnly;
        this.mAppPackageName = appPackageName;
        this.mLocalePickerCollector = localePickerController;
        setRetainInstance(true);
        setRetainInstance(true);


        final HashSet<String> langTagsToIgnore = new HashSet<>();
        mLocaleList = localePickerController.getSupportedLocaleList(
        LocaleStore.LocaleInfo appCurrentLocale =
                parent, translatedOnly, parent != null);
                LocaleStore.getAppCurrentLocaleInfo(context, appPackageName);
        boolean isForCountryMode = parent != null;

        if (!TextUtils.isEmpty(appPackageName) && !isForCountryMode) {
            // Filter current system locale to add them into suggestion
            LocaleList systemLangList = LocaleList.getDefault();
            for(int i = 0; i < systemLangList.size(); i++) {
                langTagsToIgnore.add(systemLangList.get(i).toLanguageTag());
            }

            if (appCurrentLocale != null) {
                Log.d(TAG, "appCurrentLocale: " + appCurrentLocale.getLocale().toLanguageTag());
                langTagsToIgnore.add(appCurrentLocale.getLocale().toLanguageTag());
            } else {
                Log.d(TAG, "appCurrentLocale is null");
            }
        } else if (!translatedOnly) {
            final LocaleList userLocales = LocalePicker.getLocales();
            final String[] langTags = userLocales.toLanguageTags().split(",");
            Collections.addAll(langTagsToIgnore, langTags);
        }


        if (isForCountryMode) {
        if (parent != null && listener != null && mLocaleList.size() == 1) {
            mLocaleList = LocaleStore.getLevelLocales(context,
                    langTagsToIgnore, parent, translatedOnly);
            if (mLocaleList.size() <= 1) {
                if (listener != null && (mLocaleList.size() == 1)) {
            listener.onLocaleSelected(mLocaleList.iterator().next());
            listener.onLocaleSelected(mLocaleList.iterator().next());
                }
            return false;
            return false;
            }
        } else {
        } else {
            mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore,
                    null /* no parent */, translatedOnly);
        }
        Log.d(TAG, "mLocaleList size:  " + mLocaleList.size());

        // Adding current locale and system default option into suggestion list
        if(!TextUtils.isEmpty(appPackageName)) {
            if (appCurrentLocale != null && !isForCountryMode) {
                mLocaleList.add(appCurrentLocale);
            }

            AppLocaleStore.AppLocaleResult result =
                    AppLocaleStore.getAppSupportedLocales(context, appPackageName);
            boolean shouldShowList =
                    result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG
                    || result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;

            // Add current system language into suggestion list
            for(LocaleStore.LocaleInfo localeInfo: LocaleStore.getSystemCurrentLocaleInfo()) {
                boolean isNotCurrentLocale = appCurrentLocale == null
                        || !localeInfo.getLocale().equals(appCurrentLocale.getLocale());
                if (!isForCountryMode && isNotCurrentLocale) {
                    mLocaleList.add(localeInfo);
                }
            }

            // Filter the language not support in app
            mLocaleList = filterTheLanguagesNotSupportedInApp(
                    shouldShowList, result.mAppSupportedLocales);

            Log.d(TAG, "mLocaleList after app-supported filter:  " + mLocaleList.size());

            // Add "system language"
            if (!isForCountryMode && shouldShowList) {
                mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(appCurrentLocale == null));
            }
        }
            return true;
            return true;
        }
        }

    private Set<LocaleStore.LocaleInfo> filterTheLanguagesNotSupportedInApp(
            boolean shouldShowList, HashSet<Locale> supportedLocales) {
        Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>();
        if (!shouldShowList) {
            return filteredList;
        }

        for(LocaleStore.LocaleInfo li: mLocaleList) {
            if (supportedLocales.contains(li.getLocale())) {
                filteredList.add(li);
            } else {
                for(Locale l: supportedLocales) {
                    if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
                        filteredList.add(li);
                        break;
                    }
                }
            }
        }

        return filteredList;
    }
    }


    private void returnToParentFrame() {
    private void returnToParentFrame() {
@@ -246,7 +174,9 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
        mTitle = getActivity().getTitle();
        mTitle = getActivity().getTitle();
        final boolean countryMode = mParentLocale != null;
        final boolean countryMode = mParentLocale != null;
        final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault();
        final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault();
        mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode, mAppPackageName);
        final boolean hasSpecificPackageName =
                mLocalePickerCollector != null && mLocalePickerCollector.hasSpecificPackageName();
        mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode, hasSpecificPackageName);
        final LocaleHelper.LocaleInfoComparator comp =
        final LocaleHelper.LocaleInfoComparator comp =
                new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode);
                new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode);
        mAdapter.sort(comp);
        mAdapter.sort(comp);
@@ -321,8 +251,8 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
            returnToParentFrame();
            returnToParentFrame();
        } else {
        } else {
            LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker(
            LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker(
                    getContext(), mListener, locale, mTranslatedOnly /* translate only */,
                    mListener, locale, mTranslatedOnly /* translate only */,
                    mAppPackageName, mOnActionExpandListener);
                    mOnActionExpandListener, this.mLocalePickerCollector);
            if (selector != null) {
            if (selector != null) {
                getFragmentManager().beginTransaction()
                getFragmentManager().beginTransaction()
                        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
@@ -340,7 +270,8 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
            inflater.inflate(R.menu.language_selection_list, menu);
            inflater.inflate(R.menu.language_selection_list, menu);


            final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu);
            final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu);
            if (!TextUtils.isEmpty(mAppPackageName) && mOnActionExpandListener != null) {
            if (mLocalePickerCollector.hasSpecificPackageName()
                    && mOnActionExpandListener != null) {
                searchMenuItem.setOnActionExpandListener(mOnActionExpandListener);
                searchMenuItem.setOnActionExpandListener(mOnActionExpandListener);
            }
            }


+5 −5
Original line number Original line Diff line number Diff line
@@ -69,17 +69,17 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
    protected Locale mDisplayLocale = null;
    protected Locale mDisplayLocale = null;
    // used to potentially cache a modified Context that uses mDisplayLocale
    // used to potentially cache a modified Context that uses mDisplayLocale
    protected Context mContextOverride = null;
    protected Context mContextOverride = null;
    private String mAppPackageName;
    private boolean mHasSpecificAppPackageName;


    public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) {
    public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) {
        this(localeOptions, countryMode, null);
        this(localeOptions, countryMode, false);
    }
    }


    public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode,
    public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode,
            String appPackageName) {
            boolean hasSpecificAppPackageName) {
        mCountryMode = countryMode;
        mCountryMode = countryMode;
        mLocaleOptions = new ArrayList<>(localeOptions.size());
        mLocaleOptions = new ArrayList<>(localeOptions.size());
        mAppPackageName = appPackageName;
        mHasSpecificAppPackageName = hasSpecificAppPackageName;


        for (LocaleStore.LocaleInfo li : localeOptions) {
        for (LocaleStore.LocaleInfo li : localeOptions) {
            if (li.isSuggested()) {
            if (li.isSuggested()) {
@@ -136,7 +136,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {


    @Override
    @Override
    public int getViewTypeCount() {
    public int getViewTypeCount() {
        if (!TextUtils.isEmpty(mAppPackageName) && showHeaders()) {
        if (mHasSpecificAppPackageName && showHeaders()) {
            // Two headers, 1 "System language", 1 current locale
            // Two headers, 1 "System language", 1 current locale
            return APP_LANGUAGE_PICKER_TYPE_COUNT;
            return APP_LANGUAGE_PICKER_TYPE_COUNT;
        } else if (showHeaders()) {
        } else if (showHeaders()) {
+66 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.app;

import android.content.Context;
import android.os.LocaleList;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/** The Locale data collector for System language. */
class SystemLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
    private final Context mContext;

    SystemLocaleCollector(Context context) {
        mContext = context;
    }

    @Override
    public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) {
        HashSet<String> ignoreList = new HashSet<>();
        if (!translatedOnly) {
            final LocaleList userLocales = LocalePicker.getLocales();
            final String[] langTags = userLocales.toLanguageTags().split(",");
            Collections.addAll(ignoreList, langTags);
        }
        return ignoreList;
    }

    @Override
    public Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
            boolean translatedOnly, boolean isForCountryMode) {
        Set<String> langTagsToIgnore = getIgnoredLocaleList(translatedOnly);
        Set<LocaleStore.LocaleInfo> localeList;

        if (isForCountryMode) {
            localeList = LocaleStore.getLevelLocales(mContext,
                    langTagsToIgnore, parent, translatedOnly);
        } else {
            localeList = LocaleStore.getLevelLocales(mContext, langTagsToIgnore,
                    null /* no parent */, translatedOnly);
        }
        return localeList;
    }


    @Override
    public boolean hasSpecificPackageName() {
        return false;
    }
}
 No newline at end of file