Commit e10e6939 authored by Hazem Ashmawy's avatar Hazem Ashmawy Committed by Commit Bot

[AW] DevUI: Improve different webview provider error message

Refactor the PersistentErrorView class into a Text message with an
always yellow background and black text with an optional button for
a quick action.

Use round alert error icon currently in drawable resources instead of
the framework triangle white icon, to match the black text color.

Refactor WebViewPackageError to show a more informative message with a
quick action to:
- Change WebView Provider if it's possible.
- If WebView provider cannot be changed, then offer to open DevTools
  from the current provider if it has a valid DevTools implementation.
Clicking the message body will still open a dialog to offer a more
detailed message and both options to open current WebView provider
DevTools if possible or change WebView if possible.

Stop showing the different package error dialog at the start of the UI.

This will also fix the presubmit warning because of using setTextColor.

Fixed: 1059475,1061164,1052192,1062406
Change-Id: I67176896580143e5e962991a36faf0576e156090
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2098689
Commit-Queue: Hazem Ashmawy <hazems@chromium.org>
Reviewed-by: default avatarNate Fischer <ntfschr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#756115}
parent 03fb2593
......@@ -5,16 +5,36 @@
LICENSE file.
-->
<TextView
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:visibility="gone"
android:id="@+id/crashes_list_activity_layout"
android:background="@color/warning_yellow"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="italic"
android:drawableStart="@android:drawable/stat_notify_error"
android:drawablePadding="8dp"
android:paddingTop="20dp"
android:paddingBottom="20dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/>
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:paddingBottom="5dp"
android:visibility="gone">
<TextView
android:id="@+id/error_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
style="?android:attr/textAppearanceMedium"
android:drawableStart="@drawable/ic_alert_error"
android:drawablePadding="15dp"
android:paddingTop="15dp"
android:paddingBottom="10dp"
android:textColor="@android:color/black"/>
<Button
android:id="@+id/action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/buttonBarButtonStyle"
android:textColor="@android:color/black"
android:layout_gravity="end"
android:visibility="gone"/>
</LinearLayout>
\ No newline at end of file
......@@ -463,8 +463,7 @@ public class CrashesListFragment extends Fragment {
}
Activity activity = (Activity) mContext;
return new PersistentErrorView(
activity, R.id.crash_consent_error, PersistentErrorView.Type.ERROR)
return new PersistentErrorView(activity, R.id.crash_consent_error)
.setText("Crash collection is disabled. Tap for more info.")
.setDialog(dialogBuilder.create());
}
......
......@@ -51,8 +51,6 @@ public class MainActivity extends FragmentActivity {
mSwitchFragmentOnResume = true;
mDifferentPackageError = new WebViewPackageError(this);
// show the dialog once when the activity is created.
mDifferentPackageError.showDialogIfDifferent();
// Set up bottom navigation bar:
mFragmentIdMap.put(R.id.navigation_home, FRAGMENT_ID_HOME);
......
......@@ -6,44 +6,26 @@ package org.chromium.android_webview.devui;
import android.app.Activity;
import android.app.Dialog;
import android.graphics.Color;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.IdRes;
/**
* Shows a text message at the top of a LinearLayout to show error and warning messages.
* Shows a text message at the top of a Layout to show error messages.
*/
public class PersistentErrorView {
/**
* Error message type.
*/
public enum Type {
ERROR,
WARNING,
}
private TextView mTextView;
private ViewGroup mViewGroup;
/**
* @param context The Activity where this View is shon.
* @param type View type.
*/
public PersistentErrorView(Activity context, @IdRes int errorViewId, Type type) {
mTextView = (TextView) context.findViewById(errorViewId);
switch (type) {
case ERROR:
mTextView.setBackgroundResource(R.color.error_red);
mTextView.setTextColor(Color.WHITE);
break;
case WARNING:
mTextView.setBackgroundResource(R.color.warning_yellow);
mTextView.setTextColor(Color.BLACK);
break;
}
public PersistentErrorView(Activity context, @IdRes int errorViewId) {
mViewGroup = (ViewGroup) context.findViewById(errorViewId);
}
/**
......@@ -52,7 +34,7 @@ public class PersistentErrorView {
* @return object reference for chaining.
*/
public PersistentErrorView setOnClickListener(OnClickListener listener) {
mTextView.setOnClickListener(listener);
mViewGroup.setOnClickListener(listener);
return this;
}
......@@ -62,7 +44,30 @@ public class PersistentErrorView {
* @return object reference for chaining.
*/
public PersistentErrorView setDialog(Dialog dialog) {
setOnClickListener(v -> dialog.show());
if (dialog == null) {
setOnClickListener(null);
} else {
setOnClickListener(v -> dialog.show());
}
return this;
}
/**
* Set and show the main action button. If {@code text} is {@null} the button will be hidden.
* @param text Button text.
* @param listener the listener to execute when the button is clicked.
* @return object reference for chaining.
*/
public PersistentErrorView setActionButton(String text, OnClickListener listener) {
Button button = (Button) mViewGroup.findViewById(R.id.action_button);
if (text == null) {
button.setVisibility(View.GONE);
button.setOnClickListener(null);
} else {
button.setVisibility(View.VISIBLE);
button.setText(text);
button.setOnClickListener(listener);
}
return this;
}
......@@ -72,7 +77,8 @@ public class PersistentErrorView {
* @return object reference for chaining.
*/
public PersistentErrorView setText(String text) {
mTextView.setText(text);
TextView textView = (TextView) mViewGroup.findViewById(R.id.error_text);
textView.setText(text);
return this;
}
......@@ -80,13 +86,13 @@ public class PersistentErrorView {
* Show the view by setting its visibility.
*/
public void show() {
mTextView.setVisibility(View.VISIBLE);
mViewGroup.setVisibility(View.VISIBLE);
}
/**
* Hide the view by setting its visibility.
*/
public void hide() {
mTextView.setVisibility(View.GONE);
mViewGroup.setVisibility(View.GONE);
}
}
......@@ -22,14 +22,22 @@ import java.util.Locale;
/**
* A helper class to yield an error if the the UI is launched from a different WebView package other
* than the selected package by the system. It shows a persistent error message at the top of the
* activity's root linear layout as well as an alert dialogue to change WebView implementation.
* activity's root linear layout.
*/
public class WebViewPackageError {
private static final String TAG = "WebViewDevTools";
private PersistentErrorView mErrorMessage;
private Activity mContext;
private Dialog mErrorDialog;
private static final String OPEN_WEBVIEW_PROVIDER_BUTTON_TEXT =
"Open DevTools in current provider";
private static final String CHANGE_WEBVIEW_PROVIDER_BUTTON_TEXT = "Change provider";
private static final String NO_VALID_WEBVIEW_MESSAGE =
"Cannot find a valid WebView provider installed. "
+ "Please install a valid WebView package. Contact "
+ "android-webview-dev@chromium.org for help.";
/**
* @param context The {@link Activity} where the error is yield.
......@@ -37,12 +45,7 @@ public class WebViewPackageError {
*/
public WebViewPackageError(Activity context) {
mContext = context;
mErrorDialog = buildDifferentPackageErrorDialog();
mErrorMessage = new PersistentErrorView(
context, R.id.webview_package_error, PersistentErrorView.Type.WARNING)
.setText("Warning: different WebView provider - Tap for more info.")
.setDialog(mErrorDialog);
mErrorMessage = new PersistentErrorView(context, R.id.webview_package_error);
}
/**
......@@ -50,85 +53,65 @@ public class WebViewPackageError {
* different WebView implementation. Hide it otherwise.
*/
public void showMessageIfDifferent() {
if (isWebViewPackageDifferent()) {
mErrorMessage.show();
} else {
if (WebViewPackageHelper.isCurrentSystemWebViewImplementation(mContext)) {
mErrorMessage.hide();
} else {
buildErrorMessage();
mErrorMessage.show();
}
}
/**
* Show an {@link AlertDialog} about the different WebView package error with an action to
* launch system settings to show and change WebView implementation.
*/
public void showDialogIfDifferent() {
if (isWebViewPackageDifferent()) {
mErrorDialog.show();
private void buildErrorMessage() {
if (!WebViewPackageHelper.hasValidWebViewImplementation(mContext)) {
mErrorMessage.setText(NO_VALID_WEBVIEW_MESSAGE);
// Unset action button and dialog. In case if the device got into the state where there
// is no valid WebView implementation while the UI is running and the button and dialog
// were set for other actions.
mErrorMessage.setActionButton(null, null);
mErrorMessage.setDialog(null);
return;
}
}
private boolean isWebViewPackageDifferent() {
PackageInfo systemWebViewPackage = WebViewPackageHelper.getCurrentWebViewPackage(mContext);
if (systemWebViewPackage == null) {
Log.e(TAG, "Could not find a valid WebView implementation");
buildNoValidWebViewPackageDialog().show();
return true;
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
CharSequence label = WebViewPackageHelper.loadLabel(mContext);
mErrorMessage.setText(String.format(
Locale.US, "%s is not the system's current selected WebView provider", label));
dialogBuilder.setTitle("Different WebView Provider");
dialogBuilder.setMessage(String.format(Locale.US,
"You are using DevTools for (%s) which is not the system's currently selected "
+ "WebView provider",
label));
boolean canOpenCurrentProvider = canOpenCurrentWebViewProviderDevTools();
boolean canChangeProvider = canChangeWebViewProvider();
if (canChangeProvider) {
mErrorMessage.setActionButton(
CHANGE_WEBVIEW_PROVIDER_BUTTON_TEXT, v -> openChangeWebViewProviderSettings());
} else if (canOpenCurrentProvider) {
mErrorMessage.setActionButton(
OPEN_WEBVIEW_PROVIDER_BUTTON_TEXT, v -> openCurrentWebViewProviderDevTools());
}
return !mContext.getPackageName().equals(systemWebViewPackage.packageName);
}
private Dialog buildDifferentPackageErrorDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("Wrong WebView DevTools")
.setMessage(String.format(Locale.US,
"This app (%s) is not the system's selected WebView provider.",
WebViewPackageHelper.loadLabel(mContext)));
builder.setPositiveButton("Open the current WebView provider", (dialog, id) -> {
PackageInfo systemWebViewPackage =
WebViewPackageHelper.getCurrentWebViewPackage(mContext);
if (systemWebViewPackage == null) {
Log.e(TAG, "Could not find a valid WebView implementation");
buildNoValidWebViewPackageDialog().show();
return;
}
Intent intent = new Intent("com.android.webview.SHOW_DEV_UI");
intent.setPackage(systemWebViewPackage.packageName);
// Check if the intent is resolved, i.e current system WebView package has a developer
// UI that responds to "com.android.webview.SHOW_DEV_UI" action.
List<ResolveInfo> resolveInfo =
mContext.getPackageManager().queryIntentActivities(intent, 0);
if (resolveInfo.isEmpty()) {
buildNoDevToolsDialog(systemWebViewPackage.packageName).show();
return;
}
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
mContext.finishAndRemoveTask();
});
// Switching WebView providers is possible from API >= 24.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setNeutralButton("Change WebView provider",
(dialog, id)
-> mContext.startActivity(
new Intent(Settings.ACTION_WEBVIEW_SETTINGS)));
if (canOpenCurrentProvider) {
dialogBuilder.setPositiveButton(OPEN_WEBVIEW_PROVIDER_BUTTON_TEXT,
(d, id) -> openCurrentWebViewProviderDevTools());
}
if (canChangeProvider) {
dialogBuilder.setNeutralButton(CHANGE_WEBVIEW_PROVIDER_BUTTON_TEXT,
(d, id) -> openChangeWebViewProviderSettings());
}
return builder.create();
mErrorMessage.setDialog(dialogBuilder.create());
}
private Dialog buildNoValidWebViewPackageDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("No Valid WebView")
.setMessage("Cannot find a valid WebView provider installed. "
+ "Please install a valid WebView package. Contact "
+ "android-webview-dev@chromium.org for help.");
builder.setTitle("No Valid WebView").setMessage(NO_VALID_WEBVIEW_MESSAGE);
return builder.create();
}
private Dialog buildNoDevToolsDialog(String sytemWebViewPackageName) {
private Dialog buildNoDevToolsDialog() {
PackageInfo systemWebViewPackage = WebViewPackageHelper.getCurrentWebViewPackage(mContext);
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("DevTools Not Found")
.setMessage(String.format(Locale.US,
......@@ -136,16 +119,66 @@ public class WebViewPackageError {
+ "WebView provider selected by the system (%s).\n\n"
+ "Please update to a newer version or select a different WebView "
+ "provider.",
sytemWebViewPackageName));
systemWebViewPackage.packageName));
// Switching WebView providers is possible from API >= 24.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setPositiveButton("Change WebView provider",
(dialog, id)
-> mContext.startActivity(
new Intent(Settings.ACTION_WEBVIEW_SETTINGS)));
if (canChangeWebViewProvider()) {
builder.setPositiveButton(CHANGE_WEBVIEW_PROVIDER_BUTTON_TEXT,
(dialog, id) -> openChangeWebViewProviderSettings());
}
return builder.create();
}
/**
* Check if the system current selceted WebView provider has a valid DevTools implementation.
*/
private boolean canOpenCurrentWebViewProviderDevTools() {
PackageInfo systemWebViewPackage = WebViewPackageHelper.getCurrentWebViewPackage(mContext);
if (systemWebViewPackage == null) {
Log.e(TAG, "Could not find a valid WebView implementation");
return false;
}
Intent intent = buildWebViewDevUiIntent(systemWebViewPackage.packageName);
// Check if the intent is resolved, i.e current system WebView package has a developer
// UI that responds to "com.android.webview.SHOW_DEV_UI" action.
List<ResolveInfo> resolveInfo =
mContext.getPackageManager().queryIntentActivities(intent, 0);
return !resolveInfo.isEmpty();
}
private void openCurrentWebViewProviderDevTools() {
if (!WebViewPackageHelper.hasValidWebViewImplementation(mContext)) {
buildNoValidWebViewPackageDialog().show();
return;
}
if (!canOpenCurrentWebViewProviderDevTools()) {
buildNoDevToolsDialog().show();
return;
}
PackageInfo systemWebViewPackage = WebViewPackageHelper.getCurrentWebViewPackage(mContext);
Intent intent = buildWebViewDevUiIntent(systemWebViewPackage.packageName);
mContext.startActivity(intent);
}
/**
* Builds the intent used to open DevTools from the given {@code packageName}.
*/
private Intent buildWebViewDevUiIntent(String packageName) {
Intent intent = new Intent("com.android.webview.SHOW_DEV_UI");
intent.setPackage(packageName);
// If the UI is already launched then clear its task stack.
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Launch the new UI in a new task.
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
private boolean canChangeWebViewProvider() {
// Switching WebView providers is possible from API >= 24.
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N);
}
private void openChangeWebViewProviderSettings() {
mContext.startActivity(new Intent(Settings.ACTION_WEBVIEW_SETTINGS));
}
}
......@@ -134,6 +134,13 @@ public final class WebViewPackageHelper {
return context.getPackageName().equals(systemWebViewPackage.packageName);
}
/**
* Check if the system currently has a valid WebView implementation.
*/
public static boolean hasValidWebViewImplementation(Context context) {
return getCurrentWebViewPackage(context) != null;
}
/**
* Loads a label for the app specified by {@code mContext}. This is designed to be consistent
* with how the system's WebView chooser labels WebView packages (see {@code
......
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