Commit 61e32fb9 authored by Matthew Jones's avatar Matthew Jones Committed by Commit Bot

Add support for infobars on touchless devices

This patch adds the ability for infobars to spec touchless support
for themselves. Each infobar has the option to state whether it
supports touchless mode and provide a model for the touchless UI
surface to use.

Bug: 930746
Change-Id: If4a5a81c08b06c48b201a87320b2ad213ff496cc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1582835
Commit-Queue: Matthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#654580}
parent e8e7d0f5
......@@ -6,14 +6,22 @@ package org.chromium.chrome.browser.infobar;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.CallSuper;
import android.support.annotation.ColorRes;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.TextView;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogProperties;
import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogProperties.Priority;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modelutil.PropertyModel;
/**
* The base class for all InfoBar classes.
......@@ -35,6 +43,8 @@ public abstract class InfoBar implements InfoBarView {
private boolean mIsDismissed;
private boolean mControlsEnabled = true;
private @Nullable PropertyModel mModel;
// This points to the InfoBarAndroid class not any of its subclasses.
private long mNativeInfoBarPtr;
......@@ -110,6 +120,63 @@ public abstract class InfoBar implements InfoBarView {
return mView;
}
/**
* Create a property model for view systems that use this rather than a custom view.
* @return A new property model.
*/
@CallSuper
protected PropertyModel createModel() {
Drawable icon;
if (mIconBitmap != null) {
icon = new BitmapDrawable(mIconBitmap);
} else {
icon = ApiCompatibilityUtils.getDrawable(getContext().getResources(), mIconDrawableId);
}
PropertyModel model =
new PropertyModel.Builder(TouchlessDialogProperties.ALL_DIALOG_KEYS)
.with(TouchlessDialogProperties.IS_FULLSCREEN, false)
.with(TouchlessDialogProperties.PRIORITY, Priority.HIGH)
.with(TouchlessDialogProperties.CANCEL_ACTION,
view -> onCloseButtonClicked())
.with(TouchlessDialogProperties.CANCEL_NAME,
mContext.getResources().getString(R.string.cancel))
.with(TouchlessDialogProperties.SELECT_NAME,
mContext.getResources().getString(R.string.select))
.with(TouchlessDialogProperties.ALT_ACTION, null)
.with(TouchlessDialogProperties.ALT_NAME, null)
.with(ModalDialogProperties.TITLE, mMessage.toString())
.with(ModalDialogProperties.TITLE_ICON, icon)
.with(ModalDialogProperties.CONTROLLER,
new ModalDialogProperties.Controller() {
@Override
public void onClick(PropertyModel model, int buttonType) {}
@Override
public void onDismiss(PropertyModel model, int dismissalCause) {
mContainer.removeInfoBar(InfoBar.this);
}
})
.build();
mModel = model;
return model;
}
/**
* @return The model for this infobar if one was created.
*/
@Nullable
PropertyModel getModel() {
return mModel;
}
/**
* @return Whether this InfoBar is supported in touchless mode.
*/
protected boolean supportsTouchlessMode() {
return false;
}
/**
* If this returns true, the infobar contents will be replaced with a one-line layout.
* When overriding this, also override {@link #getAccessibilityMessage}.
......
......@@ -19,12 +19,15 @@ import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.chrome.browser.util.FeatureUtilities;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.KeyboardVisibilityDelegate.KeyboardVisibilityListener;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
import java.util.ArrayList;
......@@ -288,6 +291,18 @@ public class InfoBarContainer implements UserData, KeyboardVisibilityListener {
return;
}
infoBar.setContext(mInfoBarContainerView.getContext());
infoBar.setInfoBarContainer(this);
// TODO(957153): In touchless we will be needlessly logging show/close events. We should
// clean this up to avoid skewing the metrics.
if (FeatureUtilities.isNoTouchModeEnabled() && !infoBar.supportsTouchlessMode()) {
// Pose the closing of the infobar as the native container does not handle closing the
// infobar during the show process well (native will crash).
mTab.getView().post(() -> infoBar.onCloseButtonClicked());
return;
}
// We notify observers immediately (before the animation starts).
for (InfoBarContainerObserver observer : mObservers) {
observer.onAddInfoBar(this, infoBar, mInfoBars.isEmpty());
......@@ -299,10 +314,13 @@ public class InfoBarContainer implements UserData, KeyboardVisibilityListener {
// notify it's been added, as tests rely on this notification but expects the infobar view
// to be available when they get the notification.
mInfoBars.add(infoBar);
infoBar.setContext(mInfoBarContainerView.getContext());
infoBar.setInfoBarContainer(this);
mInfoBarContainerView.addInfoBar(infoBar);
if (FeatureUtilities.isNoTouchModeEnabled() && infoBar.supportsTouchlessMode()) {
mTab.getActivity().getModalDialogManager().showDialog(
infoBar.createModel(), ModalDialogType.APP);
} else {
mInfoBarContainerView.addInfoBar(infoBar);
}
}
/**
......@@ -347,18 +365,29 @@ public class InfoBarContainer implements UserData, KeyboardVisibilityListener {
assert !mDestroyed;
if (!mInfoBars.remove(infoBar)) {
// In touchless mode, an infobar can be removed without technically being added. This
// is allowed to support whitelisted infobars (while easily blocking the rest).
if (FeatureUtilities.isNoTouchModeEnabled()) return;
assert false : "Trying to remove an InfoBar that is not in this container.";
return;
}
if (infoBar.supportsTouchlessMode() && FeatureUtilities.isNoTouchModeEnabled()
&& infoBar.getModel() != null) {
mTab.getActivity().getModalDialogManager().dismissDialog(
infoBar.getModel(), DialogDismissalCause.UNKNOWN);
}
// Notify observers immediately, before any animations begin.
for (InfoBarContainerObserver observer : mObservers) {
observer.onRemoveInfoBar(this, infoBar, mInfoBars.isEmpty());
}
assert mInfoBarContainerView
!= null : "The container view is null when removing an InfoBar.";
mInfoBarContainerView.removeInfoBar(infoBar);
if (!FeatureUtilities.isNoTouchModeEnabled()) {
assert mInfoBarContainerView
!= null : "The container view is null when removing an InfoBar.";
mInfoBarContainerView.removeInfoBar(infoBar);
}
}
/**
......
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