blob: 6694ec722fbf07452ef968eed3caf4d5c1ac6a91 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tab;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Layout;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.method.LinkMovementMethod;
import android.text.style.BulletSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.components.ui_metrics.SadTabEvent;
import org.chromium.ui.text.NoUnderlineClickableSpan;
import org.chromium.ui.text.SpanApplier;
import org.chromium.ui.text.SpanApplier.SpanInfo;
/**
* A factory class for creating the "Sad Tab" view, which is shown in place of a crashed renderer.
*/
public class SadTabViewFactory {
/**
* @param context Context of the resulting Sad Tab view.
* @param suggestionAction {@link Runnable} to be executed when user clicks "try these
* suggestions".
* @param buttonAction {@link Runnable} to be executed when the button is pressed.
* (e.g., refreshing the page or sending feedback)
* @param showSendFeedbackView Whether to show the "send feedback" version of the Sad Tab view.
* @param isIncognito Whether the Sad Tab view is being showin in an incognito tab.
* @return A "Sad Tab" view instance which is used in place of a crashed renderer.
*/
public static View createSadTabView(Context context, final Runnable suggestionAction,
final Runnable buttonAction, final boolean showSendFeedbackView, boolean isIncognito) {
// Inflate Sad tab and initialize.
LayoutInflater inflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View sadTabView = inflater.inflate(R.layout.sad_tab, null);
TextView titleText = (TextView) sadTabView.findViewById(R.id.sad_tab_title);
int titleTextId =
showSendFeedbackView ? R.string.sad_tab_reload_title : R.string.sad_tab_title;
titleText.setText(titleTextId);
if (showSendFeedbackView) intializeSuggestionsViews(context, sadTabView, isIncognito);
TextView messageText = (TextView) sadTabView.findViewById(R.id.sad_tab_message);
messageText.setText(getHelpMessage(context, suggestionAction, showSendFeedbackView));
messageText.setMovementMethod(LinkMovementMethod.getInstance());
Button button = (Button) sadTabView.findViewById(R.id.sad_tab_button);
int buttonTextId = showSendFeedbackView ? R.string.sad_tab_send_feedback_label
: R.string.sad_tab_reload_label;
button.setText(buttonTextId);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
SadTabViewFactory.recordEvent(showSendFeedbackView, SadTabEvent.BUTTON_CLICKED);
buttonAction.run();
}
});
SadTabViewFactory.recordEvent(showSendFeedbackView, SadTabEvent.DISPLAYED);
return sadTabView;
}
/**
* Construct and return help message to be displayed on R.id.sad_tab_message.
* @param context Context of the resulting Sad Tab view. This is needed to load the strings.
* @param suggestionAction Action to be executed when user clicks "try these suggestions"
* or "learn more".
* @return Help message to be displayed on R.id.sad_tab_message.
*/
private static CharSequence getHelpMessage(
Context context, final Runnable suggestionAction, final boolean showSendFeedback) {
NoUnderlineClickableSpan linkSpan = new NoUnderlineClickableSpan((view) -> {
SadTabViewFactory.recordEvent(showSendFeedback, SadTabEvent.HELP_LINK_CLICKED);
suggestionAction.run();
});
if (showSendFeedback) {
SpannableString learnMoreLink =
new SpannableString(context.getString(R.string.sad_tab_reload_learn_more));
learnMoreLink.setSpan(linkSpan, 0, learnMoreLink.length(), 0);
return learnMoreLink;
} else {
String helpMessage = context.getString(R.string.sad_tab_message) + "\n\n"
+ context.getString(R.string.sad_tab_suggestions);
return SpanApplier.applySpans(helpMessage, new SpanInfo("<link>", "</link>", linkSpan));
}
}
/**
* Initializes the TextViews that display tips for handling repeated crashes.
* @param context Context of the resulting Sad Tab view.
* @param sadTabView The parent Sad Tab view that contains the TextViews.
* @param isIncognito Whether the Sad Tab view is being showing in an incognito tab.
*/
private static void intializeSuggestionsViews(
Context context, View sadTabView, boolean isIncognito) {
TextView suggestionsTitle =
(TextView) sadTabView.findViewById(R.id.sad_tab_suggestions_title);
suggestionsTitle.setVisibility(View.VISIBLE);
suggestionsTitle.setText(R.string.sad_tab_reload_try);
TextView suggestions = (TextView) sadTabView.findViewById(R.id.sad_tab_suggestions);
suggestions.setVisibility(View.VISIBLE);
SpannableStringBuilder spannableString = new SpannableStringBuilder();
if (!isIncognito) {
spannableString
.append(generateBulletedString(context,
ChromeFeatureList.isEnabled(ChromeFeatureList.INCOGNITO_STRINGS)
? R.string.sad_tab_reload_private
: R.string.sad_tab_reload_incognito))
.append("\n");
}
spannableString
.append(generateBulletedString(context, R.string.sad_tab_reload_restart_browser))
.append("\n")
.append(generateBulletedString(context, R.string.sad_tab_reload_restart_device))
.append("\n");
suggestions.setText(spannableString);
}
/**
* Generates a bulleted {@link SpannableString}.
* @param context The {@link Context} used to retrieve the String.
* @param stringResId The resource id of the String to bullet.
* @return A {@link SpannableString} with a bullet in front of the provided String.
*/
private static SpannableString generateBulletedString(Context context, int stringResId) {
SpannableString bullet = new SpannableString(context.getString(stringResId));
bullet.setSpan(new SadTabBulletSpan(context), 0, bullet.length(), 0);
return bullet;
}
/**
* Records enumerated histograms for {@link SadTabEvent}.
* @param sendFeedbackView Whether the event is for the "send feedback" version of the Sad Tab.
* @param event The {@link SadTabEvent} to record.
*/
private static void recordEvent(boolean sendFeedbackView, int event) {
if (sendFeedbackView) {
RecordHistogram.recordEnumeratedHistogram(
"Tabs.SadTab.Feedback.Event", event, SadTabEvent.MAX_SAD_TAB_EVENT);
} else {
RecordHistogram.recordEnumeratedHistogram(
"Tabs.SadTab.Reload.Event", event, SadTabEvent.MAX_SAD_TAB_EVENT);
}
}
private static class SadTabBulletSpan extends BulletSpan {
private int mXOffset;
public SadTabBulletSpan(Context context) {
super(context.getResources().getDimensionPixelSize(R.dimen.sad_tab_bullet_gap));
mXOffset = context.getResources().getDimensionPixelSize(
R.dimen.sad_tab_bullet_leading_offset);
}
@Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline,
int bottom, CharSequence text, int start, int end, boolean first, Layout l) {
// Android cuts off the bullet points. Adjust the x-position so that the bullets aren't
// cut off.
super.drawLeadingMargin(
c, p, x + mXOffset, dir, top, baseline, bottom, text, start, end, first, l);
}
}
}