Merge "Propogate per-display IME status to SysUI" into qt-dev

am: f2087397ef

Change-Id: I1c8d3a110050cff9c413da2fdd689458c5bc8796
This commit is contained in:
lumark
2019-04-19 12:23:51 -07:00
committed by android-build-merger
9 changed files with 254 additions and 30 deletions

View File

@@ -48,8 +48,7 @@ interface IStatusBarService
void setIconVisibility(String slot, boolean visible);
@UnsupportedAppUsage
void removeIcon(String slot);
// TODO(b/117478341): support back button change when IME is showing on a external display.
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);

View File

@@ -18,7 +18,10 @@ package com.android.systemui.statusbar;
import static android.app.StatusBarManager.DISABLE2_NONE;
import static android.app.StatusBarManager.DISABLE_NONE;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
@@ -40,6 +43,7 @@ import android.os.Looper;
import android.os.Message;
import android.util.Pair;
import android.util.SparseArray;
import android.view.inputmethod.InputMethodSystemProperty;
import androidx.annotation.VisibleForTesting;
@@ -127,6 +131,11 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
private Handler mHandler = new H(Looper.getMainLooper());
/** A map of display id - disable flag pair */
private SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
/**
* The last ID of the display where IME window for which we received setImeWindowStatus
* event.
*/
private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
/**
* These methods are called back on the main thread.
@@ -785,6 +794,32 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
}
private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
if (displayId == INVALID_DISPLAY) return;
if (!InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED
&& mLastUpdatedImeDisplayId != displayId
&& mLastUpdatedImeDisplayId != INVALID_DISPLAY) {
// Set previous NavBar's IME window status as invisible when IME
// window switched to another display for single-session IME case.
sendImeInvisibleStatusForPrevNavBar();
}
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setImeWindowStatus(displayId, token, vis, backDisposition,
showImeSwitcher);
}
mLastUpdatedImeDisplayId = displayId;
}
private void sendImeInvisibleStatusForPrevNavBar() {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setImeWindowStatus(mLastUpdatedImeDisplayId,
null /* token */, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT,
false /* showImeSwitcher */);
}
}
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -852,10 +887,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
break;
case MSG_SHOW_IME_BUTTON:
args = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setImeWindowStatus(args.argi1, (IBinder) args.arg1,
args.argi2, args.argi3, args.argi4 != 0 /* showImeSwitcher */);
}
handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
args.argi2 /* vis */, args.argi3 /* backDisposition */,
args.argi4 != 0 /* showImeSwitcher */);
break;
case MSG_SHOW_RECENT_APPS:
for (int i = 0; i < mCallbacks.size(); i++) {

View File

@@ -1063,4 +1063,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
return navigationBarView;
}
@VisibleForTesting
int getNavigationIconHints() {
return mNavigationIconHints;
}
}

View File

@@ -14,18 +14,38 @@
package com.android.systemui.statusbar.phone;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.LayoutRes;
import android.annotation.Nullable;
import android.app.Fragment;
import android.app.FragmentController;
import android.app.FragmentHostCallback;
import android.content.Context;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.LeakCheck.Tracker;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
@@ -34,6 +54,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
@@ -50,9 +71,16 @@ import org.junit.runner.RunWith;
@RunWithLooper()
@SmallTest
public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
private static final int EXTERNAL_DISPLAY_ID = 2;
private static final int NAV_BAR_VIEW_ID = 43;
private Fragment mFragmentExternalDisplay;
private FragmentController mControllerExternalDisplay;
private SysuiTestableContext mSysuiTestableContextExternal;
private OverviewProxyService mOverviewProxyService =
mDependency.injectMockDependency(OverviewProxyService.class);
private CommandQueue mCommandQueue;
private AccessibilityManagerWrapper mAccessibilityWrapper =
new AccessibilityManagerWrapper(mContext) {
Tracker mTracker = mLeakCheck.getTracker("accessibility_manager");
@@ -73,15 +101,51 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
}
protected void createRootView() {
mView = new NavigationBarFrame(mContext);
mView = new NavigationBarFrame(mSysuiContext);
mView.setId(NAV_BAR_VIEW_ID);
}
@Before
public void setup() {
mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
public void setupFragment() throws Exception {
setupSysuiDependency();
createRootView();
TestableLooper.get(this).runWithLooper(() -> {
mHandler = new Handler();
mFragment = instantiate(mSysuiContext, NavigationBarFragment.class.getName(), null);
mFragments = FragmentController.createController(
new HostCallbacksForExternalDisplay(mSysuiContext));
mFragments.attachHost(null);
mFragments.getFragmentManager().beginTransaction()
.replace(NAV_BAR_VIEW_ID, mFragment)
.commit();
mControllerExternalDisplay = FragmentController.createController(
new HostCallbacksForExternalDisplay(mSysuiTestableContextExternal));
mControllerExternalDisplay.attachHost(null);
mFragmentExternalDisplay = instantiate(mSysuiTestableContextExternal,
NavigationBarFragment.class.getName(), null);
mControllerExternalDisplay.getFragmentManager().beginTransaction()
.replace(NAV_BAR_VIEW_ID, mFragmentExternalDisplay)
.commit();
});
}
private void setupSysuiDependency() {
mCommandQueue = new CommandQueue(mContext);
mSysuiContext.putComponent(CommandQueue.class, mCommandQueue);
mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
mSysuiContext.putComponent(Recents.class, mock(Recents.class));
mSysuiContext.putComponent(Divider.class, mock(Divider.class));
Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
mSysuiTestableContextExternal = (SysuiTestableContext) mSysuiContext.createDisplayContext(
display);
mSysuiTestableContextExternal.putComponent(CommandQueue.class, mCommandQueue);
mSysuiTestableContextExternal.putComponent(StatusBar.class, mock(StatusBar.class));
mSysuiTestableContextExternal.putComponent(Recents.class, mock(Recents.class));
mSysuiTestableContextExternal.putComponent(Divider.class, mock(Divider.class));
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
WindowManager windowManager = mock(WindowManager.class);
Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
@@ -102,15 +166,111 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
navigationBarFragment.onHomeLongClick(navigationBarFragment.getView());
}
@Test
public void testSetImeWindowStatusWhenImeSwitchOnDisplay() {
// Create default & external NavBar fragment.
NavigationBarFragment defaultNavBar = (NavigationBarFragment) mFragment;
NavigationBarFragment externalNavBar = (NavigationBarFragment) mFragmentExternalDisplay;
mFragments.dispatchCreate();
processAllMessages();
mFragments.dispatchResume();
processAllMessages();
mControllerExternalDisplay.dispatchCreate();
processAllMessages();
mControllerExternalDisplay.dispatchResume();
processAllMessages();
// Set IME window status for default NavBar.
mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
Handler.getMain().runWithScissors(() -> { }, 500);
// Verify IME window state will be updated in default NavBar & external NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
defaultNavBar.getNavigationIconHints());
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
// Set IME window status for external NavBar.
mCommandQueue.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null,
IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true);
Handler.getMain().runWithScissors(() -> { }, 500);
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
externalNavBar.getNavigationIconHints());
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
}
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
DeviceProvisionedController deviceProvisionedController =
mock(DeviceProvisionedController.class);
assertNotNull(mAccessibilityWrapper);
return new NavigationBarFragment(mAccessibilityWrapper,
return new NavigationBarFragment(
context.getDisplayId() == DEFAULT_DISPLAY ? mAccessibilityWrapper
: mock(AccessibilityManagerWrapper.class),
deviceProvisionedController,
new MetricsLogger(),
new AssistManager(deviceProvisionedController, mContext),
mock(AssistManager.class),
mOverviewProxyService);
}
private class HostCallbacksForExternalDisplay extends
FragmentHostCallback<NavigationBarFragmentTest> {
private Context mDisplayContext;
HostCallbacksForExternalDisplay(Context context) {
super(context, mHandler, 0);
mDisplayContext = context;
}
@Override
public NavigationBarFragmentTest onGetHost() {
return NavigationBarFragmentTest.this;
}
@Override
public Fragment instantiate(Context context, String className, Bundle arguments) {
return NavigationBarFragmentTest.this.instantiate(context, className, arguments);
}
@Override
public View onFindViewById(int id) {
return mView.findViewById(id);
}
@Override
public LayoutInflater onGetLayoutInflater() {
return new LayoutInflaterWrapper(mDisplayContext);
}
}
private static class LayoutInflaterWrapper extends LayoutInflater {
protected LayoutInflaterWrapper(Context context) {
super(context);
}
@Override
public LayoutInflater cloneInContext(Context newContext) {
return null;
}
@Override
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root,
boolean attachToRoot) {
NavigationBarView view = mock(NavigationBarView.class);
when(view.getDisplay()).thenReturn(mContext.getDisplay());
when(view.getBackButton()).thenReturn(mock(ButtonDispatcher.class));
when(view.getHomeButton()).thenReturn(mock(ButtonDispatcher.class));
when(view.getRecentsButton()).thenReturn(mock(ButtonDispatcher.class));
when(view.getAccessibilityButton()).thenReturn(mock(ButtonDispatcher.class));
when(view.getRotateSuggestionButton()).thenReturn(mock(RotationContextButton.class));
when(view.getBarTransitions()).thenReturn(mock(BarTransitions.class));
when(view.getLightTransitionsController()).thenReturn(
mock(LightBarTransitionsController.class));
return view;
}
}
}

View File

