summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/widget/Editor.java11
-rw-r--r--core/java/android/widget/SpellChecker.java12
-rw-r--r--core/java/android/widget/TextView.java75
3 files changed, 91 insertions, 7 deletions
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3c32bb29cf66..55364ec1017d 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2394,6 +2394,17 @@ public class Editor {
}
}
+ /**
+ * Called when {@link TextView#mTextOperationUser} has changed.
+ *
+ * <p>Any user-specific resources need to be refreshed here.</p>
+ */
+ final void onTextOperationUserChanged() {
+ if (mSpellChecker != null) {
+ mSpellChecker.resetSession();
+ }
+ }
+
protected void stopTextActionMode() {
if (mTextActionMode != null) {
// This will hide the mSelectionModifierCursorController
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index fc1172e0457a..d4aad752daa0 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -16,7 +16,7 @@
package android.widget;
-import android.content.Context;
+import android.annotation.Nullable;
import android.text.Editable;
import android.text.Selection;
import android.text.Spanned;
@@ -93,6 +93,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
// concurrently due to the asynchronous nature of onGetSuggestions.
private WordIterator mWordIterator;
+ @Nullable
private TextServicesManager mTextServicesManager;
private Runnable mSpellRunnable;
@@ -114,12 +115,12 @@ public class SpellChecker implements SpellCheckerSessionListener {
mCookie = hashCode();
}
- private void resetSession() {
+ void resetSession() {
closeSession();
- mTextServicesManager = (TextServicesManager) mTextView.getContext().
- getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
+ mTextServicesManager = mTextView.getTextServicesManagerForUser();
if (mCurrentLocale == null
+ || mTextServicesManager == null
|| mTextView.length() == 0
|| !mTextServicesManager.isSpellCheckerEnabled()
|| mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) {
@@ -226,7 +227,8 @@ public class SpellChecker implements SpellCheckerSessionListener {
start = 0;
end = mTextView.getText().length();
} else {
- final boolean spellCheckerActivated = mTextServicesManager.isSpellCheckerEnabled();
+ final boolean spellCheckerActivated =
+ mTextServicesManager != null && mTextServicesManager.isSpellCheckerEnabled();
if (isSessionActive != spellCheckerActivated) {
// Spell checker has been turned of or off since last spellCheck
resetSession();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4a6095464b42..1085e5dadb42 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -31,6 +32,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
+import android.annotation.RequiresPermission;
import android.annotation.Size;
import android.annotation.StringRes;
import android.annotation.StyleRes;
@@ -45,6 +47,7 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.UndoManager;
+import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -73,7 +76,9 @@ import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelableParcel;
+import android.os.Process;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.BoringLayout;
import android.text.DynamicLayout;
@@ -194,6 +199,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -785,6 +791,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private InputFilter[] mFilters = NO_FILTERS;
+ /**
+ * {@link UserHandle} that represents the logical owner of the text. {@code null} when it is
+ * the same as {@link Process#myUserHandle()}.
+ *
+ * <p>Most of applications should not worry about this. Some privileged apps that host UI for
+ * other apps may need to set this so that the system can use right user's resources and
+ * services such as input methods and spell checkers.</p>
+ *
+ * @see #setTextOperationUser(UserHandle)
+ */
+ @Nullable
+ private UserHandle mTextOperationUser;
+
private volatile Locale mCurrentSpellCheckerLocaleCache;
// It is possible to have a selection even when mEditor is null (programmatically set, like when
@@ -8324,6 +8343,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
}
outAttrs.hintText = mHint;
+ outAttrs.targetInputMethodUser = mTextOperationUser;
if (mText instanceof Editable) {
InputConnection ic = new EditableInputConnection(this);
outAttrs.initialSelStart = getSelectionStart();
@@ -10901,6 +10921,55 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Associate {@link UserHandle} who is considered to be the logical owner of the text shown in
+ * this {@link TextView}.
+ *
+ * <p>Most of applications should not worry about this. Some privileged apps that host UI for
+ * other apps may need to set this so that the system can user right user's resources and
+ * services such as input methods and spell checkers.</p>
+ *
+ * @param user {@link UserHandle} who is considered to be the owner of the text shown in this
+ * {@link TextView}. {@code null} to reset {@link #mTextOperationUser}.
+ * @hide
+ */
+ @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
+ public final void setTextOperationUser(@Nullable UserHandle user) {
+ if (Objects.equals(mTextOperationUser, user)) {
+ return;
+ }
+ if (user != null && !Process.myUserHandle().equals(user)) {
+ // Just for preventing people from accidentally using this hidden API without
+ // the required permission. The same permission is also checked in the system server.
+ if (getContext().checkSelfPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("INTERACT_ACROSS_USERS_FULL is required."
+ + " userId=" + user.getIdentifier()
+ + " callingUserId" + UserHandle.myUserId());
+ }
+ }
+ mTextOperationUser = user;
+ // Invalidate some resources
+ mCurrentSpellCheckerLocaleCache = null;
+ if (mEditor != null) {
+ mEditor.onTextOperationUserChanged();
+ }
+ }
+
+ @Nullable
+ final TextServicesManager getTextServicesManagerForUser() {
+ if (mTextOperationUser == null) {
+ return getContext().getSystemService(TextServicesManager.class);
+ }
+ try {
+ return getContext().createPackageContextAsUser(
+ "android", 0 /* flags */, mTextOperationUser)
+ .getSystemService(TextServicesManager.class);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
* This is a temporary method. Future versions may support multi-locale text.
* Caveat: This method may not return the latest text services locale, but this should be
* acceptable and it's more important to make this method asynchronous.
@@ -10972,8 +11041,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@UnsupportedAppUsage
private void updateTextServicesLocaleLocked() {
- final TextServicesManager textServicesManager = (TextServicesManager)
- mContext.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
+ final TextServicesManager textServicesManager = getTextServicesManagerForUser();
+ if (textServicesManager == null) {
+ return;
+ }
final SpellCheckerSubtype subtype = textServicesManager.getCurrentSpellCheckerSubtype(true);
final Locale locale;
if (subtype != null) {