add local focus mode and input event injection API to Window
- This enables keyboard navigation for window without focus. - FLAG_LOCAL_FOCUS_MODE puts window into local focus mode. - Application needs to put window in local focus mode, control focus, and inject events to make dpad navigation work. - Window in local focus mode does not interact with window manager or ime regarding focus related events. - Also renamed ViewRootImpl.dispatchKey to dispatchInputEvent to allow both key and touch events injection. Change-Id: I8e8561f29e0dade3797fb7ae3ee7690e6b7f8895
This commit is contained in:
@@ -27827,6 +27827,7 @@ package android.view {
|
||||
method public final boolean hasChildren();
|
||||
method public boolean hasFeature(int);
|
||||
method protected final boolean hasSoftInputMode();
|
||||
method public void injectInputEvent(android.view.InputEvent);
|
||||
method public abstract void invalidatePanelMenu(int);
|
||||
method public final boolean isActive();
|
||||
method public abstract boolean isFloating();
|
||||
@@ -27864,6 +27865,7 @@ package android.view {
|
||||
method public void setGravity(int);
|
||||
method public void setIcon(int);
|
||||
method public void setLayout(int, int);
|
||||
method public void setLocalFocus(boolean, boolean);
|
||||
method public void setLogo(int);
|
||||
method public void setSoftInputMode(int);
|
||||
method public abstract void setTitle(java.lang.CharSequence);
|
||||
@@ -28002,6 +28004,7 @@ package android.view {
|
||||
field public static final int FLAG_LAYOUT_IN_OVERSCAN = 33554432; // 0x2000000
|
||||
field public static final int FLAG_LAYOUT_IN_SCREEN = 256; // 0x100
|
||||
field public static final int FLAG_LAYOUT_NO_LIMITS = 512; // 0x200
|
||||
field public static final int FLAG_LOCAL_FOCUS_MODE = 268435456; // 0x10000000
|
||||
field public static final int FLAG_NOT_FOCUSABLE = 8; // 0x8
|
||||
field public static final int FLAG_NOT_TOUCHABLE = 16; // 0x10
|
||||
field public static final int FLAG_NOT_TOUCH_MODAL = 32; // 0x20
|
||||
|
||||
@@ -608,6 +608,11 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether the window is in local focus mode or not */
|
||||
private boolean isInLocalFocusMode() {
|
||||
return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
|
||||
}
|
||||
|
||||
void destroyHardwareResources() {
|
||||
if (mAttachInfo.mHardwareRenderer != null) {
|
||||
if (mAttachInfo.mHardwareRenderer.isEnabled()) {
|
||||
@@ -1818,7 +1823,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
mNewSurfaceNeeded = false;
|
||||
mViewVisibility = viewVisibility;
|
||||
|
||||
if (mAttachInfo.mHasWindowFocus) {
|
||||
if (mAttachInfo.mHasWindowFocus && !isInLocalFocusMode()) {
|
||||
final boolean imTarget = WindowManager.LayoutParams
|
||||
.mayUseInputMethod(mWindowAttributes.flags);
|
||||
if (imTarget != mLastWasImTarget) {
|
||||
@@ -2906,7 +2911,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
private final static int MSG_RESIZED = 4;
|
||||
private final static int MSG_RESIZED_REPORT = 5;
|
||||
private final static int MSG_WINDOW_FOCUS_CHANGED = 6;
|
||||
private final static int MSG_DISPATCH_KEY = 7;
|
||||
private final static int MSG_DISPATCH_INPUT_EVENT = 7;
|
||||
private final static int MSG_DISPATCH_APP_VISIBILITY = 8;
|
||||
private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9;
|
||||
private final static int MSG_DISPATCH_KEY_FROM_IME = 11;
|
||||
@@ -2941,8 +2946,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
return "MSG_RESIZED_REPORT";
|
||||
case MSG_WINDOW_FOCUS_CHANGED:
|
||||
return "MSG_WINDOW_FOCUS_CHANGED";
|
||||
case MSG_DISPATCH_KEY:
|
||||
return "MSG_DISPATCH_KEY";
|
||||
case MSG_DISPATCH_INPUT_EVENT:
|
||||
return "MSG_DISPATCH_INPUT_EVENT";
|
||||
case MSG_DISPATCH_APP_VISIBILITY:
|
||||
return "MSG_DISPATCH_APP_VISIBILITY";
|
||||
case MSG_DISPATCH_GET_NEW_SURFACE:
|
||||
@@ -3092,7 +3097,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
InputMethodManager imm = InputMethodManager.peekInstance();
|
||||
if (mView != null) {
|
||||
if (hasWindowFocus && imm != null && mLastWasImTarget) {
|
||||
if (hasWindowFocus && imm != null && mLastWasImTarget &&
|
||||
!isInLocalFocusMode()) {
|
||||
imm.startGettingWindowFocus(mView);
|
||||
}
|
||||
mAttachInfo.mKeyDispatchState.reset();
|
||||
@@ -3103,7 +3109,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
// Note: must be done after the focus change callbacks,
|
||||
// so all of the view state is set up correctly.
|
||||
if (hasWindowFocus) {
|
||||
if (imm != null && mLastWasImTarget) {
|
||||
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
|
||||
imm.onWindowFocus(mView, mView.findFocus(),
|
||||
mWindowAttributes.softInputMode,
|
||||
!mHasHadWindowFocus, mWindowAttributes.flags);
|
||||
@@ -3131,8 +3137,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
case MSG_DIE:
|
||||
doDie();
|
||||
break;
|
||||
case MSG_DISPATCH_KEY: {
|
||||
KeyEvent event = (KeyEvent)msg.obj;
|
||||
case MSG_DISPATCH_INPUT_EVENT: {
|
||||
InputEvent event = (InputEvent)msg.obj;
|
||||
enqueueInputEvent(event, null, 0, true);
|
||||
} break;
|
||||
case MSG_DISPATCH_KEY_FROM_IME: {
|
||||
@@ -3222,7 +3228,9 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
// tell the window manager
|
||||
try {
|
||||
mWindowSession.setInTouchMode(inTouchMode);
|
||||
if (!isInLocalFocusMode()) {
|
||||
mWindowSession.setInTouchMode(inTouchMode);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -3624,7 +3632,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
@Override
|
||||
protected int onProcess(QueuedInputEvent q) {
|
||||
if (mLastWasImTarget) {
|
||||
if (mLastWasImTarget && !isInLocalFocusMode()) {
|
||||
InputMethodManager imm = InputMethodManager.peekInstance();
|
||||
if (imm != null) {
|
||||
final InputEvent event = q.mEvent;
|
||||
@@ -5666,8 +5674,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
mInvalidateOnAnimationRunnable.removeView(view);
|
||||
}
|
||||
|
||||
public void dispatchKey(KeyEvent event) {
|
||||
Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY, event);
|
||||
public void dispatchInputEvent(InputEvent event) {
|
||||
Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, event);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
@@ -5697,7 +5705,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
flags, event.getSource(), null);
|
||||
fallbackAction.recycle();
|
||||
|
||||
dispatchKey(fallbackEvent);
|
||||
dispatchInputEvent(fallbackEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1290,4 +1290,18 @@ public abstract class Window {
|
||||
* @hide
|
||||
*/
|
||||
public void setDefaultLogo(int resId) { }
|
||||
|
||||
/**
|
||||
* Set focus locally. The window should have the
|
||||
* {@link WindowManager.LayoutParams#FLAG_LOCAL_FOCUS_MODE} flag set already.
|
||||
* @param hasFocus Whether this window has focus or not.
|
||||
* @param inTouchMode Whether this window is in touch mode or not.
|
||||
*/
|
||||
public void setLocalFocus(boolean hasFocus, boolean inTouchMode) { }
|
||||
|
||||
/**
|
||||
* Inject an event to window locally.
|
||||
* @param event A key or touch event to inject to this window.
|
||||
*/
|
||||
public void injectInputEvent(InputEvent event) { }
|
||||
}
|
||||
|
||||
@@ -866,6 +866,15 @@ public interface WindowManager extends ViewManager {
|
||||
*/
|
||||
public static final int FLAG_NEEDS_MENU_KEY = 0x08000000;
|
||||
|
||||
/**
|
||||
* Flag for a window in local focus mode.
|
||||
* Window in local focus mode can control focus independent of window manager using
|
||||
* {@link Window#setLocalFocus(boolean, boolean)}.
|
||||
* Usually window in this mode will not get touch/key events from window manager, but will
|
||||
* get events only via local injection using {@link Window#injectInputEvent(InputEvent)}.
|
||||
*/
|
||||
public static final int FLAG_LOCAL_FOCUS_MODE = 0x10000000;
|
||||
|
||||
/** Window flag: special flag to limit the size of the window to be
|
||||
* original size ([320x480] x density). Used to create window for applications
|
||||
* running under compatibility mode.
|
||||
@@ -905,6 +914,7 @@ public interface WindowManager extends ViewManager {
|
||||
* @see #FLAG_DISMISS_KEYGUARD
|
||||
* @see #FLAG_SPLIT_TOUCH
|
||||
* @see #FLAG_HARDWARE_ACCELERATED
|
||||
* @see #FLAG_LOCAL_FOCUS_MODE
|
||||
*/
|
||||
@ViewDebug.ExportedProperty(flagMapping = {
|
||||
@ViewDebug.FlagToString(mask = FLAG_ALLOW_LOCK_WHILE_SCREEN_ON, equals = FLAG_ALLOW_LOCK_WHILE_SCREEN_ON,
|
||||
@@ -956,7 +966,9 @@ public interface WindowManager extends ViewManager {
|
||||
@ViewDebug.FlagToString(mask = FLAG_SPLIT_TOUCH, equals = FLAG_SPLIT_TOUCH,
|
||||
name = "FLAG_SPLIT_TOUCH"),
|
||||
@ViewDebug.FlagToString(mask = FLAG_HARDWARE_ACCELERATED, equals = FLAG_HARDWARE_ACCELERATED,
|
||||
name = "FLAG_HARDWARE_ACCELERATED")
|
||||
name = "FLAG_HARDWARE_ACCELERATED"),
|
||||
@ViewDebug.FlagToString(mask = FLAG_LOCAL_FOCUS_MODE, equals = FLAG_LOCAL_FOCUS_MODE,
|
||||
name = "FLAG_LOCAL_FOCUS_MODE")
|
||||
})
|
||||
public int flags;
|
||||
|
||||
|
||||
@@ -503,7 +503,7 @@ public class ZoomButtonsController implements View.OnTouchListener {
|
||||
|
||||
ViewRootImpl viewRoot = mOwnerView.getViewRootImpl();
|
||||
if (viewRoot != null) {
|
||||
viewRoot.dispatchKey(event);
|
||||
viewRoot.dispatchInputEvent(event);
|
||||
}
|
||||
|
||||
// We gave the key to the owner, don't let the container handle this key
|
||||
|
||||
@@ -184,7 +184,7 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
|
||||
KeyEvent event = events[i];
|
||||
event = KeyEvent.changeFlags(event, event.getFlags()
|
||||
| KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
|
||||
viewRootImpl.dispatchKey(event);
|
||||
viewRootImpl.dispatchInputEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ import android.view.ContextThemeWrapper;
|
||||
import android.view.Gravity;
|
||||
import android.view.IRotationWatcher;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.InputEvent;
|
||||
import android.view.InputQueue;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
@@ -79,6 +80,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewManager;
|
||||
import android.view.ViewParent;
|
||||
import android.view.ViewRootImpl;
|
||||
import android.view.ViewStub;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
@@ -1459,6 +1461,27 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalFocus(boolean hasFocus, boolean inTouchMode) {
|
||||
getViewRootImpl().windowFocusChanged(hasFocus, inTouchMode);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectInputEvent(InputEvent event) {
|
||||
getViewRootImpl().dispatchInputEvent(event);
|
||||
}
|
||||
|
||||
private ViewRootImpl getViewRootImpl() {
|
||||
if (mDecor != null) {
|
||||
ViewRootImpl viewRootImpl = mDecor.getViewRootImpl();
|
||||
if (viewRootImpl != null) {
|
||||
return viewRootImpl;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("view not added");
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that key events come to this activity. Use this if your activity
|
||||
* has no views with focus, but the activity still wants a chance to process
|
||||
|
||||
Reference in New Issue
Block a user