Mholloway has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/344687 )
Change subject: Show an accurate message in the link preview when a page
doesn't exist
......................................................................
Show an accurate message in the link preview when a page doesn't exist
Updates the link preview to show a "page does not exist" message when
appropriate.
Bug: T161008
Change-Id: I3b95431abb9305b191250ff527272d1869208c54
---
M app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewDialog.java
A app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewErrorView.java
M app/src/main/java/org/wikipedia/util/ThrowableUtil.java
A app/src/main/res/drawable/ic_portable_wifi_off_black_24px.xml
M app/src/main/res/layout/dialog_link_preview.xml
A app/src/main/res/layout/view_link_preview_error.xml
M app/src/main/res/values-qq/strings.xml
M app/src/main/res/values/strings.xml
8 files changed, 251 insertions(+), 54 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia
refs/changes/87/344687/1
diff --git
a/app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewDialog.java
b/app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewDialog.java
index 09154c2..c230ca3 100755
--- a/app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewDialog.java
+++ b/app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewDialog.java
@@ -40,17 +40,20 @@
import org.wikipedia.util.log.L;
import org.wikipedia.views.ViewUtil;
+import java.net.UnknownHostException;
+
import retrofit2.Call;
import retrofit2.Response;
import static org.wikipedia.util.L10nUtil.getStringForArticleLanguage;
import static org.wikipedia.util.L10nUtil.setConditionalLayoutDirection;
+import static org.wikipedia.util.ThrowableUtil.is404;
+import static org.wikipedia.util.ThrowableUtil.isOffline;
public class LinkPreviewDialog extends ExtendedBottomSheetDialogFragment
- implements DialogInterface.OnDismissListener {
+ implements LinkPreviewErrorView.Callback,
DialogInterface.OnDismissListener {
public interface Callback {
- void onLinkPreviewLoadPage(@NonNull PageTitle title, @NonNull
HistoryEntry entry,
- boolean inNewTab);
+ void onLinkPreviewLoadPage(@NonNull PageTitle title, @NonNull
HistoryEntry entry, boolean inNewTab);
void onLinkPreviewCopyLink(@NonNull PageTitle title);
void onLinkPreviewAddToList(@NonNull PageTitle title);
void onLinkPreviewShareLink(@NonNull PageTitle title);
@@ -60,7 +63,7 @@
private LinearLayout dialogContainer;
private View contentContainer;
- private View offlineContainer;
+ private LinkPreviewErrorView errorContainer;
private ProgressBar progressBar;
private TextView extractText;
private SimpleDraweeView thumbnailView;
@@ -88,8 +91,7 @@
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
WikipediaApp app = WikipediaApp.getInstance();
pageTitle = getArguments().getParcelable("title");
entrySource = getArguments().getInt("entrySource");
@@ -98,7 +100,7 @@
View rootView = inflater.inflate(R.layout.dialog_link_preview,
container);
dialogContainer = (LinearLayout)
rootView.findViewById(R.id.dialog_link_preview_container);
contentContainer =
rootView.findViewById(R.id.dialog_link_preview_content_container);
- offlineContainer =
rootView.findViewById(R.id.dialog_link_preview_offline_container);
+ errorContainer = (LinkPreviewErrorView)
rootView.findViewById(R.id.dialog_link_preview_error_container);
progressBar = (ProgressBar)
rootView.findViewById(R.id.link_preview_progress);
toolbarView = rootView.findViewById(R.id.link_preview_toolbar);
toolbarView.setOnClickListener(goToPageListener);
@@ -198,6 +200,28 @@
}
}
+ @Override
+ public void setOverlayButtonText(@NonNull String text) {
+ overlayView.setPrimaryButtonText(text);
+ }
+
+ @Override
+ public void setOverlayButtonCallback(@NonNull
LinkPreviewOverlayView.Callback callback) {
+ overlayView.setCallback(callback);
+ }
+
+ @Override
+ public void onAddToList() {
+ if (callback() != null) {
+ callback().onLinkPreviewAddToList(pageTitle);
+ }
+ }
+
+ @Override
+ public void onDismiss() {
+ dismiss();
+ }
+
private void loadContent() {
PageClientFactory
.create(pageTitle.getWikiSite(), pageTitle.namespace())
@@ -205,7 +229,7 @@
.enqueue(linkPreviewNetworkOnLoadCallback);
}
- private void tryLoadingFromCache() {
+ private void tryLoadingFromCache(@Nullable final UnknownHostException
offlineException) {
L.v("Loading link preview from Saved Pages");
new LoadSavedPageTask(pageTitle) {
@Override public void onFinish(Page page) {
@@ -219,7 +243,11 @@
if (!isAdded()) {
return;
}
- showPreviewOfflineNotice();
+ if (offlineException != null) {
+ showError(offlineException);
+ } else {
+ showError(caught);
+ }
L.e("Page summary cache request failed", caught);
}
}.execute();
@@ -230,14 +258,13 @@
setPreviewContents(contents);
}
- private void showPreviewOfflineNotice() {
+ private void showError(@Nullable Throwable caught) {
dialogContainer.setLayoutTransition(null);
progressBar.setVisibility(View.GONE);
contentContainer.setVisibility(View.GONE);
- offlineContainer.setVisibility(View.VISIBLE);
-
overlayView.setPrimaryButtonText(getResources().getString(R.string.button_add_to_reading_list));
overlayView.showSecondaryButton(false);
- overlayView.setCallback(new OverlayViewOfflineCallback());
+ errorContainer.setVisibility(View.VISIBLE);
+ errorContainer.setError(this, caught);
}
private void setPreviewContents(@NonNull LinkPreviewContents contents) {
@@ -258,15 +285,22 @@
if (summary != null && !summary.hasError()) {
showPreview(new LinkPreviewContents(summary,
pageTitle.getWikiSite()));
} else {
- tryLoadingFromCache();
+ tryLoadingFromCache(null);
logError(summary.hasError() ? summary.getError() : null,
"Page summary network request failed");
}
}
- @Override public void onFailure(Call<PageSummary> call, Throwable t) {
- tryLoadingFromCache();
- L.e(t);
+ @Override public void onFailure(Call<PageSummary> call, Throwable
caught) {
+ L.e(caught);
+ if (is404(getContext(), caught)) {
+ // If the page doesn't exist, show an error immediately
without checking the cache.
+ showError(caught);
+ } else {
+ // If we're offline, check the cache but pass along the
exception so that we show
+ // the correct error message if the page isn't found in the
cache.
+ tryLoadingFromCache(isOffline(caught) ? (UnknownHostException)
caught : null);
+ }
}
};
@@ -369,19 +403,6 @@
@Override
public void onSecondaryClick() {
goToExternalMapsApp();
- }
- }
-
- private class OverlayViewOfflineCallback implements
LinkPreviewOverlayView.Callback {
- @Override
- public void onPrimaryClick() {
- if (callback() != null) {
- callback().onLinkPreviewAddToList(pageTitle);
- }
- }
-
- @Override
- public void onSecondaryClick() {
}
}
diff --git
a/app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewErrorView.java
b/app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewErrorView.java
new file mode 100644
index 0000000..4d1e02d
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/page/linkpreview/LinkPreviewErrorView.java
@@ -0,0 +1,151 @@
+package org.wikipedia.page.linkpreview;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.v4.content.ContextCompat;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.wikipedia.R;
+import org.wikipedia.util.ThrowableUtil;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class LinkPreviewErrorView extends LinearLayout {
+ @BindView(R.id.view_link_preview_error_icon) ImageView icon;
+ @BindView(R.id.view_link_preview_error_text) TextView textView;
+
+ interface Callback {
+ @NonNull Context getContext();
+ void setOverlayButtonText(@NonNull String text);
+ void setOverlayButtonCallback(@NonNull LinkPreviewOverlayView.Callback
buttonCallback);
+ void onAddToList();
+ void onDismiss();
+ }
+
+ @Nullable private Callback callback;
+
+ public LinkPreviewErrorView(Context context) {
+ this(context, null);
+ }
+
+ public LinkPreviewErrorView(Context context, @Nullable AttributeSet attrs)
{
+ this(context, attrs, 0);
+ }
+
+ public LinkPreviewErrorView(Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ @TargetApi(21)
+ public LinkPreviewErrorView(Context context, AttributeSet attrs, int
defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ inflate(context, R.layout.view_link_preview_error, this);
+ ButterKnife.bind(this);
+ }
+
+ public void setError(@NonNull Callback callback, @Nullable Throwable
caught) {
+ this.callback = callback;
+ Context context = callback.getContext();
+ Resources resources = context.getResources();
+ ErrorType errorType = getErrorType(context, caught);
+ icon.setImageDrawable(ContextCompat.getDrawable(context,
errorType.icon()));
+ textView.setText(resources.getString(errorType.text()));
+
callback.setOverlayButtonText(resources.getString(errorType.buttonText()));
+
callback.setOverlayButtonCallback(getCallbackForAction(errorType.buttonAction()));
+ }
+
+ @NonNull private LinkPreviewOverlayView.Callback
getCallbackForAction(ButtonAction action) {
+ if (action == ButtonAction.ADD_TO_LIST) {
+ return new ButtonAddToListCallback();
+ }
+ if (action == ButtonAction.DISMISS) {
+ return new ButtonDismissCallback();
+ }
+ throw new IllegalArgumentException();
+ }
+
+ @NonNull private ErrorType getErrorType(@NonNull Context context,
@Nullable Throwable caught) {
+ if (caught != null && ThrowableUtil.is404(context, caught)) {
+ return ErrorType.PAGE_MISSING;
+ }
+ if (caught != null && ThrowableUtil.isOffline(caught)) {
+ return ErrorType.OFFLINE;
+ }
+ return ErrorType.GENERIC;
+ }
+
+ private enum ErrorType {
+ OFFLINE(R.drawable.ic_portable_wifi_off_black_24px,
R.string.dialog_link_preview_offline_notice,
+ R.string.button_add_to_reading_list, ButtonAction.ADD_TO_LIST)
{
+ },
+
+ PAGE_MISSING(R.drawable.ic_error_black_24dp,
R.string.error_page_does_not_exist,
+ R.string.view_link_preview_error_button_dismiss,
ButtonAction.DISMISS),
+
+ GENERIC(R.drawable.ic_error_black_24dp, R.string.error_message_generic,
+ R.string.view_link_preview_error_button_dismiss,
ButtonAction.DISMISS);
+
+ @DrawableRes private int icon;
+ @StringRes private int text;
+ @StringRes private int buttonText;
+ private ButtonAction buttonAction;
+
+ ErrorType(@DrawableRes int icon, @StringRes int text, @StringRes int
buttonText, ButtonAction buttonAction) {
+ this.icon = icon;
+ this.text = text;
+ this.buttonText = buttonText;
+ this.buttonAction = buttonAction;
+ }
+
+ @DrawableRes int icon() {
+ return icon;
+ }
+
+ @StringRes int text() {
+ return text;
+ }
+
+ @StringRes int buttonText() {
+ return buttonText;
+ }
+
+ ButtonAction buttonAction() {
+ return buttonAction;
+ }
+ }
+
+ private class ButtonAddToListCallback implements
LinkPreviewOverlayView.Callback {
+ @Override public void onPrimaryClick() {
+ if (callback != null) {
+ callback.onAddToList();
+ }
+ }
+
+ @Override public void onSecondaryClick() {
+ }
+ }
+
+ private class ButtonDismissCallback implements
LinkPreviewOverlayView.Callback {
+ @Override public void onPrimaryClick() {
+ if (callback != null) {
+ callback.onDismiss();
+ }
+ }
+
+ @Override public void onSecondaryClick() {
+ }
+ }
+
+ private enum ButtonAction {
+ ADD_TO_LIST, DISMISS
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/util/ThrowableUtil.java
b/app/src/main/java/org/wikipedia/util/ThrowableUtil.java
index e8881f7..447bc30 100644
--- a/app/src/main/java/org/wikipedia/util/ThrowableUtil.java
+++ b/app/src/main/java/org/wikipedia/util/ThrowableUtil.java
@@ -11,6 +11,7 @@
import org.wikipedia.R;
import org.wikipedia.createaccount.CreateAccountException;
import org.wikipedia.dataclient.mwapi.MwException;
+import org.wikipedia.dataclient.okhttp.HttpStatusException;
import org.wikipedia.login.LoginClient;
import java.net.UnknownHostException;
@@ -53,8 +54,10 @@
getApiErrorMessage(context, (ApiException)
inner));
} else if (isNetworkError(e)) {
result = new
AppError(context.getString(R.string.error_network_error),
-
context.getString(R.string.format_error_server_message,
- inner.getLocalizedMessage()));
+ context.getString(R.string.format_error_server_message,
+ inner.getLocalizedMessage()));
+ } else if (e instanceof HttpStatusException) {
+ result = new AppError(e.getMessage(),
Integer.toString(((HttpStatusException) e).code()));
} else if (inner instanceof LoginClient.LoginFailedException
|| inner instanceof CreateAccountException
|| inner instanceof MwException) {
@@ -78,6 +81,10 @@
return e.getDetail() != null && e.getDetail().contains("404");
}
+ public static boolean isOffline(@NonNull Throwable caught) {
+ return caught instanceof UnknownHostException;
+ }
+
private static boolean isNetworkError(@NonNull Throwable e) {
return ThrowableUtil.throwableContainsException(e,
HttpRequest.HttpRequestException.class)
|| ThrowableUtil.throwableContainsException(e,
UnknownHostException.class)
diff --git a/app/src/main/res/drawable/ic_portable_wifi_off_black_24px.xml
b/app/src/main/res/drawable/ic_portable_wifi_off_black_24px.xml
new file mode 100644
index 0000000..98b6999
--- /dev/null
+++ b/app/src/main/res/drawable/ic_portable_wifi_off_black_24px.xml
@@ -0,0 +1,5 @@
+<vector android:autoMirrored="true" android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#000000"
android:pathData="M17.56,14.24c0.28,-0.69 0.44,-1.45 0.44,-2.24 0,-3.31
-2.69,-6 -6,-6 -0.79,0 -1.55,0.16 -2.24,0.44l1.62,1.62c0.2,-0.03 0.41,-0.06
0.62,-0.06 2.21,0 4,1.79 4,4 0,0.21 -0.02,0.42
-0.05,0.63l1.61,1.61zM12,4c4.42,0 8,3.58 8,8 0,1.35 -0.35,2.62
-0.95,3.74l1.47,1.47C21.46,15.69 22,13.91 22,12c0,-5.52 -4.48,-10 -10,-10
-1.91,0 -3.69,0.55 -5.21,1.47l1.46,1.46C9.37,4.34 10.65,4
12,4zM3.27,2.5L2,3.77l2.1,2.1C2.79,7.57 2,9.69 2,12c0,3.7 2.01,6.92
4.99,8.65l1,-1.73C5.61,17.53 4,14.96 4,12c0,-1.76 0.57,-3.38
1.53,-4.69l1.43,1.44C6.36,9.68 6,10.8 6,12c0,2.22 1.21,4.15
3,5.19l1,-1.74c-1.19,-0.7 -2,-1.97 -2,-3.45 0,-0.65 0.17,-1.25
0.44,-1.79l1.58,1.58L10,12c0,1.1 0.9,2 2,2l0.21,-0.02 0.01,0.01
7.51,7.51L21,20.23 4.27,3.5l-1,-1z"/>
+</vector>
diff --git a/app/src/main/res/layout/dialog_link_preview.xml
b/app/src/main/res/layout/dialog_link_preview.xml
index 17863f4..9c6cc46 100755
--- a/app/src/main/res/layout/dialog_link_preview.xml
+++ b/app/src/main/res/layout/dialog_link_preview.xml
@@ -94,31 +94,13 @@
android:layout_gravity="center_horizontal" />
</LinearLayout>
- <LinearLayout
- android:id="@+id/dialog_link_preview_offline_container"
+ <org.wikipedia.page.linkpreview.LinkPreviewErrorView
+ android:id="@+id/dialog_link_preview_error_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
android:visibility="gone">
- <ImageView
- android:layout_width="72dp"
- android:layout_height="72dp"
- android:layout_margin="24dp"
- android:layout_gravity="center_horizontal"
- app:srcCompat="@drawable/ic_no_article"
- android:tint="?attr/link_preview_offline_text_color"
- android:contentDescription="@null"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="42dp"
- android:layout_marginRight="42dp"
- android:textSize="18sp"
- android:textColor="?attr/link_preview_offline_text_color"
- android:textAlignment="center"
- android:lineSpacingMultiplier="1.2"
- android:text="@string/dialog_link_preview_offline_notice"/>
- </LinearLayout>
+
+ </org.wikipedia.page.linkpreview.LinkPreviewErrorView>
<!-- Bottom padding for overlay button -->
<View
diff --git a/app/src/main/res/layout/view_link_preview_error.xml
b/app/src/main/res/layout/view_link_preview_error.xml
new file mode 100644
index 0000000..3fe5310
--- /dev/null
+++ b/app/src/main/res/layout/view_link_preview_error.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/view_link_preview_error_icon"
+ android:layout_width="72dp"
+ android:layout_height="72dp"
+ android:layout_margin="24dp"
+ android:layout_gravity="center_horizontal"
+ app:srcCompat="@drawable/ic_error_black_24dp"
+ android:tint="?attr/link_preview_offline_text_color"
+ android:contentDescription="@null"/>
+ <TextView
+ android:id="@+id/view_link_preview_error_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="42dp"
+ android:layout_marginRight="42dp"
+ android:textSize="18sp"
+ android:textColor="?attr/link_preview_offline_text_color"
+ android:textAlignment="center"
+ android:lineSpacingMultiplier="1.2"
+ android:text="@string/error_message_generic"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/values-qq/strings.xml
b/app/src/main/res/values-qq/strings.xml
index db76529..da9ffe8 100644
--- a/app/src/main/res/values-qq/strings.xml
+++ b/app/src/main/res/values-qq/strings.xml
@@ -369,6 +369,7 @@
<string name="location_permissions_enable_action">Button label for
requesting to turn on location permissions.</string>
<string name="error_webview_updating">Error informing the user that the
System WebView is being updated, meaning that the app cannot be launched right
now, and that the user should try again in a moment.</string>
<string name="multi_select_items_selected">Toolbar title shown when the user
is selecting multiple items in a list. The \"%d\" symbol is replaced with the
number of items currently selected.</string>
+ <string name="error_message_generic">Generic message informing the user that
an error occurred</string>
<string name="crash_report_dialog_title">Title of the dialog that pops up
when the app crashes, asking user to send a report</string>
<string name="crash_report_dialog_text">Text asking the user to send us a
crash report</string>
<string name="crash_report_dialog_dont_send_button">In the dialog that pops
up when the app crashes, text for button that doesn\'t send a crash report.
diff --git a/app/src/main/res/values/strings.xml
b/app/src/main/res/values/strings.xml
index 7dba702..0a1386f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -255,6 +255,9 @@
<string name="location_permissions_enable_action">Turn on</string>
<string name="error_webview_updating">The Android System WebView is
currently being updated. Please try again in a moment.</string>
<string name="multi_select_items_selected">%d selected</string>
+ <string name="error_message_generic">An error occurred</string>
+ <string name="view_link_preview_error_button_dismiss">Dismiss</string>
+ <string name="error_page_does_not_exist">This page does not exist</string>
<!-- Crash reporter -->
<string name="crash_report_dialog_title">Sorry, app crashed last
time</string>
--
To view, visit https://gerrit.wikimedia.org/r/344687
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I3b95431abb9305b191250ff527272d1869208c54
Gerrit-PatchSet: 1
Gerrit-Project: apps/android/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Mholloway <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits