Commit 9c50e7da authored by Friedrich Horschig's avatar Friedrich Horschig Committed by Commit Bot

[Mfill Android] Ensure accessory reports keyboard replacements timely

Before this CL, components like the infobar would query the state of
the keyboard as soon as the activity is resized. That means, the
accessory might not have updated the state of filling components (and
immediate updates might cause relayouts) and an old keyboard state is
reported.

Now, the accessory reports the state of keyboard replacements before the
actual component is updated. This way, replacements can be considered
for keyboard visibility, even if the component updates are pending.

Bug: 903256
Change-Id: Ie95297b6dbdfc1929aaf0643a805fbb4fd93378b
Reviewed-on: https://chromium-review.googlesource.com/c/1329246
Commit-Queue: Friedrich Horschig [CET] <fhorschig@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Reviewed-by: default avatarIoana Pandele <ioanap@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607671}
parent 47096f4a
...@@ -59,7 +59,7 @@ public class ChromeKeyboardVisibilityDelegate extends SingleWindowKeyboardVisibi ...@@ -59,7 +59,7 @@ public class ChromeKeyboardVisibilityDelegate extends SingleWindowKeyboardVisibi
boolean wasManualFillingViewShowing = false; boolean wasManualFillingViewShowing = false;
if (activity != null) { if (activity != null) {
wasManualFillingViewShowing = wasManualFillingViewShowing =
activity.getManualFillingController().isFillingViewShown(); activity.getManualFillingController().isFillingViewShown(view);
activity.getManualFillingController().hide(); activity.getManualFillingController().hide();
} }
return super.hideKeyboard(view) || wasManualFillingViewShowing; return super.hideKeyboard(view) || wasManualFillingViewShowing;
...@@ -69,6 +69,7 @@ public class ChromeKeyboardVisibilityDelegate extends SingleWindowKeyboardVisibi ...@@ -69,6 +69,7 @@ public class ChromeKeyboardVisibilityDelegate extends SingleWindowKeyboardVisibi
public boolean isKeyboardShowing(Context context, View view) { public boolean isKeyboardShowing(Context context, View view) {
ChromeActivity activity = getActivity(); ChromeActivity activity = getActivity();
return super.isKeyboardShowing(context, view) return super.isKeyboardShowing(context, view)
|| (activity != null && activity.getManualFillingController().isFillingViewShown()); || (activity != null
&& activity.getManualFillingController().isFillingViewShown(view));
} }
} }
\ No newline at end of file
...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.autofill.keyboard_accessory; ...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.autofill.keyboard_accessory;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.view.View;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider; import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider;
...@@ -141,10 +142,11 @@ public class ManualFillingCoordinator { ...@@ -141,10 +142,11 @@ public class ManualFillingCoordinator {
} }
/** /**
* Returns whether - at this very moment - the Keyboard is replaced by an accessory sheet. * Returns whether the Keyboard is replaced by an accessory sheet or is about to do so.
* @return True if an accessory sheet is open and replacing the keyboard. * @return True if an accessory sheet is (being) opened and replacing the keyboard.
* @param view A {@link View} that is used to find the window root.
*/ */
public boolean isFillingViewShown() { public boolean isFillingViewShown(View view) {
return mMediator.isFillingViewShown(); return mMediator.isFillingViewShown(view);
} }
} }
\ No newline at end of file
...@@ -32,7 +32,6 @@ import org.chromium.chrome.browser.tabmodel.TabModel; ...@@ -32,7 +32,6 @@ import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver; import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver; import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
import org.chromium.ui.DropdownPopupWindow; import org.chromium.ui.DropdownPopupWindow;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import java.util.HashMap; import java.util.HashMap;
...@@ -185,16 +184,17 @@ class ManualFillingMediator extends EmptyTabObserver ...@@ -185,16 +184,17 @@ class ManualFillingMediator extends EmptyTabObserver
return mWindowAndroid != null; return mWindowAndroid != null;
} }
boolean isFillingViewShown() { boolean isFillingViewShown(View view) {
return mAccessorySheet != null && mAccessorySheet.isShown(); if (!isInitialized()) return false;
boolean isSoftInputShowing = getKeyboard().isSoftKeyboardShowing(mActivity, view);
return !isSoftInputShowing && mKeyboardAccessory.hasActiveTab();
} }
@Override @Override
public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) { int oldTop, int oldRight, int oldBottom) {
if (mActivity == null) return; // Activity has been cleaned up already. if (mActivity == null) return; // Activity has been cleaned up already.
onKeyboardVisibilityPossiblyChanged( onKeyboardVisibilityPossiblyChanged(getKeyboard().isSoftKeyboardShowing(mActivity, view));
getKeyboardDelegate().isSoftKeyboardShowing(mActivity, view));
} }
private void onKeyboardVisibilityPossiblyChanged(boolean isShowing) { private void onKeyboardVisibilityPossiblyChanged(boolean isShowing) {
...@@ -387,8 +387,9 @@ class ManualFillingMediator extends EmptyTabObserver ...@@ -387,8 +387,9 @@ class ManualFillingMediator extends EmptyTabObserver
} }
private ChromeKeyboardVisibilityDelegate getKeyboard() { private ChromeKeyboardVisibilityDelegate getKeyboard() {
KeyboardVisibilityDelegate delegate = mWindowAndroid.getKeyboardDelegate(); assert mWindowAndroid instanceof ChromeWindow;
return (ChromeKeyboardVisibilityDelegate) delegate; assert mWindowAndroid.getKeyboardDelegate() instanceof ChromeKeyboardVisibilityDelegate;
return (ChromeKeyboardVisibilityDelegate) mWindowAndroid.getKeyboardDelegate();
} }
private AccessoryState getOrCreateAccessoryState(Tab tab) { private AccessoryState getOrCreateAccessoryState(Tab tab) {
...@@ -432,12 +433,6 @@ class ManualFillingMediator extends EmptyTabObserver ...@@ -432,12 +433,6 @@ class ManualFillingMediator extends EmptyTabObserver
org.chromium.chrome.R.dimen.keyboard_accessory_suggestion_height); org.chromium.chrome.R.dimen.keyboard_accessory_suggestion_height);
} }
private ChromeKeyboardVisibilityDelegate getKeyboardDelegate() {
assert mWindowAndroid instanceof ChromeWindow;
assert mWindowAndroid.getKeyboardDelegate() instanceof ChromeKeyboardVisibilityDelegate;
return (ChromeKeyboardVisibilityDelegate) mWindowAndroid.getKeyboardDelegate();
}
@VisibleForTesting @VisibleForTesting
void addTab(KeyboardAccessoryData.Tab tab) { void addTab(KeyboardAccessoryData.Tab tab) {
if (!isInitialized()) return; if (!isInitialized()) return;
......
...@@ -10,12 +10,15 @@ import static org.hamcrest.CoreMatchers.not; ...@@ -10,12 +10,15 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessoryAction.GENERATE_PASSWORD_AUTOMATIC; import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessoryAction.GENERATE_PASSWORD_AUTOMATIC;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TABS; import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TABS;
import static org.chromium.chrome.browser.tab.Tab.INVALID_TAB_ID; import static org.chromium.chrome.browser.tab.Tab.INVALID_TAB_ID;
import static org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType.FROM_BROWSER_ACTIONS; import static org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType.FROM_BROWSER_ACTIONS;
...@@ -41,6 +44,8 @@ import org.chromium.base.metrics.test.ShadowRecordHistogram; ...@@ -41,6 +44,8 @@ import org.chromium.base.metrics.test.ShadowRecordHistogram;
import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.ChromeKeyboardVisibilityDelegate;
import org.chromium.chrome.browser.ChromeWindow;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action; import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item; import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Item;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.PropertyProvider; import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.PropertyProvider;
...@@ -56,7 +61,7 @@ import org.chromium.chrome.test.util.browser.Features; ...@@ -56,7 +61,7 @@ import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.Features.DisableFeatures; import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider; import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
import org.chromium.ui.base.ActivityWindowAndroid; import org.chromium.ui.KeyboardVisibilityDelegate;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Map; import java.util.Map;
...@@ -70,7 +75,7 @@ import java.util.Map; ...@@ -70,7 +75,7 @@ import java.util.Map;
@DisableFeatures(ChromeFeatureList.EXPERIMENTAL_UI) @DisableFeatures(ChromeFeatureList.EXPERIMENTAL_UI)
public class ManualFillingControllerTest { public class ManualFillingControllerTest {
@Mock @Mock
private ActivityWindowAndroid mMockWindow; private ChromeWindow mMockWindow;
@Mock @Mock
private ChromeActivity mMockActivity; private ChromeActivity mMockActivity;
@Mock @Mock
...@@ -89,6 +94,8 @@ public class ManualFillingControllerTest { ...@@ -89,6 +94,8 @@ public class ManualFillingControllerTest {
private Drawable mMockIcon; private Drawable mMockIcon;
@Mock @Mock
private android.content.res.Resources mMockResources; private android.content.res.Resources mMockResources;
@Mock
private ChromeKeyboardVisibilityDelegate mMockKeyboard;
@Rule @Rule
public Features.JUnitProcessor mFeaturesProcessor = new Features.JUnitProcessor(); public Features.JUnitProcessor mFeaturesProcessor = new Features.JUnitProcessor();
...@@ -101,6 +108,8 @@ public class ManualFillingControllerTest { ...@@ -101,6 +108,8 @@ public class ManualFillingControllerTest {
public void setUp() { public void setUp() {
ShadowRecordHistogram.reset(); ShadowRecordHistogram.reset();
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
KeyboardVisibilityDelegate.setInstance(mMockKeyboard);
when(mMockWindow.getKeyboardDelegate()).thenReturn(mMockKeyboard);
when(mMockWindow.getActivity()).thenReturn(new WeakReference<>(mMockActivity)); when(mMockWindow.getActivity()).thenReturn(new WeakReference<>(mMockActivity));
when(mMockActivity.getTabModelSelector()).thenReturn(mMockTabModelSelector); when(mMockActivity.getTabModelSelector()).thenReturn(mMockTabModelSelector);
ChromeFullscreenManager fullscreenManager = new ChromeFullscreenManager(mMockActivity, 0); ChromeFullscreenManager fullscreenManager = new ChromeFullscreenManager(mMockActivity, 0);
...@@ -478,6 +487,27 @@ public class ManualFillingControllerTest { ...@@ -478,6 +487,27 @@ public class ManualFillingControllerTest {
assertThat(mediator.getPasswordAccessorySheet(), is(nullValue())); assertThat(mediator.getPasswordAccessorySheet(), is(nullValue()));
} }
@Test
public void testIsFillingViewShownReturnsTargetValueAheadOfComponentUpdate() {
// After initialization with one tab, the accessory sheet is closed.
KeyboardAccessoryCoordinator accessory =
mController.getMediatorForTesting().getKeyboardAccessory();
mController.getMediatorForTesting().addTab(
new KeyboardAccessoryData.Tab(null, null, 0, 0, null));
accessory.requestShowing();
assertThat(mController.isFillingViewShown(null), is(false));
// As soon as active tab and keyboard change, |isFillingViewShown| returns the expected
// state - even if the sheet component wasn't updated yet.
accessory.getMediatorForTesting().getModelForTesting().set(ACTIVE_TAB, 0);
when(mMockKeyboard.isSoftKeyboardShowing(eq(mMockActivity), any())).thenReturn(false);
assertThat(mController.isFillingViewShown(null), is(true));
// The layout change impacts the component, but not the coordinator method.
mController.getMediatorForTesting().onLayoutChange(null, 0, 0, 0, 0, 0, 0, 0, 0);
assertThat(mController.isFillingViewShown(null), is(true));
}
/** /**
* Creates a tab and calls the observer events as if it was just created and switched to. * Creates a tab and calls the observer events as if it was just created and switched to.
* @param mediator The {@link ManualFillingMediator} whose observers should be triggered. * @param mediator The {@link ManualFillingMediator} whose observers should be triggered.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment