Remove InputMethodManagerDelegate#getLockObject()
This is a follow up CL to our previous CL [1], which introduced
InputMethodManagerDelegate#getLockObject()
to clarify that "mServedView" and "mNextServedView" need to be guarded
with
InputMethodManager#mH
as a short term workaround.
With this CL, above two fields are moved back to InputMethodManager
like they used to be before we introduce ImeFocusController [2].
This is a mostly mechanical refactoring. Hopefully there should be no
observable behavior change.
[1]: I9c072b829d1db1e68b65e766d764ee71cb16e6a2
31e4678463
[2]: Ib455704fe1e9d243f93190a84f230210dbceac2a
970d9d2e0c
Bug: 244504062
Test: presubmit
Change-Id: Ie3446e1d0f62c489195e31fc7f073020884d6cae
This commit is contained in:
@@ -17,8 +17,6 @@
|
||||
package android.view;
|
||||
|
||||
import static android.view.ImeFocusControllerProto.HAS_IME_FOCUS;
|
||||
import static android.view.ImeFocusControllerProto.NEXT_SERVED_VIEW;
|
||||
import static android.view.ImeFocusControllerProto.SERVED_VIEW;
|
||||
|
||||
import android.annotation.AnyThread;
|
||||
import android.annotation.NonNull;
|
||||
@@ -31,8 +29,6 @@ import com.android.internal.inputmethod.InputMethodDebug;
|
||||
import com.android.internal.inputmethod.StartInputFlags;
|
||||
import com.android.internal.inputmethod.StartInputReason;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Responsible for IME focus handling inside {@link ViewRootImpl}.
|
||||
* @hide
|
||||
@@ -43,21 +39,6 @@ public final class ImeFocusController {
|
||||
|
||||
private final ViewRootImpl mViewRootImpl;
|
||||
private boolean mHasImeFocus = false;
|
||||
|
||||
/**
|
||||
* This is the view that should currently be served by an input method,
|
||||
* regardless of the state of setting that up.
|
||||
* @see InputMethodManagerDelegate#getLockObject()
|
||||
*/
|
||||
private View mServedView;
|
||||
|
||||
/**
|
||||
* This is the next view that will be served by the input method, when
|
||||
* we get around to updating things.
|
||||
* @see InputMethodManagerDelegate#getLockObject()
|
||||
*/
|
||||
private View mNextServedView;
|
||||
|
||||
private InputMethodManagerDelegate mDelegate;
|
||||
|
||||
@UiThread
|
||||
@@ -134,122 +115,30 @@ public final class ImeFocusController {
|
||||
windowAttribute.softInputMode));
|
||||
}
|
||||
|
||||
boolean forceFocus = false;
|
||||
final InputMethodManagerDelegate immDelegate = getImmDelegate();
|
||||
synchronized (immDelegate.getLockObject()) {
|
||||
// Update mNextServedView when focusedView changed.
|
||||
onViewFocusChanged(viewForWindowFocus, true);
|
||||
|
||||
// Starting new input when the next focused view is same as served view but the
|
||||
// currently active connection (if any) is not associated with it.
|
||||
final boolean nextFocusIsServedView = mServedView == viewForWindowFocus;
|
||||
|
||||
if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) {
|
||||
forceFocus = true;
|
||||
}
|
||||
}
|
||||
|
||||
immDelegate.startInputOnWindowFocusGain(viewForWindowFocus,
|
||||
windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
|
||||
getImmDelegate().onPostWindowFocus(viewForWindowFocus, windowAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see InputMethodManager#checkFocus()
|
||||
*/
|
||||
public boolean checkFocus(boolean forceNewFocus, boolean startInput) {
|
||||
final InputMethodManagerDelegate immDelegate = getImmDelegate();
|
||||
synchronized (immDelegate.getLockObject()) {
|
||||
if (!immDelegate.isCurrentRootView(mViewRootImpl)) {
|
||||
return false;
|
||||
}
|
||||
if (mServedView == mNextServedView && !forceNewFocus) {
|
||||
return false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "checkFocus: view=" + mServedView
|
||||
+ " next=" + mNextServedView
|
||||
+ " force=" + forceNewFocus
|
||||
+ " package="
|
||||
+ (mServedView != null ? mServedView.getContext().getPackageName()
|
||||
: "<none>"));
|
||||
}
|
||||
// Close the connection when no next served view coming.
|
||||
if (mNextServedView == null) {
|
||||
immDelegate.finishInput();
|
||||
immDelegate.closeCurrentIme();
|
||||
return false;
|
||||
}
|
||||
mServedView = mNextServedView;
|
||||
immDelegate.finishComposingText();
|
||||
}
|
||||
|
||||
if (startInput) {
|
||||
immDelegate.startInput(StartInputReason.CHECK_FOCUS, null /* focusedView */,
|
||||
0 /* startInputFlags */, 0 /* softInputMode */, 0 /* windowFlags */);
|
||||
}
|
||||
return true;
|
||||
return getImmDelegate().checkFocus(forceNewFocus, startInput, mViewRootImpl);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onViewFocusChanged(View view, boolean hasFocus) {
|
||||
if (view == null || view.isTemporarilyDetached()) {
|
||||
return;
|
||||
}
|
||||
final InputMethodManagerDelegate immDelegate = getImmDelegate();
|
||||
synchronized (immDelegate.getLockObject()) {
|
||||
if (!immDelegate.isCurrentRootView(view.getViewRootImpl())) {
|
||||
return;
|
||||
}
|
||||
if (!view.hasImeFocus() || !view.hasWindowFocus()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view)
|
||||
+ ", mServedView=" + InputMethodDebug.dumpViewInfo(mServedView));
|
||||
}
|
||||
|
||||
// We don't need to track the next served view when the view lost focus here because:
|
||||
// 1) The current view focus may be cleared temporary when in touch mode, closing input
|
||||
// at this moment isn't the right way.
|
||||
// 2) We only care about the served view change when it focused, since changing input
|
||||
// connection when the focus target changed is reasonable.
|
||||
// 3) Setting the next served view as null when no more served view should be handled in
|
||||
// other special events (e.g. view detached from window or the window dismissed).
|
||||
if (hasFocus) {
|
||||
mNextServedView = view;
|
||||
}
|
||||
}
|
||||
mViewRootImpl.dispatchCheckFocus();
|
||||
getImmDelegate().onViewFocusChanged(view, hasFocus);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onViewDetachedFromWindow(View view) {
|
||||
final InputMethodManagerDelegate immDelegate = getImmDelegate();
|
||||
synchronized (immDelegate.getLockObject()) {
|
||||
if (!immDelegate.isCurrentRootView(view.getViewRootImpl())) {
|
||||
return;
|
||||
}
|
||||
if (mNextServedView == view) {
|
||||
mNextServedView = null;
|
||||
}
|
||||
if (mServedView == view) {
|
||||
mViewRootImpl.dispatchCheckFocus();
|
||||
}
|
||||
}
|
||||
getImmDelegate().onViewDetachedFromWindow(view, mViewRootImpl);
|
||||
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onWindowDismissed() {
|
||||
final InputMethodManagerDelegate immDelegate = getImmDelegate();
|
||||
synchronized (immDelegate.getLockObject()) {
|
||||
if (!immDelegate.isCurrentRootView(mViewRootImpl)) {
|
||||
return;
|
||||
}
|
||||
if (mServedView != null) {
|
||||
immDelegate.finishInput();
|
||||
}
|
||||
immDelegate.setCurrentRootView(null);
|
||||
}
|
||||
getImmDelegate().onWindowDismissed(mViewRootImpl);
|
||||
mHasImeFocus = false;
|
||||
}
|
||||
|
||||
@@ -302,75 +191,21 @@ public final class ImeFocusController {
|
||||
public interface InputMethodManagerDelegate {
|
||||
/**
|
||||
* Starts the input connection.
|
||||
* Note that this method must not hold the {@link InputMethodManager} lock with
|
||||
* {@link InputMethodManagerDelegate#getLockObject()} while {@link InputMethodManager}
|
||||
* calling into app-code in different threads.
|
||||
*/
|
||||
boolean startInput(@StartInputReason int startInputReason, View focusedView,
|
||||
@StartInputFlags int startInputFlags,
|
||||
@WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags);
|
||||
/**
|
||||
* Starts the input connection when gaining the window focus.
|
||||
* Note that this method must not hold the {@link InputMethodManager} lock with
|
||||
* {@link InputMethodManagerDelegate#getLockObject()} while {@link InputMethodManager}
|
||||
* calling into app-code in different threads.
|
||||
*/
|
||||
void startInputOnWindowFocusGain(View rootView,
|
||||
@WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
|
||||
boolean forceNewFocus);
|
||||
void finishInput();
|
||||
|
||||
void onPostWindowFocus(View viewForWindowFocus,
|
||||
@NonNull WindowManager.LayoutParams windowAttribute);
|
||||
void onViewFocusChanged(@NonNull View view, boolean hasFocus);
|
||||
boolean checkFocus(boolean forceNewFocus, boolean startInput, ViewRootImpl viewRootImpl);
|
||||
void onViewDetachedFromWindow(View view, ViewRootImpl viewRootImpl);
|
||||
void onWindowDismissed(ViewRootImpl viewRootImpl);
|
||||
|
||||
void finishInputAndReportToIme();
|
||||
void closeCurrentIme();
|
||||
void finishComposingText();
|
||||
void setCurrentRootView(ViewRootImpl rootView);
|
||||
boolean isCurrentRootView(ViewRootImpl rootView);
|
||||
boolean hasActiveConnection(View view);
|
||||
|
||||
/**
|
||||
* Returns the {@code InputMethodManager#mH} lock object.
|
||||
* Used for {@link ImeFocusController} to guard the served view being accessed by
|
||||
* {@link InputMethodManager} in different threads.
|
||||
*/
|
||||
Object getLockObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns The current IME served view for {@link InputMethodManager}.
|
||||
* Used to start input connection or check the caller's validity when calling
|
||||
* {@link InputMethodManager} APIs.
|
||||
* Note that this method requires to be called inside {@code InputMethodManager#mH} lock for
|
||||
* data consistency.
|
||||
*/
|
||||
public View getServedViewLocked() {
|
||||
return mServedView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns The next incoming IME served view for {@link InputMethodManager}.
|
||||
* Note that this method requires to be called inside {@code InputMethodManager#mH} lock for
|
||||
* data consistency.
|
||||
*/
|
||||
public View getNextServedViewLocked() {
|
||||
return mNextServedView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the served & the next served view when the controller triggers
|
||||
* {@link InputMethodManagerDelegate#finishInput()} or
|
||||
* {@link InputMethodManagerDelegate#finishInputAndReportToIme()}.
|
||||
* Note that this method requires to be called inside {@code InputMethodManager#mH} lock for
|
||||
* data consistency.
|
||||
*
|
||||
* @return The {@code mServedView} that has cleared, or {@code null} means nothing to clear.
|
||||
*/
|
||||
public View clearServedViewsLocked() {
|
||||
View clearedView = null;
|
||||
mNextServedView = null;
|
||||
if (mServedView != null) {
|
||||
clearedView = mServedView;
|
||||
mServedView = null;
|
||||
}
|
||||
return clearedView;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,8 +219,6 @@ public final class ImeFocusController {
|
||||
void dumpDebug(ProtoOutputStream proto, long fieldId) {
|
||||
final long token = proto.start(fieldId);
|
||||
proto.write(HAS_IME_FOCUS, mHasImeFocus);
|
||||
proto.write(SERVED_VIEW, Objects.toString(mServedView));
|
||||
proto.write(NEXT_SERVED_VIEW, Objects.toString(mNextServedView));
|
||||
proto.end(token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewRootImpl;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
|
||||
import android.view.autofill.AutofillId;
|
||||
import android.view.autofill.AutofillManager;
|
||||
@@ -456,6 +457,22 @@ public final class InputMethodManager {
|
||||
|
||||
// -----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This is the view that should currently be served by an input method,
|
||||
* regardless of the state of setting that up.
|
||||
*/
|
||||
@Nullable
|
||||
@GuardedBy("mH")
|
||||
private View mServedView;
|
||||
|
||||
/**
|
||||
* This is the next view that will be served by the input method, when
|
||||
* we get around to updating things.
|
||||
*/
|
||||
@Nullable
|
||||
@GuardedBy("mH")
|
||||
private View mNextServedView;
|
||||
|
||||
/**
|
||||
* This is the root view of the overall window that currently has input
|
||||
* method focus.
|
||||
@@ -691,8 +708,7 @@ public final class InputMethodManager {
|
||||
if (mCurRootView == null) {
|
||||
return null;
|
||||
}
|
||||
final View servedView = mCurRootView.getImeFocusController().getServedViewLocked();
|
||||
return servedView != null ? servedView.getContext() : null;
|
||||
return mServedView != null ? mServedView.getContext() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,11 +743,7 @@ public final class InputMethodManager {
|
||||
startInputFlags, softInputMode, windowFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link ImeFocusController} to finish input connection.
|
||||
*/
|
||||
@Override
|
||||
public void finishInput() {
|
||||
private void finishInput() {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"InputMethodManager.DelegateImpl#finishInput", InputMethodManager.this,
|
||||
null /* icProto */);
|
||||
@@ -760,19 +772,28 @@ public final class InputMethodManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link ImeFocusController} to hide current input method editor.
|
||||
*/
|
||||
@Override
|
||||
public void closeCurrentIme() {
|
||||
closeCurrentInput();
|
||||
public void onPostWindowFocus(View viewForWindowFocus,
|
||||
@NonNull WindowManager.LayoutParams windowAttribute) {
|
||||
boolean forceFocus = false;
|
||||
synchronized (mH) {
|
||||
// Update mNextServedView when focusedView changed.
|
||||
onViewFocusChanged(viewForWindowFocus, true);
|
||||
|
||||
// Starting new input when the next focused view is same as served view but the
|
||||
// currently active connection (if any) is not associated with it.
|
||||
final boolean nextFocusIsServedView = mServedView == viewForWindowFocus;
|
||||
|
||||
if (nextFocusIsServedView
|
||||
&& !hasActiveInputConnectionInternal(viewForWindowFocus)) {
|
||||
forceFocus = true;
|
||||
}
|
||||
}
|
||||
startInputOnWindowFocusGain(viewForWindowFocus,
|
||||
windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
|
||||
}
|
||||
|
||||
/**
|
||||
* For {@link ImeFocusController} to start input when gaining the window focus.
|
||||
*/
|
||||
@Override
|
||||
public void startInputOnWindowFocusGain(View focusedView,
|
||||
private void startInputOnWindowFocusGain(View focusedView,
|
||||
@SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) {
|
||||
int startInputFlags = getStartInputFlags(focusedView, 0);
|
||||
startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS;
|
||||
@@ -808,8 +829,7 @@ public final class InputMethodManager {
|
||||
synchronized (mH) {
|
||||
// For some reason we didn't do a startInput + windowFocusGain, so
|
||||
// we'll just do a window focus gain and call it a day.
|
||||
View servedView = controller.getServedViewLocked();
|
||||
boolean nextFocusHasConnection = servedView != null && servedView == focusedView
|
||||
boolean nextFocusHasConnection = mServedView != null && mServedView == focusedView
|
||||
&& hasActiveInputConnectionInternal(focusedView);
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "Reporting focus gain, without startInput"
|
||||
@@ -831,16 +851,102 @@ public final class InputMethodManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link ImeFocusController} to finish current composing text.
|
||||
*/
|
||||
@Override
|
||||
public void finishComposingText() {
|
||||
public void onViewFocusChanged(@Nullable View view, boolean hasFocus) {
|
||||
if (view == null || view.isTemporarilyDetached()) {
|
||||
return;
|
||||
}
|
||||
final ViewRootImpl viewRootImpl = view.getViewRootImpl();
|
||||
synchronized (mH) {
|
||||
if (mCurRootView != viewRootImpl) {
|
||||
return;
|
||||
}
|
||||
if (!view.hasImeFocus() || !view.hasWindowFocus()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view));
|
||||
}
|
||||
|
||||
// We don't need to track the next served view when the view lost focus here
|
||||
// because:
|
||||
// 1) The current view focus may be cleared temporary when in touch mode, closing
|
||||
// input at this moment isn't the right way.
|
||||
// 2) We only care about the served view change when it focused, since changing
|
||||
// input connection when the focus target changed is reasonable.
|
||||
// 3) Setting the next served view as null when no more served view should be
|
||||
// handled in other special events (e.g. view detached from window or the window
|
||||
// dismissed).
|
||||
if (hasFocus) {
|
||||
mNextServedView = view;
|
||||
}
|
||||
}
|
||||
viewRootImpl.dispatchCheckFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkFocus(boolean forceNewFocus, boolean startInput,
|
||||
ViewRootImpl viewRootImpl) {
|
||||
synchronized (mH) {
|
||||
if (mCurRootView != viewRootImpl) {
|
||||
return false;
|
||||
}
|
||||
if (mServedView == mNextServedView && !forceNewFocus) {
|
||||
return false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "checkFocus: view=" + mServedView
|
||||
+ " next=" + mNextServedView
|
||||
+ " force=" + forceNewFocus
|
||||
+ " package="
|
||||
+ (mServedView != null ? mServedView.getContext().getPackageName()
|
||||
: "<none>"));
|
||||
}
|
||||
// Close the connection when no next served view coming.
|
||||
if (mNextServedView == null) {
|
||||
finishInput();
|
||||
closeCurrentInput();
|
||||
return false;
|
||||
}
|
||||
mServedView = mNextServedView;
|
||||
if (mServedInputConnection != null) {
|
||||
mServedInputConnection.finishComposingTextFromImm();
|
||||
}
|
||||
}
|
||||
|
||||
if (startInput) {
|
||||
startInput(StartInputReason.CHECK_FOCUS, null /* focusedView */,
|
||||
0 /* startInputFlags */, 0 /* softInputMode */, 0 /* windowFlags */);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View view, ViewRootImpl viewRootImpl) {
|
||||
synchronized (mH) {
|
||||
if (mCurRootView != view.getViewRootImpl()) {
|
||||
return;
|
||||
}
|
||||
if (mNextServedView == view) {
|
||||
mNextServedView = null;
|
||||
}
|
||||
if (mServedView == view) {
|
||||
viewRootImpl.dispatchCheckFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowDismissed(ViewRootImpl viewRootImpl) {
|
||||
synchronized (mH) {
|
||||
if (mCurRootView != viewRootImpl) {
|
||||
return;
|
||||
}
|
||||
if (mServedView != null) {
|
||||
finishInput();
|
||||
}
|
||||
setCurrentRootView(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -865,26 +971,6 @@ public final class InputMethodManager {
|
||||
return mCurRootView == rootView;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the active input connection (if any) is for the given view.
|
||||
*
|
||||
* @see #hasActiveInputConnectionInternal(View)}
|
||||
*/
|
||||
@Override
|
||||
public boolean hasActiveConnection(View view) {
|
||||
return hasActiveInputConnectionInternal(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link InputMethodManager#mH} lock object.
|
||||
* Used for {@link ImeFocusController} to guard the served view being accessed by
|
||||
* {@link InputMethodManager} in different threads.
|
||||
*/
|
||||
@Override
|
||||
public Object getLockObject() {
|
||||
return mH;
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@@ -938,14 +1024,12 @@ public final class InputMethodManager {
|
||||
|
||||
@GuardedBy("mH")
|
||||
private View getServedViewLocked() {
|
||||
return mCurRootView != null ? mCurRootView.getImeFocusController().getServedViewLocked()
|
||||
: null;
|
||||
return mCurRootView != null ? mServedView : null;
|
||||
}
|
||||
|
||||
@GuardedBy("mH")
|
||||
private View getNextServedViewLocked() {
|
||||
return mCurRootView != null ? mCurRootView.getImeFocusController().getNextServedViewLocked()
|
||||
: null;
|
||||
return mCurRootView != null ? mNextServedView : null;
|
||||
}
|
||||
|
||||
private ImeFocusController getFocusController() {
|
||||
@@ -1794,8 +1878,12 @@ public final class InputMethodManager {
|
||||
@GuardedBy("mH")
|
||||
void finishInputLocked() {
|
||||
mVirtualDisplayToScreenMatrix = null;
|
||||
final ImeFocusController controller = getFocusController();
|
||||
final View clearedView = controller != null ? controller.clearServedViewsLocked() : null;
|
||||
View clearedView = null;
|
||||
mNextServedView = null;
|
||||
if (mServedView != null) {
|
||||
clearedView = mServedView;
|
||||
mServedView = null;
|
||||
}
|
||||
if (clearedView != null) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "FINISH INPUT: mServedView="
|
||||
|
||||
Reference in New Issue
Block a user