package org.wikipedia.views; 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.Space; import android.widget.TextView; import org.mediawiki.api.json.ApiException; import org.wikipedia.R; import org.wikipedia.page.PageActivity; import butterknife.BindView; import butterknife.ButterKnife; import static org.wikipedia.util.ThrowableUtil.is404; import static org.wikipedia.util.ThrowableUtil.isOffline; public class WikiErrorView extends LinearLayout { @BindView(R.id.view_wiki_error_icon) ImageView icon; @BindView(R.id.view_wiki_error_text) TextView errorText; @BindView(R.id.view_wiki_error_button) TextView button; @BindView(R.id.view_wiki_error_footer_text) TextView footerText; @BindView(R.id.view_wiki_error_article_content_top_offset) Space contentTopOffset; @BindView(R.id.view_wiki_error_article_tab_layout_offset) Space tabLayoutOffset; @Nullable private OnClickListener retryListener; @Nullable private OnClickListener backListener; public WikiErrorView(Context context) { this(context, null); } public WikiErrorView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public WikiErrorView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); inflate(context, R.layout.view_wiki_error, this); ButterKnife.bind(this); } @Nullable public OnClickListener getRetryListener() { return retryListener; } public void setRetryClickListener(@Nullable OnClickListener listener) { retryListener = listener; } @Nullable public OnClickListener getBackListener() { return backListener; } public void setBackClickListener(@Nullable OnClickListener listener) { backListener = listener; } public void setError(@Nullable Throwable caught) { Resources resources = getContext().getResources(); ErrorType errorType = getErrorType(getContext(), caught); icon.setImageDrawable(ContextCompat.getDrawable(getContext(), errorType.icon())); errorText.setText(resources.getString(errorType.text())); button.setText(resources.getString(errorType.buttonText())); button.setOnClickListener(errorType.buttonClickListener(this)); if (errorType.hasFooterText()) { footerText.setText(resources.getString(errorType.footerText())); } } private ErrorType getErrorType(@NonNull Context context, @Nullable Throwable caught) { // apps-android-java-mwapi wraps all exceptions in an ApiException. Strip it so we get // useful information about the underlying cause. // TODO: update when the apps-android-java-mwapi dependency is dropped (T141127) if (caught instanceof ApiException) { caught = caught.getCause(); } if (caught != null && is404(caught)) { return ErrorType.PAGE_MISSING; } if (caught != null && isOffline(caught)) { if (context instanceof PageActivity) { return ErrorType.PAGE_OFFLINE; } return ErrorType.OFFLINE; } return ErrorType.GENERIC; } private enum ErrorType { PAGE_MISSING(R.drawable.ic_error_black_24dp, R.string.error_page_does_not_exist, R.string.page_error_back_to_main) { @Nullable @Override OnClickListener buttonClickListener(@NonNull WikiErrorView errorView) { return errorView.getBackListener(); } }, PAGE_OFFLINE(R.drawable.ic_no_article, R.string.page_offline_notice_cannot_load_while_offline, R.string.page_error_retry, R.string.page_offline_notice_add_to_reading_list) { @Nullable @Override OnClickListener buttonClickListener(@NonNull WikiErrorView errorView) { return errorView.getRetryListener(); } }, OFFLINE(R.drawable.ic_portable_wifi_off_black_24px, R.string.view_wiki_error_message_offline, R.string.page_error_retry) { @Nullable @Override OnClickListener buttonClickListener(@NonNull WikiErrorView errorView) { return errorView.getRetryListener(); } }, GENERIC(R.drawable.ic_error_black_24dp, R.string.error_message_generic, R.string.page_error_back_to_main) { @Nullable @Override OnClickListener buttonClickListener(@NonNull WikiErrorView errorView) { return errorView.getBackListener(); } }; @DrawableRes private int icon; @StringRes private int text; @StringRes private int buttonText; @StringRes private int footerText; @DrawableRes int icon() { return icon; } @StringRes int text() { return text; } @StringRes int buttonText() { return buttonText; } @StringRes int footerText() { return footerText; } boolean hasFooterText() { return footerText != 0; } @Nullable abstract OnClickListener buttonClickListener(@NonNull WikiErrorView errorView); ErrorType(@DrawableRes int icon, @StringRes int text, @StringRes int buttonText) { this.icon = icon; this.text = text; this.buttonText = buttonText; } ErrorType(@DrawableRes int icon, @StringRes int text, @StringRes int buttonText, @StringRes int footerText) { this.icon = icon; this.text = text; this.buttonText = buttonText; this.footerText = footerText; } } }