From 75e404bdde585484b85ce6c02617a6ff7369e007 Mon Sep 17 00:00:00 2001 From: Siqi Liu Date: Mon, 1 Aug 2016 08:08:46 -0700 Subject: [PATCH] Add Report Button for Android Redbox, enabled in Ads Manager, Groups and FB4A Reviewed By: foghina Differential Revision: D3542780 fbshipit-source-id: a0dfb8b8af7a5ae0ca696e84ec4b8795a293c26f --- .../devsupport/DevSupportManagerImpl.java | 6 +- .../react/devsupport/RedBoxDialog.java | 106 ++++++++++++++++-- .../react/devsupport/RedBoxHandler.java | 30 ++++- .../res/devsupport/layout/redbox_view.xml | 53 +++++++++ .../main/res/devsupport/values/strings.xml | 5 +- 5 files changed, 186 insertions(+), 14 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java index 8bb5423e9c471a..dfb826fb9af51d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java @@ -252,6 +252,7 @@ public void run() { // JS errors are reported here after source mapping. if (mRedBoxHandler != null) { mRedBoxHandler.handleRedbox(message, stack, RedBoxHandler.ErrorType.JS); + mRedBoxDialog.resetReporting(true); } mRedBoxDialog.show(); } @@ -276,7 +277,7 @@ private void showNewError( @Override public void run() { if (mRedBoxDialog == null) { - mRedBoxDialog = new RedBoxDialog(mApplicationContext, DevSupportManagerImpl.this); + mRedBoxDialog = new RedBoxDialog(mApplicationContext, DevSupportManagerImpl.this, mRedBoxHandler); mRedBoxDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); } if (mRedBoxDialog.isShowing()) { @@ -290,6 +291,9 @@ public void run() { // inside {@link #updateJSError} after source mapping. if (mRedBoxHandler != null && errorType == ErrorType.NATIVE) { mRedBoxHandler.handleRedbox(message, stack, RedBoxHandler.ErrorType.NATIVE); + mRedBoxDialog.resetReporting(true); + } else { + mRedBoxDialog.resetReporting(false); } mRedBoxDialog.show(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java index 6079acc627abbb..01c4f803222606 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java @@ -9,10 +9,15 @@ package com.facebook.react.devsupport; +import javax.annotation.Nullable; + import android.app.Dialog; import android.content.Context; +import android.graphics.Color; import android.net.Uri; import android.os.AsyncTask; +import android.text.SpannedString; +import android.text.method.LinkMovementMethod; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -22,6 +27,7 @@ import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ListView; +import android.widget.ProgressBar; import android.widget.TextView; import com.facebook.common.logging.FLog; @@ -30,6 +36,7 @@ import com.facebook.react.common.MapBuilder; import com.facebook.react.common.ReactConstants; import com.facebook.react.devsupport.StackTraceHelper.StackFrame; +import com.facebook.react.devsupport.RedBoxHandler.ReportCompletedListener; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -44,11 +51,59 @@ private final DevSupportManager mDevSupportManager; private final DoubleTapReloadRecognizer mDoubleTapReloadRecognizer; + private final @Nullable RedBoxHandler mRedBoxHandler; private ListView mStackView; - private Button mReloadJs; - private Button mDismiss; - private Button mCopyToClipboard; + private Button mReloadJsButton; + private Button mDismissButton; + private Button mCopyToClipboardButton; + private @Nullable Button mReportButton; + private @Nullable TextView mReportTextView; + private @Nullable ProgressBar mLoadingIndicator; + private @Nullable View mLineSeparator; + private boolean isReporting = false; + + private ReportCompletedListener mReportCompletedListener = new ReportCompletedListener() { + @Override + public void onReportSuccess(final SpannedString spannedString) { + isReporting = false; + Assertions.assertNotNull(mReportButton).setEnabled(true); + Assertions.assertNotNull(mLoadingIndicator).setVisibility(View.GONE); + Assertions.assertNotNull(mReportTextView).setText(spannedString); + } + @Override + public void onReportError(final SpannedString spannedString) { + isReporting = false; + Assertions.assertNotNull(mReportButton).setEnabled(true); + Assertions.assertNotNull(mLoadingIndicator).setVisibility(View.GONE); + Assertions.assertNotNull(mReportTextView).setText(spannedString); + } + }; + + private View.OnClickListener mReportButtonOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mRedBoxHandler == null || !mRedBoxHandler.isReportEnabled() || isReporting) { + return; + } + isReporting = true; + Assertions.assertNotNull(mReportTextView).setText("Reporting..."); + Assertions.assertNotNull(mReportTextView).setVisibility(View.VISIBLE); + Assertions.assertNotNull(mLoadingIndicator).setVisibility(View.VISIBLE); + Assertions.assertNotNull(mLineSeparator).setVisibility(View.VISIBLE); + Assertions.assertNotNull(mReportButton).setEnabled(false); + + String title = Assertions.assertNotNull(mDevSupportManager.getLastErrorTitle()); + StackFrame[] stack = Assertions.assertNotNull(mDevSupportManager.getLastErrorStack()); + String sourceUrl = mDevSupportManager.getSourceUrl(); + + mRedBoxHandler.reportRedbox( + title, + stack, + sourceUrl, + Assertions.assertNotNull(mReportCompletedListener)); + } + }; private static class StackAdapter extends BaseAdapter { private static final int VIEW_TYPE_COUNT = 2; @@ -203,7 +258,10 @@ protected Void doInBackground(String... clipBoardString) { } } - protected RedBoxDialog(Context context, DevSupportManager devSupportManager) { + protected RedBoxDialog( + Context context, + DevSupportManager devSupportManager, + @Nullable RedBoxHandler redBoxHandler) { super(context, R.style.Theme_Catalyst_RedBox); requestWindowFeature(Window.FEATURE_NO_TITLE); @@ -212,25 +270,27 @@ protected RedBoxDialog(Context context, DevSupportManager devSupportManager) { mDevSupportManager = devSupportManager; mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); + mRedBoxHandler = redBoxHandler; mStackView = (ListView) findViewById(R.id.rn_redbox_stack); mStackView.setOnItemClickListener(this); - mReloadJs = (Button) findViewById(R.id.rn_redbox_reload_button); - mReloadJs.setOnClickListener(new View.OnClickListener() { + + mReloadJsButton = (Button) findViewById(R.id.rn_redbox_reload_button); + mReloadJsButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mDevSupportManager.handleReloadJS(); } }); - mDismiss = (Button) findViewById(R.id.rn_redbox_dismiss_button); - mDismiss.setOnClickListener(new View.OnClickListener() { + mDismissButton = (Button) findViewById(R.id.rn_redbox_dismiss_button); + mDismissButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); - mCopyToClipboard = (Button) findViewById(R.id.rn_redbox_copy_button); - mCopyToClipboard.setOnClickListener(new View.OnClickListener() { + mCopyToClipboardButton = (Button) findViewById(R.id.rn_redbox_copy_button); + mCopyToClipboardButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String title = mDevSupportManager.getLastErrorTitle(); @@ -242,12 +302,38 @@ public void onClick(View v) { StackTraceHelper.formatStackTrace(title, stack)); } }); + + if (mRedBoxHandler != null && mRedBoxHandler.isReportEnabled()) { + mLoadingIndicator = (ProgressBar) findViewById(R.id.rn_redbox_loading_indicator); + mLineSeparator = (View) findViewById(R.id.rn_redbox_line_separator); + mReportTextView = (TextView) findViewById(R.id.rn_redbox_report_label); + mReportTextView.setMovementMethod(LinkMovementMethod.getInstance()); + mReportTextView.setHighlightColor(Color.TRANSPARENT); + mReportButton = (Button) findViewById(R.id.rn_redbox_report_button); + mReportButton.setOnClickListener(mReportButtonOnClickListener); + } } public void setExceptionDetails(String title, StackFrame[] stack) { mStackView.setAdapter(new StackAdapter(title, stack)); } + /** + * Show the report button, hide the report textview and the loading indicator. + */ + public void resetReporting(boolean enabled) { + if (mRedBoxHandler == null || !mRedBoxHandler.isReportEnabled()) { + return; + } + isReporting = false; + Assertions.assertNotNull(mReportTextView).setVisibility(View.GONE); + Assertions.assertNotNull(mLoadingIndicator).setVisibility(View.GONE); + Assertions.assertNotNull(mLineSeparator).setVisibility(View.GONE); + Assertions.assertNotNull(mReportButton).setVisibility( + enabled ? View.VISIBLE : View.GONE); + Assertions.assertNotNull(mReportButton).setEnabled(true); + } + @Override public void onItemClick(AdapterView parent, View view, int position, long id) { new OpenStackFrameTask(mDevSupportManager).executeOnExecutor( diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java index e526257d61bfc9..2ec51cb7f51a76 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java @@ -9,12 +9,14 @@ package com.facebook.react.devsupport; +import android.text.SpannedString; + import com.facebook.react.devsupport.StackTraceHelper.StackFrame; /** * Interface used by {@link DevSupportManagerImpl} to allow interception on any redboxes * during development and handling the information from the redbox. - * The implementation should be passed by {@link #setRedBoxHandler} in {@link ReactInstanceManager}. + * The implementation should be passed by setRedBoxHandler in ReactInstanceManager. */ public interface RedBoxHandler { enum ErrorType { @@ -29,5 +31,31 @@ public String getName() { return name; } } + + /** + * Callback interface for {@link #reportRedbox}. + */ + interface ReportCompletedListener { + void onReportSuccess(SpannedString spannedString); + void onReportError(SpannedString spannedString); + } + + /** + * Handle the information from the redbox. + */ void handleRedbox(String title, StackFrame[] stack, ErrorType errorType); + + /** + * Whether the report feature is enabled. + */ + boolean isReportEnabled(); + + /** + * Report the information from the redbox and set up a callback listener. + */ + void reportRedbox( + String title, + StackFrame[] stack, + String sourceUrl, + ReportCompletedListener reportCompletedListener); } diff --git a/ReactAndroid/src/main/res/devsupport/layout/redbox_view.xml b/ReactAndroid/src/main/res/devsupport/layout/redbox_view.xml index 429cd40eea062a..259cc2e2c82089 100644 --- a/ReactAndroid/src/main/res/devsupport/layout/redbox_view.xml +++ b/ReactAndroid/src/main/res/devsupport/layout/redbox_view.xml @@ -11,6 +11,42 @@ android:layout_height="0dp" android:layout_weight="1" /> + + + + +