Merge "Introduce IMM#getCurrentInputMethodInfo(userId)"

This commit is contained in:
Taran Singh
2023-01-13 22:06:55 +00:00
committed by Android (Google) Code Review
8 changed files with 161 additions and 1 deletions

View File

@@ -54830,6 +54830,7 @@ package android.view.inputmethod {
public final class InputMethodManager {
method public void dispatchKeyEventFromInputMethod(@Nullable android.view.View, @NonNull android.view.KeyEvent);
method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
method @Nullable public android.view.inputmethod.InputMethodInfo getCurrentInputMethodInfo();
method @Nullable public android.view.inputmethod.InputMethodSubtype getCurrentInputMethodSubtype();
method @NonNull public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodList();
method @NonNull public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeList(@Nullable android.view.inputmethod.InputMethodInfo, boolean);

View File

@@ -16633,6 +16633,14 @@ package android.view.displayhash {
}
package android.view.inputmethod {
public final class InputMethodManager {
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.view.inputmethod.InputMethodInfo getCurrentInputMethodInfoAsUser(@NonNull android.os.UserHandle);
}
}
package android.view.translation {
public final class TranslationCapability implements android.os.Parcelable {

View File

@@ -214,6 +214,21 @@ final class IInputMethodManagerGlobalInvoker {
}
}
@AnyThread
@Nullable
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
static InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId) {
final IInputMethodManager service = getService();
if (service == null) {
return null;
}
try {
return service.getCurrentInputMethodInfoAsUser(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@AnyThread
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)

View File

@@ -339,6 +339,28 @@ public final class InputMethodInfo implements Parcelable {
mIsVrOnly = isVrOnly;
}
/**
* @hide
*/
public InputMethodInfo(InputMethodInfo source) {
mId = source.mId;
mSettingsActivityName = source.mSettingsActivityName;
mIsDefaultResId = source.mIsDefaultResId;
mIsAuxIme = source.mIsAuxIme;
mSupportsSwitchingToNextInputMethod = source.mSupportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = source.mInlineSuggestionsEnabled;
mSupportsInlineSuggestionsWithTouchExploration =
source.mSupportsInlineSuggestionsWithTouchExploration;
mSuppressesSpellChecker = source.mSuppressesSpellChecker;
mShowInInputMethodPicker = source.mShowInInputMethodPicker;
mIsVrOnly = source.mIsVrOnly;
mService = source.mService;
mSubtypes = source.mSubtypes;
mHandledConfigChanges = source.mHandledConfigChanges;
mSupportsStylusHandwriting = source.mSupportsStylusHandwriting;
mForceDefault = source.mForceDefault;
}
InputMethodInfo(Parcel source) {
mId = source.readString();
mSettingsActivityName = source.readString();

View File

@@ -43,6 +43,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UiThread;
@@ -1597,6 +1599,37 @@ public final class InputMethodManager {
return IInputMethodManagerGlobalInvoker.getInputMethodList(userId, directBootAwareness);
}
/**
* Returns the {@link InputMethodInfo} of the currently selected input method (for the process's
* user).
*
* <p>On multi user environment, this API returns a result for the calling process user.</p>
*/
@Nullable
public InputMethodInfo getCurrentInputMethodInfo() {
// We intentionally do not use UserHandle.getCallingUserId() here because for system
// services InputMethodManagerInternal.getCurrentInputMethodInfoForUser() should be used
// instead.
return IInputMethodManagerGlobalInvoker.getCurrentInputMethodInfoAsUser(
UserHandle.myUserId());
}
/**
* Returns the {@link InputMethodInfo} for currently selected input method for the given user.
*
* @param user user to query.
* @hide
*/
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@Nullable
@SystemApi
@SuppressLint("UserHandle")
public InputMethodInfo getCurrentInputMethodInfoAsUser(@NonNull UserHandle user) {
Objects.requireNonNull(user);
return IInputMethodManagerGlobalInvoker.getCurrentInputMethodInfoAsUser(
user.getIdentifier());
}
/**
* Returns the list of enabled input methods.
*

View File

@@ -37,6 +37,11 @@ interface IInputMethodManager {
void addClient(in IInputMethodClient client, in IRemoteInputConnection inputmethod,
int untrustedDisplayId);
// TODO: Use ParceledListSlice instead
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
InputMethodInfo getCurrentInputMethodInfoAsUser(int userId);
// TODO: Use ParceledListSlice instead
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")

View File

@@ -1957,6 +1957,19 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return true;
}
@BinderThread
@Nullable
@Override
public InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
return queryDefaultInputMethodForUserIdLocked(userId);
}
}
@BinderThread
@NonNull
@Override
@@ -5288,6 +5301,53 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return mCurrentSubtype;
}
/**
* Returns the default {@link InputMethodInfo} for the specific userId.
* @param userId user ID to query.
*/
@GuardedBy("ImfLock.class")
private InputMethodInfo queryDefaultInputMethodForUserIdLocked(@UserIdInt int userId) {
final String imeId = mSettings.getSelectedInputMethodForUser(userId);
if (TextUtils.isEmpty(imeId)) {
Slog.e(TAG, "No default input method found for userId " + userId);
return null;
}
InputMethodInfo curInputMethodInfo;
if (userId == mSettings.getCurrentUserId()
&& (curInputMethodInfo = mMethodMap.get(imeId)) != null) {
// clone the InputMethodInfo before returning.
return new InputMethodInfo(curInputMethodInfo);
}
final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = new ArrayMap<>();
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
Context userAwareContext =
mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
final int flags = PackageManager.GET_META_DATA
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_AUTO;
final List<ResolveInfo> services =
userAwareContext.getPackageManager().queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
PackageManager.ResolveInfoFlags.of(flags),
userId);
for (ResolveInfo ri : services) {
final String imeIdResolved = InputMethodInfo.computeId(ri);
if (imeId.equals(imeIdResolved)) {
try {
return new InputMethodInfo(
userAwareContext, ri, additionalSubtypeMap.get(imeId));
} catch (Exception e) {
Slog.wtf(TAG, "Unable to load input method " + imeId, e);
}
}
}
// we didn't find the InputMethodInfo for imeId. This shouldn't happen.
Slog.e(TAG, "Error while locating input method info for imeId: " + imeId);
return null;
}
private ArrayMap<String, InputMethodInfo> queryMethodMapForUser(@UserIdInt int userId) {
final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
final ArrayList<InputMethodInfo> methodList = new ArrayList<>();

View File

@@ -330,11 +330,17 @@ final class InputMethodUtils {
@Nullable
private String getString(@NonNull String key, @Nullable String defaultValue) {
return getStringForUser(key, defaultValue, mCurrentUserId);
}
@Nullable
private String getStringForUser(
@NonNull String key, @Nullable String defaultValue, @UserIdInt int userId) {
final String result;
if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
result = mCopyOnWriteDataStore.get(key);
} else {
result = Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId);
result = Settings.Secure.getStringForUser(mResolver, key, userId);
}
return result != null ? result : defaultValue;
}
@@ -741,6 +747,16 @@ final class InputMethodUtils {
return imi;
}
@Nullable
String getSelectedInputMethodForUser(@UserIdInt int userId) {
final String imi =
getStringForUser(Settings.Secure.DEFAULT_INPUT_METHOD, null, userId);
if (DEBUG) {
Slog.d(TAG, "getSelectedInputMethodForUserStr: " + imi);
}
return imi;
}
void putDefaultVoiceInputMethod(String imeId) {
if (DEBUG) {
Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);