Commit 82b7a051 authored by Friedrich Horschig's avatar Friedrich Horschig Committed by Commit Bot

[Android] Move keyboard delegate into ActivityWindow

Before this CL, several places (esp. in browser/) would override the
instance of the keyboard delegate in ui/. This caused an inconsistent,
hard to track state of the KeyboardVisibilityDelegate.

With this CL, the ui/ takes control of setting the delegate and specific
browser-side implementations are called by using the appropriate factory
methods. For that, the KeyboardVisibilityDelegate (brief: KVD) uses
sublasses with very clearly split responsibilities:

KeyboardVisibilityDelegate (ui/; public interface for all clients)
<-- ActivityKVD (ui/; propagates visibility changes to listeners)
  <-- SingleWindowKVD (browser/; prevents multi-window use)
    <-- ChromeKVD (browser/ separates extensions/native)
      <-- FakeKeyboard (javatests/; mocks native keyboard)

The CL actually has no functional changes, it performs these moves:
 - WindowAndroid Listeners moved into KeyboardVisibilityDelegate
 - AcivityWindowAndroid callbacks moved into ActivityKVD
 - ProcessInitializationHandler delegate became SingleWindowKVD
 - ManualFillingTestHelper's FakeKeyboard became a new, public class

Functional changes follow with https://crrev.com/c/1286426 and mainly
affect the ChromeKVD.

Purpose of isAndroidSoftKeyboardShowing(...) and
hideAndroidSoftKeyboardKeyboard(...):
 - Template methods to simplify mocking the calls to InputMethodManager
 - Ensures access to keyboard without extensions in subclasses (e.g. Chrome KVD)
 - prevents use of @CallSuper

