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

Commit 040d9338 authored by Wilson Wu's avatar Wilson Wu Committed by Android (Google) Code Review
Browse files

Merge "Make InputMethodPreference support work profile"

parents 0626f7c7 70ff1255
Loading
Loading
Loading
Loading
+41 −4
Original line number Diff line number Diff line
@@ -18,11 +18,13 @@ package com.android.settingslib.inputmethod;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.icu.text.ListFormatter;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
@@ -155,9 +157,41 @@ public class InputMethodAndSubtypeUtilCompat {
        return set;
    }

    public static void saveInputMethodSubtypeList(PreferenceFragmentCompat context,
    /**
     * Save the enabled/disabled input methods and selected subtype states into system settings.
     *
     * @param fragment The preference fragment user interact with.
     * @param resolver The {@link ContentResolver} used to access the database.
     * @param inputMethodInfos The list of {@link InputMethodInfo} to be checked.
     * @param hasHardKeyboard {@code true} if the device has the hardware keyboard.
     */
    public static void saveInputMethodSubtypeList(PreferenceFragmentCompat fragment,
            ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
            boolean hasHardKeyboard) {
        saveInputMethodSubtypeListForUserInternal(
                fragment, resolver, inputMethodInfos, hasHardKeyboard, UserHandle.myUserId());
    }

    /**
     * Save the enabled/disabled input methods and selected subtype states into system settings as
     * given userId.
     *
     * @param fragment The preference fragment user interact with.
     * @param resolver The {@link ContentResolver} used to access the database.
     * @param inputMethodInfos The list of {@link InputMethodInfo} to be checked.
     * @param hasHardKeyboard {@code true} if the device has the hardware keyboard.
     * @param userId The given userId
     */
    public static void saveInputMethodSubtypeListForUser(PreferenceFragmentCompat fragment,
            ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
            boolean hasHardKeyboard, @UserIdInt int userId) {
        saveInputMethodSubtypeListForUserInternal(
                fragment, resolver, inputMethodInfos, hasHardKeyboard, userId);
    }

    private static void saveInputMethodSubtypeListForUserInternal(PreferenceFragmentCompat fragment,
            ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
            boolean hasHardKeyboard, @UserIdInt int userId) {
        String currentInputMethodId = Settings.Secure.getString(resolver,
                Settings.Secure.DEFAULT_INPUT_METHOD);
        final int selectedInputMethodSubtype = getInputMethodSubtypeSelected(resolver);
@@ -168,7 +202,7 @@ public class InputMethodAndSubtypeUtilCompat {
        boolean needsToResetSelectedSubtype = false;
        for (final InputMethodInfo imi : inputMethodInfos) {
            final String imiId = imi.getId();
            final Preference pref = context.findPreference(imiId);
            final Preference pref = fragment.findPreference(imiId);
            if (pref == null) {
                continue;
            }
@@ -184,8 +218,11 @@ public class InputMethodAndSubtypeUtilCompat {
            }
            final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
            final boolean systemIme = imi.isSystem();
            // Create context as given userId
            final Context wrapperContext = userId == UserHandle.myUserId() ? fragment.getActivity()
                    : fragment.getActivity().createContextAsUser(UserHandle.of(userId), 0);
            if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
                    context.getActivity()).isAlwaysCheckedIme(imi))
                    wrapperContext).isAlwaysCheckedIme(imi))
                    || isImeChecked) {
                if (!enabledIMEsAndSubtypesMap.containsKey(imiId)) {
                    // imiId has just been enabled
@@ -198,7 +235,7 @@ public class InputMethodAndSubtypeUtilCompat {
                for (int i = 0; i < subtypeCount; ++i) {
                    final InputMethodSubtype subtype = imi.getSubtypeAt(i);
                    final String subtypeHashCodeStr = String.valueOf(subtype.hashCode());
                    final TwoStatePreference subtypePref = (TwoStatePreference) context
                    final TwoStatePreference subtypePref = (TwoStatePreference) fragment
                            .findPreference(imiId + subtypeHashCodeStr);
                    // In the Configure input method screen which does not have subtype preferences.
                    if (subtypePref == null) {
+27 −19
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.settingslib.inputmethod;

import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;

import android.annotation.UserIdInt;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -75,30 +76,34 @@ public class InputMethodPreference extends PrimarySwitchPreference
    private final OnSavePreferenceListener mOnSaveListener;
    private final InputMethodSettingValuesWrapper mInputMethodSettingValues;
    private final boolean mIsAllowedByOrganization;
    @UserIdInt
    private final int mUserId;

    private AlertDialog mDialog = null;

    /**
     * A preference entry of an input method.
     *
     * @param context The Context this is associated with.
     * @param prefContext The Context this preference is associated with.
     * @param imi The {@link InputMethodInfo} of this preference.
     * @param isAllowedByOrganization false if the IME has been disabled by a device or profile
     *     owner.
     * @param onSaveListener The listener called when this preference has been changed and needs
     *     to save the state to shared preference.
     * @param userId The userId to specify the corresponding user for this preference.
     */
    public InputMethodPreference(final Context context, final InputMethodInfo imi,
            final boolean isAllowedByOrganization, final OnSavePreferenceListener onSaveListener) {
        this(context, imi, imi.loadLabel(context.getPackageManager()), isAllowedByOrganization,
                onSaveListener);
    public InputMethodPreference(final Context prefContext, final InputMethodInfo imi,
            final boolean isAllowedByOrganization, final OnSavePreferenceListener onSaveListener,
            final @UserIdInt int userId) {
        this(prefContext, imi, imi.loadLabel(prefContext.getPackageManager()),
                isAllowedByOrganization, onSaveListener, userId);
    }

    @VisibleForTesting
    InputMethodPreference(final Context context, final InputMethodInfo imi,
    InputMethodPreference(final Context prefContext, final InputMethodInfo imi,
            final CharSequence title, final boolean isAllowedByOrganization,
            final OnSavePreferenceListener onSaveListener) {
        super(context);
            final OnSavePreferenceListener onSaveListener, final @UserIdInt int userId) {
        super(prefContext);
        setPersistent(false);
        mImi = imi;
        mIsAllowedByOrganization = isAllowedByOrganization;
@@ -114,7 +119,12 @@ public class InputMethodPreference extends PrimarySwitchPreference
            intent.setClassName(imi.getPackageName(), settingsActivity);
            setIntent(intent);
        }
        mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(context);
        // Handle the context by given userId because {@link InputMethodSettingValuesWrapper} is
        // per-user instance.
        final Context userAwareContext = userId == UserHandle.myUserId() ? prefContext :
                getContext().createContextAsUser(UserHandle.of(userId), 0);
        mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(userAwareContext);
        mUserId = userId;
        mHasPriorityInSorting = imi.isSystem()
                && InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(imi);
        setOnPreferenceClickListener(this);
@@ -130,17 +140,15 @@ public class InputMethodPreference extends PrimarySwitchPreference
        super.onBindViewHolder(holder);
        final Switch switchWidget = getSwitch();
        if (switchWidget != null) {
            // Avoid default behavior in {@link PrimarySwitchPreference#onBindViewHolder}.
            switchWidget.setOnClickListener(v -> {
                // no-op, avoid default behavior in {@link PrimarySwitchPreference#onBindViewHolder}
            });
            switchWidget.setOnCheckedChangeListener((buttonView, isChecked) -> {
                // Avoid the invocation after we call {@link PrimarySwitchPreference#setChecked()}
                // in {@link setCheckedInternal}
                if (isChecked != isChecked()) {
                    // Keep switch to previous state because we have to show the dialog first
                    buttonView.setChecked(!isChecked);
                    callChangeListener(isChecked());
                if (!switchWidget.isEnabled()) {
                    return;
                }
                final boolean newValue = !isChecked();
                // Keep switch to previous state because we have to show the dialog first.
                switchWidget.setChecked(isChecked());
                callChangeListener(newValue);
            });
        }
        final ImageView icon = holder.itemView.findViewById(android.R.id.icon);
@@ -187,7 +195,7 @@ public class InputMethodPreference extends PrimarySwitchPreference
            final Intent intent = getIntent();
            if (intent != null) {
                // Invoke a settings activity of an input method.
                context.startActivity(intent);
                context.startActivityAsUser(intent, UserHandle.of(mUserId));
            }
        } catch (final ActivityNotFoundException e) {
            Log.d(TAG, "IME's Settings Activity Not Found", e);
+33 −9
Original line number Diff line number Diff line
@@ -16,13 +16,18 @@

package com.android.settingslib.inputmethod;

import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.UiThread;
import android.content.ContentResolver;
import android.content.Context;
import android.util.Log;
import android.util.SparseArray;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -39,20 +44,39 @@ import java.util.List;
public class InputMethodSettingValuesWrapper {
    private static final String TAG = InputMethodSettingValuesWrapper.class.getSimpleName();

    private static volatile InputMethodSettingValuesWrapper sInstance;
    private static final Object sInstanceMapLock = new Object();
    /**
     * Manages mapping between user ID and corresponding singleton
     * {@link InputMethodSettingValuesWrapper} object.
     */
    @GuardedBy("sInstanceMapLock")
    private static SparseArray<InputMethodSettingValuesWrapper> sInstanceMap = new SparseArray<>();
    private final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
    private final ContentResolver mContentResolver;
    private final InputMethodManager mImm;

    public static InputMethodSettingValuesWrapper getInstance(Context context) {
        if (sInstance == null) {
            synchronized (TAG) {
                if (sInstance == null) {
                    sInstance = new InputMethodSettingValuesWrapper(context);
    @AnyThread
    @NonNull
    public static InputMethodSettingValuesWrapper getInstance(@NonNull Context context) {
        final int requestUserId = context.getUserId();
        InputMethodSettingValuesWrapper valuesWrapper;
        // First time to create the wrapper.
        synchronized (sInstanceMapLock) {
            if (sInstanceMap.size() == 0) {
                valuesWrapper = new InputMethodSettingValuesWrapper(context);
                sInstanceMap.put(requestUserId, valuesWrapper);
                return valuesWrapper;
            }
            // We have same user context as request.
            if (sInstanceMap.indexOfKey(requestUserId) >= 0) {
                return sInstanceMap.get(requestUserId);
            }
            // Request by a new user context.
            valuesWrapper = new InputMethodSettingValuesWrapper(context);
            sInstanceMap.put(context.getUserId(), valuesWrapper);
        }
            }
        }
        return sInstance;

        return valuesWrapper;
    }

    // Ensure singleton
@@ -64,7 +88,7 @@ public class InputMethodSettingValuesWrapper {

    public void refreshAllInputMethodAndSubtypes() {
        mMethodList.clear();
        mMethodList.addAll(mImm.getInputMethodList());
        mMethodList.addAll(mImm.getInputMethodListAsUser(mContentResolver.getUserId()));
    }

    public List<InputMethodInfo> getInputMethodList() {
+3 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.UserHandle;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;

@@ -112,7 +113,8 @@ public class InputMethodPreferenceTest {
                createInputMethodInfo(systemIme, name),
                title,
                true /* isAllowedByOrganization */,
                p -> {} /* onSavePreferenceListener */);
                p -> {} /* onSavePreferenceListener */,
                UserHandle.myUserId());
    }

    private static InputMethodInfo createInputMethodInfo(