@@ -575,6 +575,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
*/
int mCurTokenDisplayId = INVALID_DISPLAY;
/**
* The display ID of the input method indicates the fallback display which returned by
* {@link #computeImeDisplayIdForTarget}.
*/
private static final int FALLBACK_DISPLAY_ID = DEFAULT_DISPLAY;
final ImeDisplayValidator mImeDisplayValidator;
/**
@@ -625,7 +631,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
* currently invisible.
* </dd>
* </dl>
* <em>Do not update this value outside of setImeWindowStatus.</em>
* <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
* {@link #unbindCurrentMethodLocked()}.</em>
*/
int mImeWindowVis;
@@ -2124,12 +2131,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
*/
static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
// We always assume that the default display id suitable to show the IME window.
return DEFAULT_DISPLAY;
return FALLBACK_DISPLAY_ID;
}
// Show IME in default display when the display with IME target doesn't support system
// decorations.
return checker.displayCanShowIme(displayId) ? displayId : DEFAULT_DISPLAY;
// Show IME window on fallback display when the display is not allowed.
return checker.displayCanShowIme(displayId) ? displayId : FALLBACK_DISPLAY_ID;
}
@Override
@@ -2198,6 +2203,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
} catch (RemoteException e) {
}
// Set IME window status as invisible when unbind current method.
mImeWindowVis = 0;
mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
mCurToken = null;
mCurTokenDisplayId = INVALID_DISPLAY;
}
@@ -2399,10 +2408,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@SuppressWarnings("deprecation")
private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) {
final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
synchronized (mMethodMap) {
if (!calledWithValidTokenLocked(token)) {
return;
}
// Skip update IME status when current token display is not same as focused display.
// Note that we still need to update IME status when focusing external display
// that does not support system decoration and fallback to show IME on default
// display since it is intentional behavior.
if (mCurTokenDisplayId != topFocusedDisplayId
&& mCurTokenDisplayId != FALLBACK_DISPLAY_ID) {
return;
}
mImeWindowVis = vis;
mBackDisposition = backDisposition;
updateSystemUiLocked(vis, backDisposition);
@@ -2447,7 +2466,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) {
Slog.d(TAG, "IME window vis: " + vis
+ " active: " + (vis & InputMethodService.IME_ACTIVE)
+ " inv: " + (vis & InputMethodService.IME_INVISIBLE));
+ " inv: " + (vis & InputMethodService.IME_INVISIBLE)
+ " displayId: " + mCurTokenDisplayId);
}
// TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
@@ -2461,7 +2481,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
if (mStatusBar != null) {
mStatusBar.setImeWindowStatus(mCurToken, vis, backDisposition,
mStatusBar.setImeWindowStatus(mCurTokenDisplayId, mCurToken, vis, backDisposition,
needsToShowImeSwitcher);
}
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);

View File

@@ -843,10 +843,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
}
// TODO(b/117478341): support back button change when IME is showing on a external display.
@Override
public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
final boolean showImeSwitcher) {
public void setImeWindowStatus(int displayId, final IBinder token, final int vis,
final int backDisposition, final boolean showImeSwitcher) {
enforceStatusBar();
if (SPEW) {
@@ -857,18 +856,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
// In case of IME change, we need to call up setImeWindowStatus() regardless of
// mImeWindowVis because mImeWindowVis may not have been set to false when the
// previous IME was destroyed.
// TODO(b/117478341): support back button change when IME is showing on a external
// display.
getUiState(DEFAULT_DISPLAY)
.setImeWindowState(vis, backDisposition, showImeSwitcher, token);
getUiState(displayId).setImeWindowState(vis, backDisposition, showImeSwitcher, token);
mHandler.post(() -> {
if (mBar == null) return;
try {
// TODO(b/117478341): support back button change when IME is showing on a
// external display.
mBar.setImeWindowStatus(
DEFAULT_DISPLAY, token, vis, backDisposition, showImeSwitcher);
displayId, token, vis, backDisposition, showImeSwitcher);
} catch (RemoteException ex) { }
});
}

View File

@@ -475,6 +475,11 @@ public abstract class WindowManagerInternal {
*/
public abstract int getDisplayIdForWindow(IBinder windowToken);
/**
* @return The top focused display ID.
*/
public abstract int getTopFocusedDisplayId();
/**
* Checks whether this display should support showing system decorations.
*/

View File

@@ -7339,6 +7339,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
@Override
public int getTopFocusedDisplayId() {
synchronized (mGlobalLock) {
return mRoot.getTopFocusedDisplayContent().getDisplayId();
}
}
@Override
public boolean shouldShowSystemDecorOnDisplay(int displayId) {
synchronized (mGlobalLock) {

View File

@@ -52,7 +52,7 @@ public abstract class BaseFragmentTest {
private static final int VIEW_ID = 42;
private final Class<? extends Fragment> mCls;
private Handler mHandler;
protected Handler mHandler;
protected FrameLayout mView;
protected FragmentController mFragments;
protected Fragment mFragment;