diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 6526b8c4d743a..c7c00cb800205 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1430,8 +1430,13 @@ public final class InputMethodManager { public List getEnabledInputMethodSubtypeList(InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes) { try { - return mService.getEnabledInputMethodSubtypeList( - imi == null ? null : imi.getId(), allowsImplicitlySelectedSubtypes); + final Completable.InputMethodSubtypeList value = + Completable.createInputMethodSubtypeList(); + mService.getEnabledInputMethodSubtypeList( + imi == null ? null : imi.getId(), + allowsImplicitlySelectedSubtypes, + ResultCallbacks.of(value)); + return Completable.getResult(value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2970,7 +2975,9 @@ public final class InputMethodManager { */ public InputMethodSubtype getCurrentInputMethodSubtype() { try { - return mService.getCurrentInputMethodSubtype(); + final Completable.InputMethodSubtype value = Completable.createInputMethodSubtype(); + mService.getCurrentInputMethodSubtype(ResultCallbacks.of(value)); + return Completable.getResult(value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3019,7 +3026,11 @@ public final class InputMethodManager { } final List enabledSubtypes; try { - enabledSubtypes = mService.getEnabledInputMethodSubtypeList(imeId, true); + final Completable.InputMethodSubtypeList value = + Completable.createInputMethodSubtypeList(); + mService.getEnabledInputMethodSubtypeList( + imeId, true, ResultCallbacks.of(value)); + enabledSubtypes = Completable.getResult(value); } catch (RemoteException e) { return false; } @@ -3214,7 +3225,9 @@ public final class InputMethodManager { public InputMethodSubtype getLastInputMethodSubtype() { try { - return mService.getLastInputMethodSubtype(); + final Completable.InputMethodSubtype value = Completable.createInputMethodSubtype(); + mService.getLastInputMethodSubtype(ResultCallbacks.of(value)); + return Completable.getResult(value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java index a77583f87ee43..f72d0e68c38af 100644 --- a/core/java/com/android/internal/inputmethod/CallbackUtils.java +++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java @@ -19,9 +19,11 @@ package com.android.internal.inputmethod; import android.annotation.AnyThread; import android.annotation.NonNull; import android.os.RemoteException; +import android.view.inputmethod.InputMethodSubtype; import com.android.internal.view.InputBindResult; +import java.util.List; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -67,7 +69,7 @@ public final class CallbackUtils { /** * A utility method using given {@link IBooleanResultCallback} to callback the result. * - * @param callback {@link IInputBindResultResultCallback} to be called back. + * @param callback {@link IBooleanResultCallback} to be called back. * @param resultSupplier the supplier from which the result is provided. */ public static void onResult(@NonNull IBooleanResultCallback callback, @@ -89,4 +91,58 @@ public final class CallbackUtils { callback.onResult(result); } catch (RemoteException ignored) { } } + + /** + * A utility method using given {@link IInputMethodSubtypeResultCallback} to callback the + * result. + * + * @param callback {@link IInputMethodSubtypeResultCallback} to be called back. + * @param resultSupplier the supplier from which the result is provided. + */ + public static void onResult(@NonNull IInputMethodSubtypeResultCallback callback, + @NonNull Supplier resultSupplier) { + InputMethodSubtype result = null; + Throwable exception = null; + + try { + result = resultSupplier.get(); + } catch (Throwable throwable) { + exception = throwable; + } + + try { + if (exception != null) { + callback.onError(ThrowableHolder.of(exception)); + return; + } + callback.onResult(result); + } catch (RemoteException ignored) { } + } + + /** + * A utility method using given {@link IInputMethodSubtypeListResultCallback} to callback the + * result. + * + * @param callback {@link IInputMethodSubtypeListResultCallback} to be called back. + * @param resultSupplier the supplier from which the result is provided. + */ + public static void onResult(@NonNull IInputMethodSubtypeListResultCallback callback, + @NonNull Supplier> resultSupplier) { + List result = null; + Throwable exception = null; + + try { + result = resultSupplier.get(); + } catch (Throwable throwable) { + exception = throwable; + } + + try { + if (exception != null) { + callback.onError(ThrowableHolder.of(exception)); + return; + } + callback.onResult(result); + } catch (RemoteException ignored) { } + } } diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java index bd8c23e66e515..8196536428086 100644 --- a/core/java/com/android/internal/inputmethod/Completable.java +++ b/core/java/com/android/internal/inputmethod/Completable.java @@ -23,10 +23,12 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; +import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; import java.lang.annotation.Retention; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -372,6 +374,20 @@ public final class Completable { return new Completable.InputBindResult(); } + /** + * @return an instance of {@link Completable.InputMethodSubtype}. + */ + public static Completable.InputMethodSubtype createInputMethodSubtype() { + return new Completable.InputMethodSubtype(); + } + + /** + * @return an instance of {@link Completable.InputMethodSubtypeList}. + */ + public static Completable.InputMethodSubtypeList createInputMethodSubtypeList() { + return new Completable.InputMethodSubtypeList(); + } + /** * Completable object of {@link java.lang.Boolean}. */ @@ -400,6 +416,18 @@ public final class Completable { public static final class InputBindResult extends Values { } + /** + * Completable object of {@link android.view.inputmethod.InputMethodSubtype}. + */ + public static final class InputMethodSubtype + extends Values { } + + /** + * Completable object of {@link List}. + */ + public static final class InputMethodSubtypeList + extends Values> { } + /** * Await the result by the {@link Completable.Values}. * diff --git a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl new file mode 100644 index 0000000000000..619c87e1cd5b1 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 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.inputmethod; + +import android.view.inputmethod.InputMethodSubtype; +import com.android.internal.inputmethod.ThrowableHolder; + +oneway interface IInputMethodSubtypeListResultCallback { + void onResult(in List result); + void onError(in ThrowableHolder exception); +} \ No newline at end of file diff --git a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl new file mode 100644 index 0000000000000..66c09026321f9 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 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.inputmethod; + +import android.view.inputmethod.InputMethodSubtype; +import com.android.internal.inputmethod.ThrowableHolder; + +oneway interface IInputMethodSubtypeResultCallback { + void onResult(in InputMethodSubtype result); + void onError(in ThrowableHolder exception); +} \ No newline at end of file diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java index b07c5f8c9b2a8..8d95f2945c829 100644 --- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java +++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java @@ -20,10 +20,12 @@ import android.annotation.AnyThread; import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.view.inputmethod.InputMethodSubtype; import com.android.internal.view.InputBindResult; import java.lang.ref.WeakReference; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** @@ -228,4 +230,78 @@ public final class ResultCallbacks { } }; } + + /** + * Creates {@link IInputMethodSubtypeResultCallback.Stub} that is to set + * {@link Completable.InputMethodSubtype} when receiving the result. + * + * @param value {@link Completable.InputMethodSubtype} to be set when receiving the result. + * @return {@link IInputMethodSubtypeResultCallback.Stub} that can be passed as a binder + * IPC parameter. + */ + @AnyThread + public static IInputMethodSubtypeResultCallback.Stub of( + @NonNull Completable.InputMethodSubtype value) { + final AtomicReference> + atomicRef = new AtomicReference<>(new WeakReference<>(value)); + + return new IInputMethodSubtypeResultCallback.Stub() { + @BinderThread + @Override + public void onResult(InputMethodSubtype result) { + final Completable.InputMethodSubtype value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onComplete(result); + } + + @BinderThread + @Override + public void onError(ThrowableHolder throwableHolder) { + final Completable.InputMethodSubtype value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onError(throwableHolder); + } + }; + } + + /** + * Creates {@link IInputMethodSubtypeListResultCallback.Stub} that is to set + * {@link Completable.InputMethodSubtypeList} when receiving the result. + * + * @param value {@link Completable.InputMethodSubtypeList} to be set when receiving the result. + * @return {@link IInputMethodSubtypeListResultCallback.Stub} that can be passed as a binder + * IPC parameter. + */ + @AnyThread + public static IInputMethodSubtypeListResultCallback.Stub of( + @NonNull Completable.InputMethodSubtypeList value) { + final AtomicReference> + atomicRef = new AtomicReference<>(new WeakReference<>(value)); + + return new IInputMethodSubtypeListResultCallback.Stub() { + @BinderThread + @Override + public void onResult(List result) { + final Completable.InputMethodSubtypeList value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onComplete(result); + } + + @BinderThread + @Override + public void onError(ThrowableHolder throwableHolder) { + final Completable.InputMethodSubtypeList value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onError(throwableHolder); + } + }; + } } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 455e48949690c..1b5cf6ce6819e 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -26,6 +26,8 @@ import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; import com.android.internal.inputmethod.IBooleanResultCallback; import com.android.internal.inputmethod.IInputBindResultResultCallback; +import com.android.internal.inputmethod.IInputMethodSubtypeResultCallback; +import com.android.internal.inputmethod.IInputMethodSubtypeListResultCallback; /** * Public interface to the global input method manager, used by all client @@ -39,9 +41,9 @@ interface IInputMethodManager { List getInputMethodList(int userId); // TODO: Use ParceledListSlice instead List getEnabledInputMethodList(int userId); - List getEnabledInputMethodSubtypeList(in String imiId, - boolean allowsImplicitlySelectedSubtypes); - InputMethodSubtype getLastInputMethodSubtype(); + void getEnabledInputMethodSubtypeList(in String imiId, boolean allowsImplicitlySelectedSubtypes, + in IInputMethodSubtypeListResultCallback resultCallback); + void getLastInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback); void showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags, in ResultReceiver resultReceiver, in IBooleanResultCallback resultCallback); @@ -66,7 +68,7 @@ interface IInputMethodManager { int displayId); void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId); void isInputMethodPickerShownForTest(in IBooleanResultCallback resultCallback); - InputMethodSubtype getCurrentInputMethodSubtype(); + void getCurrentInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback); void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); // This is kept due to @UnsupportedAppUsage. // TODO(Bug 113914148): Consider removing this. diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 90edd77bca344..a0fa72a0d6774 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -163,6 +163,8 @@ import com.android.internal.inputmethod.IBooleanResultCallback; import com.android.internal.inputmethod.IInputBindResultResultCallback; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; +import com.android.internal.inputmethod.IInputMethodSubtypeListResultCallback; +import com.android.internal.inputmethod.IInputMethodSubtypeResultCallback; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.StartInputFlags; @@ -2159,27 +2161,34 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } /** - * @param imiId if null, returns enabled subtypes for the current imi - * @return enabled subtypes of the specified imi + * Gets enabled subtypes of the specified {@link InputMethodInfo}. + * + * @param imiId if null, returns enabled subtypes for the current {@link InputMethodInfo}. + * @param allowsImplicitlySelectedSubtypes {@code true} to return the implicitly selected + * subtypes. + * @param resultCallback to callback the result. */ @Override - public List getEnabledInputMethodSubtypeList(String imiId, - boolean allowsImplicitlySelectedSubtypes) { - final int callingUserId = UserHandle.getCallingUserId(); - synchronized (mMethodMap) { - final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId, - mSettings.getCurrentUserId(), null); - if (resolvedUserIds.length != 1) { - return Collections.emptyList(); + public void getEnabledInputMethodSubtypeList(String imiId, + boolean allowsImplicitlySelectedSubtypes, + IInputMethodSubtypeListResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, () -> { + final int callingUserId = UserHandle.getCallingUserId(); + synchronized (mMethodMap) { + final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId, + mSettings.getCurrentUserId(), null); + if (resolvedUserIds.length != 1) { + return Collections.emptyList(); + } + final long ident = Binder.clearCallingIdentity(); + try { + return getEnabledInputMethodSubtypeListLocked(imiId, + allowsImplicitlySelectedSubtypes, resolvedUserIds[0]); + } finally { + Binder.restoreCallingIdentity(ident); + } } - final long ident = Binder.clearCallingIdentity(); - try { - return getEnabledInputMethodSubtypeListLocked(imiId, - allowsImplicitlySelectedSubtypes, resolvedUserIds[0]); - } finally { - Binder.restoreCallingIdentity(ident); - } - } + }); } @GuardedBy("mMethodMap") @@ -3892,29 +3901,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public InputMethodSubtype getLastInputMethodSubtype() { - synchronized (mMethodMap) { - if (!calledFromValidUserLocked()) { - return null; - } - final Pair lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); - // TODO: Handle the case of the last IME with no subtypes - if (lastIme == null || TextUtils.isEmpty(lastIme.first) - || TextUtils.isEmpty(lastIme.second)) return null; - final InputMethodInfo lastImi = mMethodMap.get(lastIme.first); - if (lastImi == null) return null; - try { - final int lastSubtypeHash = Integer.parseInt(lastIme.second); - final int lastSubtypeId = - InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash); - if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) { + public void getLastInputMethodSubtype(IInputMethodSubtypeResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, () -> { + synchronized (mMethodMap) { + if (!calledFromValidUserLocked()) { + return null; + } + final Pair lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); + // TODO: Handle the case of the last IME with no subtypes + if (lastIme == null || TextUtils.isEmpty(lastIme.first) + || TextUtils.isEmpty(lastIme.second)) return null; + final InputMethodInfo lastImi = mMethodMap.get(lastIme.first); + if (lastImi == null) return null; + try { + final int lastSubtypeHash = Integer.parseInt(lastIme.second); + final int lastSubtypeId = + InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash); + if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) { + return null; + } + return lastImi.getSubtypeAt(lastSubtypeId); + } catch (NumberFormatException e) { return null; } - return lastImi.getSubtypeAt(lastSubtypeId); - } catch (NumberFormatException e) { - return null; } - } + }); } @Override @@ -4950,17 +4961,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } /** - * @return Return the current subtype of this input method. + * Gets the current subtype of this input method. + * + * @param resultCallback to callback the result. */ @Override - public InputMethodSubtype getCurrentInputMethodSubtype() { - synchronized (mMethodMap) { - // TODO: Make this work even for non-current users? - if (!calledFromValidUserLocked()) { - return null; + public void getCurrentInputMethodSubtype(IInputMethodSubtypeResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, () -> { + synchronized (mMethodMap) { + // TODO: Make this work even for non-current users? + if (!calledFromValidUserLocked()) { + return null; + } + return getCurrentInputMethodSubtypeLocked(); } - return getCurrentInputMethodSubtypeLocked(); - } + }); } InputMethodSubtype getCurrentInputMethodSubtypeLocked() { diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index ccb78a416386e..40ef55e9d7c33 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -75,6 +75,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.CallbackUtils; import com.android.internal.inputmethod.IBooleanResultCallback; import com.android.internal.inputmethod.IInputBindResultResultCallback; +import com.android.internal.inputmethod.IInputMethodSubtypeListResultCallback; +import com.android.internal.inputmethod.IInputMethodSubtypeResultCallback; import com.android.internal.inputmethod.IMultiClientInputMethod; import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations; import com.android.internal.inputmethod.IMultiClientInputMethodSession; @@ -1475,17 +1477,18 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public List getEnabledInputMethodSubtypeList(String imiId, - boolean allowsImplicitlySelectedSubtypes) { + public void getEnabledInputMethodSubtypeList(String imiId, + boolean allowsImplicitlySelectedSubtypes, + IInputMethodSubtypeListResultCallback resultCallback) { reportNotSupported(); - return Collections.emptyList(); + CallbackUtils.onResult(resultCallback, Collections::emptyList); } @BinderThread @Override - public InputMethodSubtype getLastInputMethodSubtype() { + public void getLastInputMethodSubtype(IInputMethodSubtypeResultCallback resultCallback) { reportNotSupported(); - return null; + CallbackUtils.onResult(resultCallback, () -> null); } @BinderThread @@ -1793,9 +1796,9 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public InputMethodSubtype getCurrentInputMethodSubtype() { + public void getCurrentInputMethodSubtype(IInputMethodSubtypeResultCallback resultCallback) { reportNotSupported(); - return null; + CallbackUtils.onResult(resultCallback, () -> null); } @BinderThread