Change-Id: I06cf50f48625d2d883efc8cd66a3d062204e51b2
Reviewed-on: https://chromium-review.googlesource.com/c/1286649
Commit-Queue: Friedrich Horschig [EDT] <fhorschig@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602293}
parent 64380b10
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser;
import android.app.Activity;
import android.content.Context;
import android.support.annotation.Nullable;
import android.view.View;
import org.chromium.chrome.browser.init.SingleWindowKeyboardVisibilityDelegate;
import java.lang.ref.WeakReference;
/**
* A {@link SingleWindowKeyboardVisibilityDelegate} that considers UI elements of a
* {@link ChromeActivity} which amend or replace the keyboard.
*/
public class ChromeKeyboardVisibilityDelegate extends SingleWindowKeyboardVisibilityDelegate {
/**
* Creates a new visibility delegate.
* @param activity A {@link WeakReference} to a {@link ChromeActivity}.
*/
public ChromeKeyboardVisibilityDelegate(WeakReference<Activity> activity) {
super(activity);
assert activity.get() instanceof ChromeActivity;
}
@Override
public @Nullable ChromeActivity getActivity() {
return (ChromeActivity) super.getActivity();
}
/**
* Hide only Android's soft keyboard. Keeps eventual keyboard replacements and extensions
* untouched. Usually, you will want to call {@link #hideKeyboard(View)}.
* @param view A focused {@link View}.
* @return True if the keyboard was visible before this call.
*/
public boolean hideSoftKeyboardOnly(View view) {
return hideAndroidSoftKeyboard(view);
}
/**
* Returns whether Android soft keyboard is showing and ignores all extensions/replacements.
* Usually, you will want to call {@link #isKeyboardShowing(Context, View)}.
* @param context A {@link Context} instance.
* @param view A {@link View}.
* @return Returns true if Android's soft keyboard is visible. Ignores extensions/replacements.
*/
public boolean isSoftKeyboardShowing(Context context, View view) {
return isAndroidSoftKeyboardShowing(context, view);
}
}
\ No newline at end of file
......@@ -7,19 +7,33 @@ package org.chromium.chrome.browser;
import android.app.Activity;
import android.view.View;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
import org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder;
import org.chromium.chrome.browser.metrics.WebApkUma;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.webapps.WebApkActivity;
import org.chromium.ui.base.ActivityAndroidPermissionDelegate;
import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
import org.chromium.ui.base.ActivityWindowAndroid;
import java.lang.ref.WeakReference;
/**
* The window that has access to the main activity and is able to create and receive intents,
* and show error messages.
*/
public class ChromeWindow extends ActivityWindowAndroid {
/**
* Interface allowing to inject a different keyboard delegate for testing.
*/
@VisibleForTesting
public interface KeyboardVisibilityDelegateFactory {
ActivityKeyboardVisibilityDelegate create(WeakReference<Activity> activity);
}
private static KeyboardVisibilityDelegateFactory sKeyboardVisibilityDelegateFactory =
ChromeKeyboardVisibilityDelegate::new;
/**
* Creates Chrome specific ActivityWindowAndroid.
* @param activity The activity that owns the ChromeWindow.
......@@ -53,6 +67,11 @@ public class ChromeWindow extends ActivityWindowAndroid {
};
}
@Override
protected ActivityKeyboardVisibilityDelegate createKeyboardVisibilityDelegate() {
return sKeyboardVisibilityDelegateFactory.create(getActivity());
}
/**
* Shows an infobar error message overriding the WindowAndroid implementation.
*/
......@@ -71,4 +90,15 @@ public class ChromeWindow extends ActivityWindowAndroid {
super.showCallbackNonExistentError(error);
}
}
@VisibleForTesting
public static void setKeyboardVisibilityDelegateFactory(
KeyboardVisibilityDelegateFactory factory) {
sKeyboardVisibilityDelegateFactory = factory;
}
@VisibleForTesting
public static void resetKeyboardVisibilityDelegateFactory() {
setKeyboardVisibilityDelegateFactory(ChromeKeyboardVisibilityDelegate::new);
}
}
......@@ -13,6 +13,7 @@ import org.chromium.base.Supplier;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.ChromeKeyboardVisibilityDelegate;
import org.chromium.chrome.browser.InsetObserverView;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider;
......@@ -31,6 +32,7 @@ import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
import org.chromium.ui.DropdownPopupWindow;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.base.WindowAndroid;
import java.util.HashMap;
......@@ -43,7 +45,7 @@ import java.util.Map;
class ManualFillingMediator
extends EmptyTabObserver implements KeyboardAccessoryCoordinator.VisibilityDelegate {
private WindowAndroid mWindowAndroid;
private final WindowAndroid.KeyboardVisibilityListener mVisibilityListener =
private final KeyboardVisibilityDelegate.KeyboardVisibilityListener mVisibilityListener =
this::onKeyboardVisibilityChanged;
private Supplier<InsetObserverView> mInsetObserverViewSupplier;
private boolean mShouldShow = false;
......@@ -155,7 +157,7 @@ class ManualFillingMediator
setInsetObserverViewSupplier(mActivity::getInsetObserverView);
LayoutManager manager = getLayoutManager();
if (manager != null) manager.addSceneChangeObserver(mTabSwitcherObserver);
windowAndroid.addKeyboardVisibilityListener(mVisibilityListener);
windowAndroid.getKeyboardDelegate().addKeyboardVisibilityListener(mVisibilityListener);
mTabModelObserver = new TabModelSelectorTabModelObserver(mActivity.getTabModelSelector()) {
@Override
public void didSelectTab(Tab tab, @TabModel.TabSelectionType int type, int lastId) {
......@@ -224,7 +226,7 @@ class ManualFillingMediator
void destroy() {
if (!isInitialized()) return;
pause();
mWindowAndroid.removeKeyboardVisibilityListener(mVisibilityListener);
getKeyboard().removeKeyboardVisibilityListener(mVisibilityListener);
LayoutManager manager = getLayoutManager();
if (manager != null) manager.removeSceneChangeObserver(mTabSwitcherObserver);
mWindowAndroid = null;
......@@ -245,7 +247,7 @@ class ManualFillingMediator
pause();
ViewGroup contentView = getContentView();
if (contentView != null) {
mWindowAndroid.getKeyboardDelegate().hideKeyboard(getContentView());
getKeyboard().hideSoftKeyboardOnly(getContentView());
}
}
......@@ -257,7 +259,7 @@ class ManualFillingMediator
if (!isInitialized() || !mKeyboardAccessory.hasContents() || mShouldShow) return;
mShouldShow = true;
ViewGroup contentView = getContentView();
if (mWindowAndroid.getKeyboardDelegate().isKeyboardShowing(mActivity, contentView)) {
if (getKeyboard().isSoftKeyboardShowing(mActivity, contentView)) {
displayKeyboardAccessory();
}
}
......@@ -309,14 +311,14 @@ class ManualFillingMediator
View rootView = contentView.getRootView();
if (rootView == null) return;
mAccessorySheet.setHeight(calculateAccessorySheetHeight(rootView));
mWindowAndroid.getKeyboardDelegate().hideKeyboard(contentView);
getKeyboard().hideSoftKeyboardOnly(contentView);
}
@Override
public void onCloseAccessorySheet() {
ViewGroup contentView = getContentView();
if (contentView == null || mActivity == null) return; // The tab was cleaned up already.
if (mWindowAndroid.getKeyboardDelegate().isKeyboardShowing(mActivity, contentView)) {
if (getKeyboard().isSoftKeyboardShowing(mActivity, contentView)) {
return; // If the keyboard is showing or is starting to show, the sheet closes gently.
}
mActivity.getFullscreenManager().setBottomControlsHeight(mPreviousControlHeight);
......@@ -339,7 +341,7 @@ class ManualFillingMediator
assert mActivity != null : "ManualFillingMediator needs initialization.";
mKeyboardExtensionSizeManager.setKeyboardExtensionHeight(calculateAccessoryBarHeight());
if (mActivity.getCurrentFocus() != null) {
mWindowAndroid.getKeyboardDelegate().showKeyboard(mActivity.getCurrentFocus());
getKeyboard().showKeyboard(mActivity.getCurrentFocus());
}
}
......@@ -386,6 +388,11 @@ class ManualFillingMediator
return compositorViewHolder.getLayoutManager();
}
private ChromeKeyboardVisibilityDelegate getKeyboard() {
KeyboardVisibilityDelegate delegate = mWindowAndroid.getKeyboardDelegate();
return (ChromeKeyboardVisibilityDelegate) delegate;
}
private AccessoryState getOrCreateAccessoryState(Tab tab) {
assert tab != null : "Accessory state was requested without providing a non-null tab!";
AccessoryState state = mModel.get(tab);
......@@ -418,7 +425,7 @@ class ManualFillingMediator
// Without known inset (which is keyboard + bottom soft keys), use the keyboard height.
return Math.max(mActivity.getResources().getDimensionPixelSize(
org.chromium.chrome.R.dimen.keyboard_accessory_suggestion_height),
mWindowAndroid.getKeyboardDelegate().calculateKeyboardHeight(mActivity, rootView));
getKeyboard().calculateKeyboardHeight(rootView));
}
private @Px int calculateAccessoryBarHeight() {
......
......@@ -5,13 +5,11 @@
package org.chromium.chrome.browser.init;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.SystemClock;
import android.support.annotation.WorkerThread;
import android.view.View;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
......@@ -55,7 +53,6 @@ import org.chromium.chrome.browser.media.MediaCaptureNotificationService;
import org.chromium.chrome.browser.media.MediaViewerUtils;
import org.chromium.chrome.browser.metrics.LaunchMetrics;
import org.chromium.chrome.browser.metrics.PackageMetrics;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
import org.chromium.chrome.browser.notifications.channels.ChannelsUpdater;
import org.chromium.chrome.browser.ntp.NewTabPage;
......@@ -82,7 +79,6 @@ import org.chromium.content_public.common.ContentSwitches;
import org.chromium.printing.PrintDocumentAdapterWrapper;
import org.chromium.printing.PrintingControllerImpl;
import org.chromium.ui.ContactsPickerListener;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.PhotoPickerListener;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.SelectFileDialog;
......@@ -152,24 +148,6 @@ public class ProcessInitializationHandler {
protected void handlePreNativeInitialization() {
Context application = ContextUtils.getApplicationContext();
KeyboardVisibilityDelegate.setInstance(new KeyboardVisibilityDelegate() {
@Override
public boolean isKeyboardShowing(Context context, View view) {
Activity activity = null;
if (context instanceof Activity) {
activity = (Activity) context;
} else if (view != null && view.getContext() instanceof Activity) {
activity = (Activity) view.getContext();
}
if (activity != null
&& MultiWindowUtils.getInstance().isLegacyMultiWindow(activity)) {
return false; // For multi-window mode we do not track keyboard visibility.
}
return super.isKeyboardShowing(context, view);
}
});
// Initialize the AccountManagerFacade with the correct AccountManagerDelegate. Must be done
// only once and before AccountMangerHelper.get(...) is called to avoid using the
// default AccountManagerDelegate.
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.init;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
import org.chromium.ui.base.WindowAndroid;
import java.lang.ref.WeakReference;
/**
* A {@link ActivityKeyboardVisibilityDelegate} that prevents the multi-window functionality from
* triggering the layout-based keyboard detection.
*/
public class SingleWindowKeyboardVisibilityDelegate extends ActivityKeyboardVisibilityDelegate {
public SingleWindowKeyboardVisibilityDelegate(WeakReference<Activity> activity) {
super(activity);
}
@Override
public boolean isKeyboardShowing(Context context, View view) {
Activity activity = WindowAndroid.activityFromContext(context);
if (activity == null && view != null && view.getContext() instanceof Activity) {
activity = (Activity) view.getContext();
}
if (activity != null && MultiWindowUtils.getInstance().isLegacyMultiWindow(activity)) {
return false; // For multi-window mode we do not track keyboard visibility.
}
return super.isKeyboardShowing(context, view);
}
}
\ No newline at end of file
......@@ -25,6 +25,7 @@ import org.chromium.chrome.browser.WindowDelegate;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.init.AsyncInitializationActivity;
import org.chromium.chrome.browser.init.SingleWindowKeyboardVisibilityDelegate;
import org.chromium.chrome.browser.locale.LocaleManager;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
......@@ -37,6 +38,7 @@ import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.components.url_formatter.UrlFormatter;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
import org.chromium.ui.base.ActivityWindowAndroid;
/** Queries the user's default search engine and shows autocomplete suggestions. */
......@@ -115,7 +117,12 @@ public class SearchActivity extends AsyncInitializationActivity
@Override
protected ActivityWindowAndroid createWindowAndroid() {
return new ActivityWindowAndroid(this);
return new ActivityWindowAndroid(this) {
@Override
protected ActivityKeyboardVisibilityDelegate createKeyboardVisibilityDelegate() {
return new SingleWindowKeyboardVisibilityDelegate(getActivity());
}
};
}
@Override
......
......@@ -22,8 +22,8 @@ import org.chromium.chrome.browser.toolbar.ToolbarButtonSlotData.ToolbarButtonDa
import org.chromium.chrome.browser.widget.textbubble.TextBubble;
import org.chromium.components.feature_engagement.FeatureConstants;
import org.chromium.components.feature_engagement.Tracker;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.base.WindowAndroid.KeyboardVisibilityListener;
import org.chromium.ui.resources.ResourceManager;
import org.chromium.ui.widget.ViewRectProvider;
......@@ -32,9 +32,9 @@ import org.chromium.ui.widget.ViewRectProvider;
* coordinators, running most of the business logic associated with the bottom toolbar, and updating
* the model accordingly.
*/
class BottomToolbarMediator implements FullscreenListener, KeyboardVisibilityListener,
OverlayPanelManagerObserver, OverviewModeObserver,
SceneChangeObserver {
class BottomToolbarMediator
implements FullscreenListener, KeyboardVisibilityDelegate.KeyboardVisibilityListener,
OverlayPanelManagerObserver, OverviewModeObserver, SceneChangeObserver {
/** The amount of time to show the Duet help bubble for. */
private static final int DUET_IPH_BUBBLE_SHOW_DURATION_MS = 6000;
......@@ -106,7 +106,9 @@ class BottomToolbarMediator implements FullscreenListener, KeyboardVisibilityLis
void destroy() {
mFullscreenManager.removeListener(this);
if (mOverviewModeBehavior != null) mOverviewModeBehavior.removeOverviewModeObserver(this);
if (mWindowAndroid != null) mWindowAndroid.removeKeyboardVisibilityListener(this);
if (mWindowAndroid != null) {
mWindowAndroid.getKeyboardDelegate().removeKeyboardVisibilityListener(this);
}
if (mModel.get(BottomToolbarModel.LAYOUT_MANAGER) != null) {
LayoutManager manager = mModel.get(BottomToolbarModel.LAYOUT_MANAGER);
manager.getOverlayPanelManager().removeObserver(this);
......@@ -229,7 +231,7 @@ class BottomToolbarMediator implements FullscreenListener, KeyboardVisibilityLis
assert mWindowAndroid == null : "#setWindowAndroid should only be called once per toolbar.";
// Watch for keyboard events so we can hide the bottom toolbar when the keyboard is showing.
mWindowAndroid = windowAndroid;
mWindowAndroid.addKeyboardVisibilityListener(this);
mWindowAndroid.getKeyboardDelegate().addKeyboardVisibilityListener(this);
}
void setTabSwitcherButtonData(
......
......@@ -31,6 +31,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/ChromeBackupWatcher.java",
"java/src/org/chromium/chrome/browser/ChromeFeatureList.java",
"java/src/org/chromium/chrome/browser/ChromeHttpAuthHandler.java",
"java/src/org/chromium/chrome/browser/ChromeKeyboardVisibilityDelegate.java",
"java/src/org/chromium/chrome/browser/ChromeStrictMode.java",
"java/src/org/chromium/chrome/browser/ChromeStringConstants.java",
"java/src/org/chromium/chrome/browser/ChromeSwitches.java",
......@@ -770,6 +771,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/init/NativeInitializationController.java",
"java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java",
"java/src/org/chromium/chrome/browser/init/ServiceManagerStartupUtils.java",
"java/src/org/chromium/chrome/browser/init/SingleWindowKeyboardVisibilityDelegate.java",
"java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderFactory.java",
"java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderImpl.java",
"java/src/org/chromium/chrome/browser/installedapp/PackageHash.java",
......@@ -1793,6 +1795,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java",
"javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/FakeKeyboard.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.autofill.keyboard_accessory;
import static org.chromium.base.ThreadUtils.runOnUiThreadBlocking;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import org.chromium.chrome.browser.ChromeKeyboardVisibilityDelegate;
import org.chromium.chrome.browser.ChromeWindow;
import org.chromium.chrome.browser.InsetObserverView;
import java.lang.ref.WeakReference;
/**
* This class allows to mock the {@link org.chromium.ui.KeyboardVisibilityDelegate} in any given
* {@link org.chromium.chrome.test.ChromeActivityTestRule} which allows to write tests relying on
* keyboard without having to deal with the soft keyboard. To use it, inject its constructor as
* factory into the {@link org.chromium.chrome.browser.ChromeWindow} before launching an activity.
* To reset, call {@link ChromeWindow#resetKeyboardVisibilityDelegateFactory()}.
* <pre>E.g.{@code
* // To force a keyboard open.
* ChromeWindow.setKeyboardVisibilityDelegateFactory(FakeKeyboard::new);
* aTestRule.startMainActivityOnBlankPage();
* ChromeWindow.resetKeyboardVisibilityDelegateFactory();
* aTestRule.getKeyboardDelegate().showKeyboard(); // No delay/waiting necessary.
* }</pre>
*/
public class FakeKeyboard extends ChromeKeyboardVisibilityDelegate {
private static final int KEYBOARD_HEIGHT_DP = 234;
private boolean mIsShowing;
public FakeKeyboard(WeakReference<Activity> activity) {
super(activity);
}
private int getStaticKeyboardHeight() {
return (int) getActivity().getResources().getDisplayMetrics().density * KEYBOARD_HEIGHT_DP;
}
@Override
protected boolean isAndroidSoftKeyboardShowing(Context context, View view) {
return mIsShowing;
}
@Override
public void showKeyboard(View view) {
boolean keyboardWasVisible = isKeyboardShowing(getActivity(), view);
mIsShowing = true;
runOnUiThreadBlocking(() -> {
if (!keyboardWasVisible) notifyListeners(mIsShowing);
// Pretend a layout change for components listening to the activity directly:
View contentView = getActivity().findViewById(android.R.id.content);
ViewGroup.LayoutParams p = contentView.getLayoutParams();
p.height = p.height - getStaticKeyboardHeight();
contentView.setLayoutParams(p);
});
}
@Override
protected boolean hideAndroidSoftKeyboard(View view) {
boolean keyboardWasVisible = isKeyboardShowing(getActivity(), view);
mIsShowing = false;
runOnUiThreadBlocking(() -> {
if (keyboardWasVisible) notifyListeners(mIsShowing);
// Pretend a layout change for components listening to the activity directly:
View contentView = getActivity().findViewById(android.R.id.content);
ViewGroup.LayoutParams p = contentView.getLayoutParams();
p.height = p.height + getStaticKeyboardHeight();
contentView.setLayoutParams(p);
});
return keyboardWasVisible;
}
@Override
public int calculateKeyboardHeight(View rootView) {
return mIsShowing ? getStaticKeyboardHeight() : 0;
}
@Override
public int calculateKeyboardDetectionThreshold(Context context, View rootView) {
return 0;
}
/**
* Creates an inset observer view calculating the bottom inset based on the fake keyboard.
* @param context Context used to instantiate this view.
* @return a {@link InsetObserverView}
*/
InsetObserverView createInsetObserver(Context context) {
return new InsetObserverView(context) {
@Override
public int getSystemWindowInsetsBottom() {
return mIsShowing ? getStaticKeyboardHeight() : 0;
}
};
}
}
......@@ -383,7 +383,7 @@ public class ManualFillingIntegrationTest {
@Test
@SmallTest
public void testInfobarStaysHiddenWhenOpeningSheet()
throws InterruptedException, TimeoutException {
throws InterruptedException, TimeoutException, ExecutionException {
mHelper.loadTestPage(false);
InfoBarTestAnimationListener listener = new InfoBarTestAnimationListener();
......@@ -413,7 +413,11 @@ public class ManualFillingIntegrationTest {
// Reopen the keyboard, then close it.
whenDisplayed(withId(R.id.tabs)).perform(selectTabAtPosition(0));
mHelper.waitForKeyboard();
mActivityTestRule.getKeyboardDelegate().hideKeyboard(null);
ThreadUtils.runOnUiThreadBlocking(() -> {
mActivityTestRule.getKeyboardDelegate().hideKeyboard(
mActivityTestRule.getActivity().getCurrentFocus());
mActivityTestRule.getInfoBarContainer().requestLayout();
});
mHelper.waitToBeHidden(withId(R.id.keyboard_accessory_sheet));
mHelper.waitToBeHidden(withId(R.id.keyboard_accessory));
......
......@@ -17,9 +17,8 @@ import static org.chromium.chrome.test.util.ViewUtils.VIEW_NULL;
import static org.chromium.chrome.test.util.ViewUtils.waitForView;
import static org.chromium.ui.base.LocalizationUtils.setRtlForTesting;
import android.content.Context;
import android.app.Activity;
import android.support.design.widget.TabLayout;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.PerformException;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
......@@ -34,7 +33,7 @@ import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.InsetObserverView;
import org.chromium.chrome.browser.ChromeWindow;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.content_public.browser.ImeAdapter;
import org.chromium.content_public.browser.WebContents;
......@@ -43,7 +42,6 @@ import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.DOMUtils;
import org.chromium.content_public.browser.test.util.TestInputMethodManagerWrapper;
import org.chromium.ui.DropdownPopupWindowInterface;
import org.chromium.ui.KeyboardVisibilityDelegate;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
......@@ -57,66 +55,16 @@ public class ManualFillingTestHelper {
private final AtomicReference<WebContents> mWebContentsRef = new AtomicReference<>();
private TestInputMethodManagerWrapper mInputMethodManagerWrapper;
private class FakeKeyboard extends KeyboardVisibilityDelegate {
static final int KEYBOARD_HEIGHT = 234;
private boolean mIsShowing;
@Override
public void showKeyboard(View view) {
mIsShowing = true;
ThreadUtils.runOnUiThreadBlocking(() -> {
mActivityTestRule.getActivity()
.getManualFillingController()
.getMediatorForTesting()
.onKeyboardVisibilityChanged(mIsShowing);
});
}
@Override
public boolean hideKeyboard(View view) {
boolean keyboardWasVisible = mIsShowing;
mIsShowing = false;
ThreadUtils.runOnUiThreadBlocking(() -> {
mActivityTestRule.getActivity()
.getManualFillingController()
.getMediatorForTesting()
.onKeyboardVisibilityChanged(mIsShowing);
});
return keyboardWasVisible;
}
@Override
public int calculateKeyboardHeight(Context context, View rootView) {
return mIsShowing ? KEYBOARD_HEIGHT : 0;
}
@Override
protected int calculateKeyboardDetectionThreshold(Context context, View rootView) {
return 0;
}
/**
* Creates an inset observer view calculating the bottom inset based on the fake keyboard.
* @param context Context used to instantiate this view.
* @return a {@link InsetObserverView}
*/
InsetObserverView createInsetObserver(Context context) {
return new InsetObserverView(context) {
@Override
public int getSystemWindowInsetsBottom() {
return mIsShowing ? KEYBOARD_HEIGHT : 0;
}
};
}
public FakeKeyboard getKeyboard() {
return (FakeKeyboard) mActivityTestRule.getKeyboardDelegate();
}
private final FakeKeyboard mKeyboard = new FakeKeyboard();
ManualFillingTestHelper(ChromeTabbedActivityTestRule activityTestRule) {
mActivityTestRule = activityTestRule;
}
public void loadTestPage(boolean isRtl) throws InterruptedException {
ChromeWindow.setKeyboardVisibilityDelegateFactory(FakeKeyboard::new);
mActivityTestRule.startMainActivityWithURL(UrlUtils.encodeHtmlDataUri("<html"
+ (isRtl ? " dir=\"rtl\"" : "") + "><head>"
+ "<meta name=\"viewport\""
......@@ -128,14 +76,14 @@ public class ManualFillingTestHelper {
+ "</form></body></html>"));
setRtlForTesting(isRtl);
ThreadUtils.runOnUiThreadBlocking(() -> {
ChromeTabbedActivity activity = mActivityTestRule.getActivity();
ChromeTabbedActivity activity = (ChromeTabbedActivity) mActivityTestRule.getActivity();
mWebContentsRef.set(activity.getActivityTab().getWebContents());
activity.getManualFillingController()
.getMediatorForTesting()
.setInsetObserverViewSupplier(() -> {
return mKeyboard.createInsetObserver(
activity.getInsetObserverView().getContext());
});
.setInsetObserverViewSupplier(
()
-> getKeyboard().createInsetObserver(
activity.getInsetObserverView().getContext()));
// The TestInputMethodManagerWrapper intercepts showSoftInput so that a keyboard is
// never brought up.
final ImeAdapter imeAdapter = ImeAdapter.fromWebContents(mWebContentsRef.get());
......@@ -143,32 +91,31 @@ public class ManualFillingTestHelper {
imeAdapter.setInputMethodManagerWrapper(mInputMethodManagerWrapper);
});
DOMUtils.waitForNonZeroNodeBounds(mWebContentsRef.get(), "password");
KeyboardVisibilityDelegate.setDelegateForTesting(mKeyboard); // Use a fake keyboard.
}
public void clear() {
KeyboardVisibilityDelegate.clearDelegateForTesting();
ChromeWindow.resetKeyboardVisibilityDelegateFactory();
}
public void waitForKeyboard() {
CriteriaHelper.pollUiThread(() -> {
return mActivityTestRule.getKeyboardDelegate().isKeyboardShowing(
InstrumentationRegistry.getContext(),
mActivityTestRule.getActivity().getCurrentFocus());
Activity activity = mActivityTestRule.getActivity();
return getKeyboard().isAndroidSoftKeyboardShowing(activity, activity.getCurrentFocus());
});
}
public void waitForKeyboardToDisappear() {
CriteriaHelper.pollUiThread(
()
-> !KeyboardVisibilityDelegate.getInstance().isKeyboardShowing(
InstrumentationRegistry.getContext(),
mActivityTestRule.getActivity().getCurrentFocus()));
CriteriaHelper.pollUiThread(() -> {
Activity activity = mActivityTestRule.getActivity();
return !getKeyboard().isAndroidSoftKeyboardShowing(
activity, activity.getCurrentFocus());
});
}
public void clickPasswordField() throws TimeoutException, InterruptedException {
DOMUtils.clickNode(mWebContentsRef.get(), "password");
requestShowKeyboardAccessory();
mKeyboard.showKeyboard(null);
getKeyboard().showKeyboard(mActivityTestRule.getActivity().getCurrentFocus());
}
public void clickEmailField(boolean forceAccessory)
......@@ -179,7 +126,7 @@ public class ManualFillingTestHelper {
} else {
requestHideKeyboardAccessory();
}
mKeyboard.showKeyboard(null);
getKeyboard().showKeyboard(mActivityTestRule.getActivity().getCurrentFocus());
}
public DropdownPopupWindowInterface waitForAutofillPopup(String filterInput)
......@@ -222,7 +169,7 @@ public class ManualFillingTestHelper {
*/
public void clickSubmit() throws TimeoutException, InterruptedException {
DOMUtils.clickNode(mWebContentsRef.get(), "submit");
mKeyboard.hideKeyboard(null);
getKeyboard().hideAndroidSoftKeyboard(null);
}
/**
......@@ -264,7 +211,7 @@ public class ManualFillingTestHelper {
.withCause(new Throwable("No tab at index " + tabIndex))
.build();
}
tabLayout.getTabAt(tabIndex).select();
ThreadUtils.runOnUiThread(() -> tabLayout.getTabAt(tabIndex).select());
}
};
}
......
......@@ -55,6 +55,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.EnableFeatures;
import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.base.WindowAndroid;
import java.lang.ref.WeakReference;
......@@ -71,6 +72,8 @@ public class ManualFillingControllerTest {
@Mock
private WindowAndroid mMockWindow;
@Mock
private KeyboardVisibilityDelegate mMockKeyboard;
@Mock
private ChromeActivity mMockActivity;
@Mock
private KeyboardAccessoryView mMockKeyboardAccessoryView;
......@@ -99,6 +102,7 @@ public class ManualFillingControllerTest {
ShadowRecordHistogram.reset();
MockitoAnnotations.initMocks(this);
when(mMockWindow.getActivity()).thenReturn(new WeakReference<>(mMockActivity));
when(mMockWindow.getKeyboardDelegate()).thenReturn(mMockKeyboard);
when(mMockActivity.getTabModelSelector()).thenReturn(mMockTabModelSelector);
ChromeFullscreenManager fullscreenManager = new ChromeFullscreenManager(mMockActivity, 0);
when(mMockActivity.getFullscreenManager()).thenReturn(fullscreenManager);
......
......@@ -260,6 +260,7 @@ android_library("ui_full_java") {
"java/src/org/chromium/ui/ViewProvider.java",
"java/src/org/chromium/ui/VSyncMonitor.java",
"java/src/org/chromium/ui/base/ActivityAndroidPermissionDelegate.java",
"java/src/org/chromium/ui/base/ActivityKeyboardVisibilityDelegate.java",
"java/src/org/chromium/ui/base/ActivityWindowAndroid.java",
"java/src/org/chromium/ui/base/AndroidPermissionDelegate.java",
"java/src/org/chromium/ui/base/Clipboard.java",
......
......@@ -15,7 +15,7 @@ import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager;
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.ObserverList;
import java.util.concurrent.atomic.AtomicInteger;
......@@ -38,8 +38,21 @@ public class KeyboardVisibilityDelegate {
/** The delegate to determine keyboard visibility. */
private static KeyboardVisibilityDelegate sInstance = new KeyboardVisibilityDelegate();
/** Set this delegate to replace the keyboard in tests. */
private static KeyboardVisibilityDelegate sTestingInstance;
/**
* An interface to notify listeners of changes in the soft keyboard's visibility.
*/
public interface KeyboardVisibilityListener {
/**
* Called whenever the keyboard might have changed.
* @param isShowing A boolean that's true if the keyboard is now visible.
*/
void keyboardVisibilityChanged(boolean isShowing);
}
private final ObserverList<KeyboardVisibilityListener> mKeyboardVisibilityListeners =
new ObserverList<>();
protected void registerKeyboardVisibilityCallbacks() {}
protected void unregisterKeyboardVisibilityCallbacks() {}
/**
* Allows setting a new strategy to override the default {@link KeyboardVisibilityDelegate}.
......@@ -52,31 +65,12 @@ public class KeyboardVisibilityDelegate {
sInstance = delegate;
}
/**
* Setting a test instance of the visibility delegate that won't affect the default instance.
*
* @param delegate A {@link KeyboardVisibilityDelegate} instance.
*/
@VisibleForTesting
public static void setDelegateForTesting(KeyboardVisibilityDelegate delegate) {
sTestingInstance = delegate;
}
/**
* Clears any previously set test instance.
*/
@VisibleForTesting
public static void clearDelegateForTesting() {
sTestingInstance = null;
}
/**
* Prefer using {@link org.chromium.ui.base.WindowAndroid#getKeyboardDelegate()} over this
* method. Both return a delegate which allows checking and influencing the keyboard state.
* @return the global {@link KeyboardVisibilityDelegate}.
*/
public static KeyboardVisibilityDelegate getInstance() {
if (sTestingInstance != null) return sTestingInstance;
return sInstance;
}
......@@ -121,11 +115,21 @@ public class KeyboardVisibilityDelegate {
}
/**
* Hides the soft keyboard by using the {@link Context#INPUT_METHOD_SERVICE}.
* Hides the soft keyboard.
* @param view The {@link View} that is currently accepting input.
* @return Whether the keyboard was visible before.
*/
public boolean hideKeyboard(View view) {
return hideAndroidSoftKeyboard(view);
}
/**
* Hides the soft keyboard by using the {@link Context#INPUT_METHOD_SERVICE}.
* This template method simplifies mocking and the access to the soft keyboard in subclasses.
* @param view The {@link View} that is currently accepting input.
* @return Whether the keyboard was visible before.
*/
protected boolean hideAndroidSoftKeyboard(View view) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
......@@ -134,11 +138,10 @@ public class KeyboardVisibilityDelegate {
/**
* Calculates the keyboard height based on the bottom margin it causes for the given
* rootView. It is used to determine whether the keyboard is visible.
* @param context A {@link Context} instance.
* @param rootView A {@link View}.
* @return The size of the bottom margin which most likely is exactly the keyboard size.
*/
public int calculateKeyboardHeight(Context context, View rootView) {
public int calculateKeyboardHeight(View rootView) {
Rect appRect = new Rect();
rootView.getWindowVisibleDisplayFrame(appRect);
......@@ -185,17 +188,61 @@ public class KeyboardVisibilityDelegate {
return (int) (KEYBOARD_DETECT_BOTTOM_THRESHOLD_DP * density);
}
/**
* Returns whether the keyboard is showing.
* @param context A {@link Context} instance.
* @param view A {@link View}.
* @return Whether or not the software keyboard is visible.
*/
public boolean isKeyboardShowing(Context context, View view) {
return isAndroidSoftKeyboardShowing(context, view);
}
/**
* Detects whether or not the keyboard is showing. This is a best guess based on the height
* of the keyboard as there is no standardized/foolproof way to do this.
* This template method simplifies mocking and the access to the soft keyboard in subclasses.
* @param context A {@link Context} instance.
* @param view A {@link View}.
* @return Whether or not the software keyboard is visible.
*/
public boolean isKeyboardShowing(Context context, View view) {
protected boolean isAndroidSoftKeyboardShowing(Context context, View view) {
View rootView = view.getRootView();
return rootView != null
&& calculateKeyboardHeight(context, rootView)
&& calculateKeyboardHeight(rootView)
> calculateKeyboardDetectionThreshold(context, rootView);
}
/**
* To be called when the keyboard visibility state might have changed. Informs listeners of the
* state change IFF there actually was a change.
* @param isShowing The current (guesstimated) state of the keyboard.
*/
protected void notifyListeners(boolean isShowing) {
for (KeyboardVisibilityListener listener : mKeyboardVisibilityListeners) {
listener.keyboardVisibilityChanged(isShowing);
}
}
/**
* Adds a listener that is updated of keyboard visibility changes. This works as a best guess.
*
* @see org.chromium.ui.KeyboardVisibilityDelegate#isKeyboardShowing(Context, View)
*/
public void addKeyboardVisibilityListener(KeyboardVisibilityListener listener) {
if (mKeyboardVisibilityListeners.isEmpty()) {
registerKeyboardVisibilityCallbacks();
}
mKeyboardVisibilityListeners.addObserver(listener);
}
/**
* @see #addKeyboardVisibilityListener(KeyboardVisibilityListener)
*/
public void removeKeyboardVisibilityListener(KeyboardVisibilityListener listener) {
mKeyboardVisibilityListeners.removeObserver(listener);
if (mKeyboardVisibilityListeners.isEmpty()) {
unregisterKeyboardVisibilityCallbacks();
}
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.ui.base;
import android.app.Activity;
import android.support.annotation.Nullable;
import android.view.View;
import org.chromium.ui.KeyboardVisibilityDelegate;
import java.lang.ref.WeakReference;
/**
* A {@link KeyboardVisibilityDelegate} that listens to a given activity for layout changes. It
* notifies {@link KeyboardVisibilityDelegate.KeyboardVisibilityListener} whenever the layout change
* is suspected to be caused by a keyboard.
*/
public class ActivityKeyboardVisibilityDelegate
extends KeyboardVisibilityDelegate implements View.OnLayoutChangeListener {
private boolean mIsKeyboardShowing;
private WeakReference<Activity> mActivity;
/**
* Creates a new delegate listening to the given activity. If the activity is destroyed, it will
* continue to work as a regular {@link KeyboardVisibilityDelegate}.
* @param activity A {@link WeakReference} to an {@link Activity}.
*/
public ActivityKeyboardVisibilityDelegate(WeakReference<Activity> activity) {
mActivity = activity;
}
public @Nullable Activity getActivity() {
return mActivity.get();
}
@Override
public void registerKeyboardVisibilityCallbacks() {
Activity activity = getActivity();
if (activity == null) return;
View content = activity.findViewById(android.R.id.content);
mIsKeyboardShowing = isKeyboardShowing(activity, content);
content.addOnLayoutChangeListener(this);
}
@Override
public void unregisterKeyboardVisibilityCallbacks() {
Activity activity = getActivity();
if (activity == null) return;
activity.findViewById(android.R.id.content).removeOnLayoutChangeListener(this);
}
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
Activity activity = getActivity();
if (activity == null) return;
boolean isShowing = isKeyboardShowing(activity, v);
if (mIsKeyboardShowing == isShowing) return;
mIsKeyboardShowing = isShowing;
notifyListeners(isShowing);
}
}
\ No newline at end of file
......@@ -10,7 +10,6 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.view.View;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
......@@ -25,8 +24,7 @@ import java.lang.ref.WeakReference;
* Only instantiate this class when you need the implemented features.
*/
public class ActivityWindowAndroid
extends WindowAndroid
implements ApplicationStatus.ActivityStateListener, View.OnLayoutChangeListener {
extends WindowAndroid implements ApplicationStatus.ActivityStateListener {
// Constants used for intent request code bounding.
private static final int REQUEST_CODE_PREFIX = 1000;
private static final int REQUEST_CODE_RANGE_SIZE = 100;
......@@ -61,6 +59,7 @@ public class ActivityWindowAndroid
ApplicationStatus.registerStateListenerForActivity(this, activity);
}
setKeyboardDelegate(createKeyboardVisibilityDelegate());
setAndroidPermissionDelegate(createAndroidPermissionDelegate());
}
......@@ -68,20 +67,13 @@ public class ActivityWindowAndroid
return new ActivityAndroidPermissionDelegate(getActivity());
}
@Override
protected void registerKeyboardVisibilityCallbacks() {
Activity activity = getActivity().get();
if (activity == null) return;
View content = activity.findViewById(android.R.id.content);
mIsKeyboardShowing = getKeyboardDelegate().isKeyboardShowing(getActivity().get(), content);
content.addOnLayoutChangeListener(this);
protected ActivityKeyboardVisibilityDelegate createKeyboardVisibilityDelegate() {
return new ActivityKeyboardVisibilityDelegate(getActivity());
}
@Override
protected void unregisterKeyboardVisibilityCallbacks() {
Activity activity = getActivity().get();
if (activity == null) return;
activity.findViewById(android.R.id.content).removeOnLayoutChangeListener(this);
public ActivityKeyboardVisibilityDelegate getKeyboardDelegate() {
return (ActivityKeyboardVisibilityDelegate) super.getKeyboardDelegate();
}
@Override
......@@ -167,7 +159,7 @@ public class ActivityWindowAndroid
@Override
public WeakReference<Activity> getActivity() {
return new WeakReference<Activity>(activityFromContext(getContext().get()));
return new WeakReference<>(activityFromContext(getContext().get()));
}
@Override
......@@ -190,13 +182,6 @@ public class ActivityWindowAndroid
: super.getActivityState();
}
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
keyboardVisibilityPossiblyChanged(
getKeyboardDelegate().isKeyboardShowing(getActivity().get(), v));
}
private int generateNextRequestCode() {
int requestCode = REQUEST_CODE_PREFIX + mNextRequestCode;
mNextRequestCode = (mNextRequestCode + 1) % REQUEST_CODE_RANGE_SIZE;
......
......@@ -51,6 +51,8 @@ import java.util.HashSet;
@JNINamespace("ui")
public class WindowAndroid implements AndroidPermissionDelegate {
private static final String TAG = "WindowAndroid";
private KeyboardVisibilityDelegate mKeyboardVisibilityDelegate =
KeyboardVisibilityDelegate.getInstance();
@TargetApi(Build.VERSION_CODES.KITKAT)
private class TouchExplorationMonitor {
......@@ -100,8 +102,6 @@ public class WindowAndroid implements AndroidPermissionDelegate {
private HashSet<Animator> mAnimationsOverContent = new HashSet<>();
private View mAnimationPlaceholderView;
protected boolean mIsKeyboardShowing;
// System accessibility service.
private final AccessibilityManager mAccessibilityManager;
......@@ -118,15 +118,6 @@ public class WindowAndroid implements AndroidPermissionDelegate {
private boolean mPendingVSyncRequest;
private boolean mVSyncPaused;
/**
* An interface to notify listeners of changes in the soft keyboard's visibility.
*/
public interface KeyboardVisibilityListener {
public void keyboardVisibilityChanged(boolean isShowing);
}
private ObserverList<KeyboardVisibilityListener> mKeyboardVisibilityListeners =
new ObserverList<>();
/**
* An interface to notify listeners that a context menu is closed.
*/
......@@ -691,40 +682,19 @@ public class WindowAndroid implements AndroidPermissionDelegate {
}
}
protected void registerKeyboardVisibilityCallbacks() {
}
protected void unregisterKeyboardVisibilityCallbacks() {
}
/**
* Adds a listener that is updated of keyboard visibility changes. This works as a best guess.
*
* @see org.chromium.ui.KeyboardVisibilityDelegate#isKeyboardShowing(Context, View)
*/
public void addKeyboardVisibilityListener(KeyboardVisibilityListener listener) {
if (mKeyboardVisibilityListeners.isEmpty()) {
registerKeyboardVisibilityCallbacks();
}
mKeyboardVisibilityListeners.addObserver(listener);
}
/**
* @see #addKeyboardVisibilityListener(KeyboardVisibilityListener)
*/
public void removeKeyboardVisibilityListener(KeyboardVisibilityListener listener) {
mKeyboardVisibilityListeners.removeObserver(listener);
if (mKeyboardVisibilityListeners.isEmpty()) {
unregisterKeyboardVisibilityCallbacks();
}
}
/**
* The returned {@link KeyboardVisibilityDelegate} can read and influence the soft keyboard.
* @return a {@link KeyboardVisibilityDelegate} specific for this window.
*/
public KeyboardVisibilityDelegate getKeyboardDelegate() {
return KeyboardVisibilityDelegate.getInstance();
return mKeyboardVisibilityDelegate;
}
@VisibleForTesting
public void setKeyboardDelegate(KeyboardVisibilityDelegate keyboardDelegate) {
mKeyboardVisibilityDelegate = keyboardDelegate;
// TODO(fhorschig): Remove - every caller should use the window to get the delegate.
KeyboardVisibilityDelegate.setInstance(keyboardDelegate);
}
/**
......@@ -753,20 +723,6 @@ public class WindowAndroid implements AndroidPermissionDelegate {
}
}
/**
* To be called when the keyboard visibility state might have changed. Informs listeners of the
* state change IFF there actually was a change.
* @param isShowing The current (guesstimated) state of the keyboard.
*/
protected void keyboardVisibilityPossiblyChanged(boolean isShowing) {
if (mIsKeyboardShowing == isShowing) return;
mIsKeyboardShowing = isShowing;
for (KeyboardVisibilityListener listener : mKeyboardVisibilityListeners) {
listener.keyboardVisibilityChanged(isShowing);
}
}
/**
* Start a post-layout animation on top of web content.
*
......
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