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 @@ ...@@ -5,16 +5,36 @@
LICENSE file. LICENSE file.
--> -->
<TextView <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" 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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium" android:paddingStart="10dp"
android:textStyle="italic" android:paddingEnd="10dp"
android:drawableStart="@android:drawable/stat_notify_error" android:paddingBottom="5dp"
android:drawablePadding="8dp" android:visibility="gone">
android:paddingTop="20dp"
android:paddingBottom="20dp" <TextView
android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:id="@+id/error_text"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/> 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 { ...@@ -463,8 +463,7 @@ public class CrashesListFragment extends Fragment {
} }
Activity activity = (Activity) mContext; Activity activity = (Activity) mContext;
return new PersistentErrorView( return new PersistentErrorView(activity, R.id.crash_consent_error)
activity, R.id.crash_consent_error, PersistentErrorView.Type.ERROR)
.setText("Crash collection is disabled. Tap for more info.") .setText("Crash collection is disabled. Tap for more info.")
.setDialog(dialogBuilder.create()); .setDialog(dialogBuilder.create());
} }
......
...@@ -51,8 +51,6 @@ public class MainActivity extends FragmentActivity { ...@@ -51,8 +51,6 @@ public class MainActivity extends FragmentActivity {
mSwitchFragmentOnResume = true; mSwitchFragmentOnResume = true;
mDifferentPackageError = new WebViewPackageError(this); mDifferentPackageError = new WebViewPackageError(this);
// show the dialog once when the activity is created.
mDifferentPackageError.showDialogIfDifferent();
// Set up bottom navigation bar: // Set up bottom navigation bar:
mFragmentIdMap.put(R.id.navigation_home, FRAGMENT_ID_HOME); mFragmentIdMap.put(R.id.navigation_home, FRAGMENT_ID_HOME);
......
...@@ -6,44 +6,26 @@ package org.chromium.android_webview.devui; ...@@ -6,44 +6,26 @@ package org.chromium.android_webview.devui;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.graphics.Color;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.IdRes; 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 { public class PersistentErrorView {
/** private ViewGroup mViewGroup;
* Error message type.
*/
public enum Type {
ERROR,
WARNING,
}
private TextView mTextView;
/** /**
* @param context The Activity where this View is shon. * @param context The Activity where this View is shon.
* @param type View type. * @param type View type.
*/ */
public PersistentErrorView(Activity context, @IdRes int errorViewId, Type type) { public PersistentErrorView(Activity context, @IdRes int errorViewId) {
mTextView = (TextView) context.findViewById(errorViewId); mViewGroup = (ViewGroup) 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;
}
} }
/** /**
...@@ -52,7 +34,7 @@ public class PersistentErrorView { ...@@ -52,7 +34,7 @@ public class PersistentErrorView {
* @return object reference for chaining. * @return object reference for chaining.
*/ */
public PersistentErrorView setOnClickListener(OnClickListener listener) { public PersistentErrorView setOnClickListener(OnClickListener listener) {
mTextView.setOnClickListener(listener); mViewGroup.setOnClickListener(listener);
return this; return this;
} }
...@@ -62,7 +44,30 @@ public class PersistentErrorView { ...@@ -62,7 +44,30 @@ public class PersistentErrorView {
* @return object reference for chaining. * @return object reference for chaining.
*/ */
public PersistentErrorView setDialog(Dialog dialog) { 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; return this;
} }
...@@ -72,7 +77,8 @@ public class PersistentErrorView { ...@@ -72,7 +77,8 @@ public class PersistentErrorView {
* @return object reference for chaining. * @return object reference for chaining.
*/ */
public PersistentErrorView setText(String text) { public PersistentErrorView setText(String text) {
mTextView.setText(text); TextView textView = (TextView) mViewGroup.findViewById(R.id.error_text);
textView.setText(text);
return this; return this;
} }
...@@ -80,13 +86,13 @@ public class PersistentErrorView { ...@@ -80,13 +86,13 @@ public class PersistentErrorView {
* Show the view by setting its visibility. * Show the view by setting its visibility.
*/ */
public void show() { public void show() {
mTextView.setVisibility(View.VISIBLE); mViewGroup.setVisibility(View.VISIBLE);
} }
/** /**
* Hide the view by setting its visibility. * Hide the view by setting its visibility.
*/ */
public void hide() { public void hide() {
mTextView.setVisibility(View.GONE); mViewGroup.setVisibility(View.GONE);
} }
} }
...@@ -22,14 +22,22 @@ import java.util.Locale; ...@@ -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 * 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 * 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 { public class WebViewPackageError {
private static final String TAG = "WebViewDevTools"; private static final String TAG = "WebViewDevTools";
private PersistentErrorView mErrorMessage; private PersistentErrorView mErrorMessage;
private Activity mContext; 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. * @param context The {@link Activity} where the error is yield.
...@@ -37,12 +45,7 @@ public class WebViewPackageError { ...@@ -37,12 +45,7 @@ public class WebViewPackageError {
*/ */
public WebViewPackageError(Activity context) { public WebViewPackageError(Activity context) {
mContext = context; mContext = context;
mErrorMessage = new PersistentErrorView(context, R.id.webview_package_error);
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);
} }
/** /**
...@@ -50,85 +53,65 @@ public class WebViewPackageError { ...@@ -50,85 +53,65 @@ public class WebViewPackageError {
* different WebView implementation. Hide it otherwise. * different WebView implementation. Hide it otherwise.
*/ */
public void showMessageIfDifferent() { public void showMessageIfDifferent() {
if (isWebViewPackageDifferent()) { if (WebViewPackageHelper.isCurrentSystemWebViewImplementation(mContext)) {
mErrorMessage.show();
} else {
mErrorMessage.hide(); mErrorMessage.hide();
} else {
buildErrorMessage();
mErrorMessage.show();
} }
} }
/** private void buildErrorMessage() {
* Show an {@link AlertDialog} about the different WebView package error with an action to if (!WebViewPackageHelper.hasValidWebViewImplementation(mContext)) {
* launch system settings to show and change WebView implementation. mErrorMessage.setText(NO_VALID_WEBVIEW_MESSAGE);
*/ // Unset action button and dialog. In case if the device got into the state where there
public void showDialogIfDifferent() { // is no valid WebView implementation while the UI is running and the button and dialog
if (isWebViewPackageDifferent()) { // were set for other actions.
mErrorDialog.show(); mErrorMessage.setActionButton(null, null);
mErrorMessage.setDialog(null);
return;
} }
}
private boolean isWebViewPackageDifferent() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
PackageInfo systemWebViewPackage = WebViewPackageHelper.getCurrentWebViewPackage(mContext); CharSequence label = WebViewPackageHelper.loadLabel(mContext);
if (systemWebViewPackage == null) { mErrorMessage.setText(String.format(
Log.e(TAG, "Could not find a valid WebView implementation"); Locale.US, "%s is not the system's current selected WebView provider", label));
buildNoValidWebViewPackageDialog().show(); dialogBuilder.setTitle("Different WebView Provider");
return true; 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 (canOpenCurrentProvider) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { dialogBuilder.setPositiveButton(OPEN_WEBVIEW_PROVIDER_BUTTON_TEXT,
builder.setNeutralButton("Change WebView provider", (d, id) -> openCurrentWebViewProviderDevTools());
(dialog, id) }
-> mContext.startActivity( if (canChangeProvider) {
new Intent(Settings.ACTION_WEBVIEW_SETTINGS))); dialogBuilder.setNeutralButton(CHANGE_WEBVIEW_PROVIDER_BUTTON_TEXT,
(d, id) -> openChangeWebViewProviderSettings());
} }
return builder.create(); mErrorMessage.setDialog(dialogBuilder.create());
} }
private Dialog buildNoValidWebViewPackageDialog() { private Dialog buildNoValidWebViewPackageDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext); AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("No Valid WebView") builder.setTitle("No Valid WebView").setMessage(NO_VALID_WEBVIEW_MESSAGE);
.setMessage("Cannot find a valid WebView provider installed. "
+ "Please install a valid WebView package. Contact "
+ "android-webview-dev@chromium.org for help.");
return builder.create(); return builder.create();
} }
private Dialog buildNoDevToolsDialog(String sytemWebViewPackageName) { private Dialog buildNoDevToolsDialog() {
PackageInfo systemWebViewPackage = WebViewPackageHelper.getCurrentWebViewPackage(mContext);
AlertDialog.Builder builder = new AlertDialog.Builder(mContext); AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("DevTools Not Found") builder.setTitle("DevTools Not Found")
.setMessage(String.format(Locale.US, .setMessage(String.format(Locale.US,
...@@ -136,16 +119,66 @@ public class WebViewPackageError { ...@@ -136,16 +119,66 @@ public class WebViewPackageError {
+ "WebView provider selected by the system (%s).\n\n" + "WebView provider selected by the system (%s).\n\n"
+ "Please update to a newer version or select a different WebView " + "Please update to a newer version or select a different WebView "
+ "provider.", + "provider.",
sytemWebViewPackageName)); systemWebViewPackage.packageName));
// Switching WebView providers is possible from API >= 24. if (canChangeWebViewProvider()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { builder.setPositiveButton(CHANGE_WEBVIEW_PROVIDER_BUTTON_TEXT,
builder.setPositiveButton("Change WebView provider", (dialog, id) -> openChangeWebViewProviderSettings());
(dialog, id)
-> mContext.startActivity(
new Intent(Settings.ACTION_WEBVIEW_SETTINGS)));
} }
return builder.create(); 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 { ...@@ -134,6 +134,13 @@ public final class WebViewPackageHelper {
return context.getPackageName().equals(systemWebViewPackage.packageName); 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 * 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 * 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