Commit cc442f6e authored by Pavel Yatsuk's avatar Pavel Yatsuk Committed by Commit Bot

Explanation dialog for blocked permission interaction

This CL adds a dialog that is displayed when interaction with permission
dialog is blocked because of overlay window covering permission dialog
buttons.

BUG=1014477

Change-Id: I1ab7a6342f77f28e3179a4f5129d8fcf292f3019
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2198365
Commit-Queue: Pavel Yatsuk <pavely@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#771356}
parent 8df8b249
......@@ -64,6 +64,7 @@ public class ModalDialogView extends BoundedLinearLayout implements View.OnClick
private boolean mTitleScrollable;
private boolean mFilterTouchForSecurity;
private boolean mFilteredTouchResultRecorded;
private Runnable mOnTouchFilteredCallback;
/**
* Constructor for inflating from XML.
......@@ -221,6 +222,10 @@ public class ModalDialogView extends BoundedLinearLayout implements View.OnClick
: SecurityFilteredTouchResult.HANDLED,
SecurityFilteredTouchResult.NUM_ENTRIES);
}
if (shouldBlockTouchEvent && mOnTouchFilteredCallback != null
&& ev.getAction() == MotionEvent.ACTION_DOWN) {
mOnTouchFilteredCallback.run();
}
} catch (NoSuchFieldException | IllegalAccessException e) {
Log.e(TAG, "Reflection failure: " + e);
}
......@@ -233,6 +238,14 @@ public class ModalDialogView extends BoundedLinearLayout implements View.OnClick
negativeButton.setOnTouchListener(onTouchListener);
}
/**
* @param callback The callback is called when touch event is filtered because of an overlay
* window.
*/
void setOnTouchFilteredCallback(Runnable callback) {
mOnTouchFilteredCallback = callback;
}
/** @param message The message in the dialog content. */
void setMessage(String message) {
mMessageView.setText(message);
......
......@@ -61,6 +61,9 @@ public class ModalDialogViewBinder
assert checkFilterTouchConsistency(model);
view.setFilterTouchForSecurity(
model.get(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY));
} else if (ModalDialogProperties.TOUCH_FILTERED_CALLBACK == propertyKey) {
view.setOnTouchFilteredCallback(
model.get(ModalDialogProperties.TOUCH_FILTERED_CALLBACK));
} else if (ModalDialogProperties.CONTENT_DESCRIPTION == propertyKey) {
// Intentionally left empty since this is a property used for the dialog container.
} else if (ModalDialogProperties.PRIMARY_BUTTON_FILLED == propertyKey) {
......
......@@ -297,6 +297,9 @@
<message name="IDS_MENU_ITEM_MOVE_TO_TOP" desc="Option in item menu. User can click the 'Move to top' option to move the item up to the top of its list. [CHAR-LIMIT=24]">
Move to top
</message>
<message name="IDS_OPEN_SETTINGS" desc="Generic label for a button to show settings screen. [CHAR-LIMIT=20]">
Open settings
</message>
<message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_MENU" desc="Content description for the settings menu button.">
More options
......
......@@ -6,14 +6,20 @@ package org.chromium.components.permissions;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.provider.Settings;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.BuildInfo;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modaldialog.SimpleModalDialogController;
import org.chromium.ui.modelutil.PropertyModel;
import java.lang.annotation.Retention;
......@@ -45,6 +51,7 @@ public class PermissionDialogController
}
private PropertyModel mDialogModel;
private PropertyModel mOverlayDetectedDialogModel;
private PermissionDialogDelegate mDialogDelegate;
private ModalDialogManager mModalDialogManager;
......@@ -158,11 +165,52 @@ public class PermissionDialogController
}
mModalDialogManager = mDialogDelegate.getWindow().getModalDialogManager();
mDialogModel = PermissionDialogModel.getModel(this, mDialogDelegate);
mDialogModel = PermissionDialogModel.getModel(
this, mDialogDelegate, () -> showFilteredTouchEventDialog(activity));
mModalDialogManager.showDialog(mDialogModel, ModalDialogManager.ModalDialogType.TAB);
mState = State.PROMPT_OPEN;
}
/**
* Displays the dialog explaining that Chrome has detected an overlay. Offers the user to close
* overlay window or revoke "Draw on top" permission in Android settings.
*/
private void showFilteredTouchEventDialog(Context context) {
// Settings.ACTION_MANAGE_OVERLAY_PERMISSION is only supported on M+ therefore we shouldn't
// display this dialog on L. The function won't be called on L anyway because touch
// filtering was introduced in M.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
// Don't show another dialog if one is already displayed.
if (mOverlayDetectedDialogModel != null) return;
ModalDialogProperties.Controller overlayDetectedDialogController =
new SimpleModalDialogController(mModalDialogManager, (Integer dismissalCause) -> {
if (dismissalCause == DialogDismissalCause.POSITIVE_BUTTON_CLICKED) {
context.startActivity(
new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));
}
mOverlayDetectedDialogModel = null;
});
mOverlayDetectedDialogModel =
new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
.with(ModalDialogProperties.CONTROLLER, overlayDetectedDialogController)
.with(ModalDialogProperties.TITLE,
context.getString(R.string.overlay_detected_dialog_title,
BuildInfo.getInstance().hostPackageLabel))
.with(ModalDialogProperties.MESSAGE, context.getResources(),
R.string.overlay_detected_dialog_message)
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, context.getResources(),
R.string.open_settings)
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, context.getResources(),
R.string.try_again)
.with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
.build();
mModalDialogManager.showDialog(
mOverlayDetectedDialogModel, ModalDialogManager.ModalDialogType.APP, true);
}
public void dismissFromNative(PermissionDialogDelegate delegate) {
if (mDialogDelegate == delegate) {
// Some caution is required here to handle cases where the user actions or dismisses
......
......@@ -21,8 +21,8 @@ import org.chromium.ui.modelutil.PropertyModel;
* This class creates the model for permission dialog.
*/
class PermissionDialogModel {
public static PropertyModel getModel(
ModalDialogProperties.Controller controller, PermissionDialogDelegate delegate) {
public static PropertyModel getModel(ModalDialogProperties.Controller controller,
PermissionDialogDelegate delegate, Runnable touchFilteredCallback) {
Context context = delegate.getWindow().getContext().get();
assert context != null;
View customView = loadDialogView(context);
......@@ -42,6 +42,7 @@ class PermissionDialogModel {
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, delegate.getSecondaryButtonText())
.with(ModalDialogProperties.CONTENT_DESCRIPTION, delegate.getMessageText())
.with(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY, true)
.with(ModalDialogProperties.TOUCH_FILTERED_CALLBACK, touchFilteredCallback)
.build();
}
......
......@@ -195,6 +195,14 @@
<message name="IDS_NFC_PROMPT_TURN_ON" desc="Text on the positive button of the nfc prompt">
Turn on
</message>
<!-- Overlay detected dialog -->
<message name="IDS_OVERLAY_DETECTED_DIALOG_TITLE" desc="Title of the dialog that informs the user about detected overlay window that prevents interaction with permissions.">
Another app is displaying over <ph name="APP_NAME">%1$s<ex>Chrome</ex></ph>
</message>
<message name="IDS_OVERLAY_DETECTED_DIALOG_MESSAGE" desc="Dialog message that informs the user about detected overlay window that prevents interaction with permissions.">
To change the permission for this site, close the other app and try again.\n\nIf you can’t close the app, turn off the app’s permission to “Display over other apps” in Android settings.
</message>
</messages>
</release>
</grit>
......@@ -113,6 +113,13 @@ public class ModalDialogProperties {
public static final ReadableBooleanPropertyKey FILTER_TOUCH_FOR_SECURITY =
new ReadableBooleanPropertyKey();
/**
* Callback to be called when the modal dialog filters touch events because the buttons are
* obscured by another window.
*/
public static final ReadableObjectPropertyKey<Runnable> TOUCH_FILTERED_CALLBACK =
new ReadableObjectPropertyKey<>();
/** Whether the title is scrollable with the message. */
public static final WritableBooleanPropertyKey TITLE_SCROLLABLE =
new WritableBooleanPropertyKey();
......@@ -125,5 +132,6 @@ public class ModalDialogProperties {
TITLE, TITLE_ICON, MESSAGE, CUSTOM_VIEW, POSITIVE_BUTTON_TEXT,
POSITIVE_BUTTON_CONTENT_DESCRIPTION, POSITIVE_BUTTON_DISABLED, NEGATIVE_BUTTON_TEXT,
NEGATIVE_BUTTON_CONTENT_DESCRIPTION, NEGATIVE_BUTTON_DISABLED, CANCEL_ON_TOUCH_OUTSIDE,
FILTER_TOUCH_FOR_SECURITY, TITLE_SCROLLABLE, PRIMARY_BUTTON_FILLED};
FILTER_TOUCH_FOR_SECURITY, TOUCH_FILTERED_CALLBACK, TITLE_SCROLLABLE,
PRIMARY_BUTTON_FILLED};
}
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