diff options
| author | 2021-11-10 15:42:53 +0800 | |
|---|---|---|
| committer | 2021-11-26 05:28:40 +0000 | |
| commit | 70ff12551b339369b447cc675e5b163bb0acf922 (patch) | |
| tree | aa87427f3d697640f25091ea8adf39dd589cb847 | |
| parent | 1f3a923a515309bee7fdac59fca2c95b4fd8cea7 (diff) | |
Make InputMethodPreference support work profile
Introduces userId into InputMethodPreference and
per-user context into InputMethodSettingValuesWrapper.
So we can handle the work input method if it's work flow.
Bug: 174360557
Bug: 197707782
Test: Manual test as bug video
Test: atest SettingsLibTests:com.android.settingslib.inputmethod.InputMethodPreferenceTest
Change-Id: Ic233cf183c8783937e638f9ab672c248a136c071
4 files changed, 104 insertions, 33 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java index 1d8f71ea4031..78ec58b89800 100644 --- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java +++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java @@ -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) { diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java index 94a0c00b4222..c1ab706fe45e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java @@ -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); diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java index 13c1b823cb85..39e6dcecba31 100644 --- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java +++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java @@ -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() { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java index 9962e1ca438a..1e75014d2017 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java @@ -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( |