use listview for redbox stacktrace, open file on click
Reviewed By: @andreicoman11 Differential Revision: D2512364 fb-gh-sync-id: 5f2c90db7eca010185080f726fd3ef0ee519cdbc
This commit is contained in:
Родитель
06ef95799a
Коммит
b5890e1283
|
@ -43,6 +43,7 @@ import com.facebook.react.bridge.UiThreadUtil;
|
|||
import com.facebook.react.bridge.WebsocketJavaScriptExecutor;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.ShakeDetector;
|
||||
import com.facebook.react.devsupport.StackTraceHelper.StackFrame;
|
||||
import com.facebook.react.modules.debug.DeveloperSettings;
|
||||
|
||||
/**
|
||||
|
@ -154,8 +155,7 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
|
|||
public void handleException(Exception e) {
|
||||
if (mIsDevSupportEnabled) {
|
||||
FLog.e(ReactConstants.TAG, "Exception in native call from JS", e);
|
||||
CharSequence details = ExceptionFormatterHelper.javaStackTraceToHtml(e.getStackTrace());
|
||||
showNewError(e.getMessage(), details, JAVA_ERROR_COOKIE);
|
||||
showNewError(e.getMessage(), StackTraceHelper.convertJavaStackTrace(e), JAVA_ERROR_COOKIE);
|
||||
} else {
|
||||
if (e instanceof RuntimeException) {
|
||||
// Because we are rethrowing the original exception, the original stacktrace will be
|
||||
|
@ -179,7 +179,7 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
|
|||
}
|
||||
|
||||
public void showNewJSError(String message, ReadableArray details, int errorCookie) {
|
||||
showNewError(message, ExceptionFormatterHelper.jsStackTraceToHtml(details), errorCookie);
|
||||
showNewError(message, StackTraceHelper.convertJsStackTrace(details), errorCookie);
|
||||
}
|
||||
|
||||
public void updateJSError(
|
||||
|
@ -198,8 +198,9 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
|
|||
errorCookie != mRedBoxDialog.getErrorCookie()) {
|
||||
return;
|
||||
}
|
||||
mRedBoxDialog.setTitle(message);
|
||||
mRedBoxDialog.setDetails(ExceptionFormatterHelper.jsStackTraceToHtml(details));
|
||||
mRedBoxDialog.setExceptionDetails(
|
||||
message,
|
||||
StackTraceHelper.convertJsStackTrace(details));
|
||||
mRedBoxDialog.show();
|
||||
}
|
||||
});
|
||||
|
@ -207,7 +208,7 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
|
|||
|
||||
private void showNewError(
|
||||
final String message,
|
||||
final CharSequence details,
|
||||
final StackFrame[] stack,
|
||||
final int errorCookie) {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
|
@ -222,8 +223,7 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
|
|||
// show the first and most actionable one.
|
||||
return;
|
||||
}
|
||||
mRedBoxDialog.setTitle(message);
|
||||
mRedBoxDialog.setDetails(details);
|
||||
mRedBoxDialog.setExceptionDetails(message, stack);
|
||||
mRedBoxDialog.setErrorCookie(errorCookie);
|
||||
mRedBoxDialog.show();
|
||||
}
|
||||
|
@ -520,7 +520,7 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
|
|||
public void run() {
|
||||
showNewError(
|
||||
mApplicationContext.getString(R.string.catalyst_remotedbg_error),
|
||||
ExceptionFormatterHelper.javaStackTraceToHtml(cause.getStackTrace()),
|
||||
StackTraceHelper.convertJavaStackTrace(cause),
|
||||
JAVA_ERROR_COOKIE);
|
||||
}
|
||||
});
|
||||
|
@ -555,13 +555,12 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
|
|||
DebugServerException debugServerException = (DebugServerException) cause;
|
||||
showNewError(
|
||||
debugServerException.description,
|
||||
ExceptionFormatterHelper.debugServerExcStackTraceToHtml(
|
||||
(DebugServerException) cause),
|
||||
StackTraceHelper.convertJavaStackTrace(cause),
|
||||
JAVA_ERROR_COOKIE);
|
||||
} else {
|
||||
showNewError(
|
||||
mApplicationContext.getString(R.string.catalyst_jsload_error),
|
||||
ExceptionFormatterHelper.javaStackTraceToHtml(cause.getStackTrace()),
|
||||
StackTraceHelper.convertJavaStackTrace(cause),
|
||||
JAVA_ERROR_COOKIE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.text.Html;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
/**
|
||||
* Helper class for displaying errors in an eye-catching form (red box).
|
||||
*/
|
||||
/* package */ class ExceptionFormatterHelper {
|
||||
|
||||
private static String getStackTraceHtmlComponent(
|
||||
String methodName, String filename, int lineNumber, int columnNumber) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
methodName = methodName.replace("<", "<").replace(">", ">");
|
||||
stringBuilder.append("<font color=#FDE5E5>")
|
||||
.append(methodName)
|
||||
.append("</font><br /><font color=#F9B3B3>")
|
||||
.append(filename)
|
||||
.append(":")
|
||||
.append(lineNumber);
|
||||
if (columnNumber != -1) {
|
||||
stringBuilder
|
||||
.append(":")
|
||||
.append(columnNumber);
|
||||
}
|
||||
stringBuilder.append("</font><br /><br />");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public static CharSequence jsStackTraceToHtml(ReadableArray stack) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
ReadableMap frame = stack.getMap(i);
|
||||
String methodName = frame.getString("methodName");
|
||||
String fileName = new File(frame.getString("file")).getName();
|
||||
int lineNumber = frame.getInt("lineNumber");
|
||||
int columnNumber = -1;
|
||||
if (frame.hasKey("column") && !frame.isNull("column")) {
|
||||
columnNumber = frame.getInt("column");
|
||||
}
|
||||
stringBuilder.append(getStackTraceHtmlComponent(
|
||||
methodName, fileName, lineNumber, columnNumber));
|
||||
}
|
||||
return Html.fromHtml(stringBuilder.toString());
|
||||
}
|
||||
|
||||
public static CharSequence javaStackTraceToHtml(StackTraceElement[] stack) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i< stack.length; i++) {
|
||||
stringBuilder.append(getStackTraceHtmlComponent(
|
||||
stack[i].getMethodName(), stack[i].getFileName(), stack[i].getLineNumber(), -1));
|
||||
|
||||
}
|
||||
return Html.fromHtml(stringBuilder.toString());
|
||||
}
|
||||
|
||||
public static CharSequence debugServerExcStackTraceToHtml(DebugServerException e) {
|
||||
String s = getStackTraceHtmlComponent("", e.fileName, e.lineNumber, e.column);
|
||||
return Html.fromHtml(s);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,28 +11,166 @@ package com.facebook.react.devsupport;
|
|||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.method.ScrollingMovementMethod;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.R;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.devsupport.StackTraceHelper.StackFrame;
|
||||
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Dialog for displaying JS errors in an eye-catching form (red box).
|
||||
*/
|
||||
/* package */ class RedBoxDialog extends Dialog {
|
||||
/* package */ class RedBoxDialog extends Dialog implements AdapterView.OnItemClickListener {
|
||||
|
||||
private final DevSupportManager mDevSupportManager;
|
||||
|
||||
private TextView mTitle;
|
||||
private TextView mDetails;
|
||||
private ListView mStackView;
|
||||
private Button mReloadJs;
|
||||
private int mCookie = 0;
|
||||
|
||||
private static class StackAdapter extends BaseAdapter {
|
||||
private static final int VIEW_TYPE_COUNT = 2;
|
||||
private static final int VIEW_TYPE_TITLE = 0;
|
||||
private static final int VIEW_TYPE_STACKFRAME = 1;
|
||||
|
||||
private final String mTitle;
|
||||
private final StackFrame[] mStack;
|
||||
|
||||
private static class FrameViewHolder {
|
||||
private final TextView mMethodView;
|
||||
private final TextView mFileView;
|
||||
|
||||
private FrameViewHolder(View v) {
|
||||
mMethodView = (TextView) v.findViewById(R.id.rn_frame_method);
|
||||
mFileView = (TextView) v.findViewById(R.id.rn_frame_file);
|
||||
}
|
||||
}
|
||||
|
||||
public StackAdapter(String title, StackFrame[] stack) {
|
||||
mTitle = title;
|
||||
mStack = stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areAllItemsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int position) {
|
||||
return position > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mStack.length + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return position == 0 ? mTitle : mStack[position - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return VIEW_TYPE_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return position == 0 ? VIEW_TYPE_TITLE : VIEW_TYPE_STACKFRAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (position == 0) {
|
||||
TextView title = convertView != null
|
||||
? (TextView) convertView
|
||||
: (TextView) LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.redbox_item_title, parent, false);
|
||||
title.setText(mTitle);
|
||||
return title;
|
||||
} else {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.redbox_item_frame, parent, false);
|
||||
convertView.setTag(new FrameViewHolder(convertView));
|
||||
}
|
||||
StackFrame frame = mStack[position - 1];
|
||||
FrameViewHolder holder = (FrameViewHolder) convertView.getTag();
|
||||
holder.mMethodView.setText(frame.getMethod());
|
||||
holder.mFileView.setText(frame.getFileName() + ":" + frame.getLine());
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class OpenStackFrameTask extends AsyncTask<StackFrame, Void, Void> {
|
||||
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
|
||||
private final DevSupportManager mDevSupportManager;
|
||||
|
||||
private OpenStackFrameTask(DevSupportManager devSupportManager) {
|
||||
mDevSupportManager = devSupportManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(StackFrame... stackFrames) {
|
||||
try {
|
||||
String openStackFrameUrl =
|
||||
Uri.parse(mDevSupportManager.getSourceUrl()).buildUpon()
|
||||
.path("/open-stack-frame")
|
||||
.query(null)
|
||||
.build()
|
||||
.toString();
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
for (StackFrame frame: stackFrames) {
|
||||
String payload = stackFrameToJson(frame).toString();
|
||||
RequestBody body = RequestBody.create(JSON, payload);
|
||||
Request request = new Request.Builder().url(openStackFrameUrl).post(body).build();
|
||||
client.newCall(request).execute();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FLog.e(ReactConstants.TAG, "Could not open stack frame", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static JSONObject stackFrameToJson(StackFrame frame) {
|
||||
return new JSONObject(
|
||||
MapBuilder.of(
|
||||
"file", frame.getFile(),
|
||||
"methodName", frame.getMethod(),
|
||||
"lineNumber", frame.getLine(),
|
||||
"column", frame.getColumn()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected RedBoxDialog(Context context, DevSupportManager devSupportManager) {
|
||||
super(context, R.style.Theme_Catalyst_RedBox);
|
||||
|
||||
|
@ -42,12 +180,9 @@ import com.facebook.react.R;
|
|||
|
||||
mDevSupportManager = devSupportManager;
|
||||
|
||||
mTitle = (TextView) findViewById(R.id.catalyst_redbox_title);
|
||||
mDetails = (TextView) findViewById(R.id.catalyst_redbox_details);
|
||||
mDetails.setTypeface(Typeface.MONOSPACE);
|
||||
mDetails.setHorizontallyScrolling(true);
|
||||
mDetails.setMovementMethod(new ScrollingMovementMethod());
|
||||
mReloadJs = (Button) findViewById(R.id.catalyst_redbox_reloadjs);
|
||||
mStackView = (ListView) findViewById(R.id.rn_redbox_stack);
|
||||
mStackView.setOnItemClickListener(this);
|
||||
mReloadJs = (Button) findViewById(R.id.rn_redbox_reloadjs);
|
||||
mReloadJs.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -56,12 +191,8 @@ import com.facebook.react.R;
|
|||
});
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
mTitle.setText(title);
|
||||
}
|
||||
|
||||
public void setDetails(CharSequence details) {
|
||||
mDetails.setText(details);
|
||||
public void setExceptionDetails(String title, StackFrame[] stack) {
|
||||
mStackView.setAdapter(new StackAdapter(title, stack));
|
||||
}
|
||||
|
||||
public void setErrorCookie(int cookie) {
|
||||
|
@ -72,6 +203,13 @@ import com.facebook.react.R;
|
|||
return mCookie;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
new OpenStackFrameTask(mDevSupportManager).executeOnExecutor(
|
||||
AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
(StackFrame) mStackView.getAdapter().getItem(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
/**
|
||||
* Helper class converting JS and Java stack traces into arrays of {@link StackFrame} objects.
|
||||
*/
|
||||
/* package */ class StackTraceHelper {
|
||||
|
||||
/**
|
||||
* Represents a generic entry in a stack trace, be it originally from JS or Java.
|
||||
*/
|
||||
public static class StackFrame {
|
||||
private final String mFile;
|
||||
private final String mMethod;
|
||||
private final int mLine;
|
||||
private final int mColumn;
|
||||
private final String mFileName;
|
||||
|
||||
private StackFrame(String file, String method, int line, int column) {
|
||||
mFile = file;
|
||||
mMethod = method;
|
||||
mLine = line;
|
||||
mColumn = column;
|
||||
mFileName = new File(file).getName();
|
||||
}
|
||||
|
||||
private StackFrame(String file, String fileName, String method, int line, int column) {
|
||||
mFile = file;
|
||||
mFileName = fileName;
|
||||
mMethod = method;
|
||||
mLine = line;
|
||||
mColumn = column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file this stack frame points to.
|
||||
*
|
||||
* JS traces return the full path to the file here, while Java traces only return the file name
|
||||
* (the path is not known).
|
||||
*/
|
||||
public String getFile() {
|
||||
return mFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the method this frame points to.
|
||||
*/
|
||||
public String getMethod() {
|
||||
return mMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the line number this frame points to in the file returned by {@link #getFile()}.
|
||||
*/
|
||||
public int getLine() {
|
||||
return mLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column this frame points to in the file returned by {@link #getFile()}.
|
||||
*/
|
||||
public int getColumn() {
|
||||
return mColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get just the name of the file this frame points to.
|
||||
*
|
||||
* For JS traces this is different from {@link #getFile()} in that it only returns the file
|
||||
* name, not the full path. For Java traces there is no difference.
|
||||
*/
|
||||
public String getFileName() {
|
||||
return mFileName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a JavaScript stack trace (see {@code parseErrorStack} JS module) to an array of
|
||||
* {@link StackFrame}s.
|
||||
*/
|
||||
public static StackFrame[] convertJsStackTrace(ReadableArray stack) {
|
||||
StackFrame[] result = new StackFrame[stack.size()];
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
ReadableMap frame = stack.getMap(i);
|
||||
String methodName = frame.getString("methodName");
|
||||
String fileName = frame.getString("file");
|
||||
int lineNumber = frame.getInt("lineNumber");
|
||||
int columnNumber = -1;
|
||||
if (frame.hasKey("column") && !frame.isNull("column")) {
|
||||
columnNumber = frame.getInt("column");
|
||||
}
|
||||
result[i] = new StackFrame(fileName, methodName, lineNumber, columnNumber);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a {@link Throwable} to an array of {@link StackFrame}s.
|
||||
*/
|
||||
public static StackFrame[] convertJavaStackTrace(Throwable exception) {
|
||||
StackTraceElement[] stackTrace = exception.getStackTrace();
|
||||
StackFrame[] result = new StackFrame[stackTrace.length];
|
||||
for (int i = 0; i < stackTrace.length; i++) {
|
||||
result[i] = new StackFrame(
|
||||
stackTrace[i].getClassName(),
|
||||
stackTrace[i].getFileName(),
|
||||
stackTrace[i].getMethodName(),
|
||||
stackTrace[i].getLineNumber(),
|
||||
0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/rn_frame_method"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="10sp"
|
||||
android:fontFamily="monospace"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/rn_frame_file"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#E6B8B8"
|
||||
android:textSize="8sp"
|
||||
android:fontFamily="monospace"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,10 @@
|
|||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/catalyst_redbox_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
/>
|
|
@ -1,54 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="#E80000"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/catalyst_redbox_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:drawableLeft="@android:drawable/ic_dialog_alert"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="@android:style/TextAppearance.Holo.Medium"
|
||||
tools:text="Error"
|
||||
/>
|
||||
<ImageView
|
||||
android:id="@+id/catalyst_redbox_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/catalyst_redbox_title"
|
||||
android:scaleType="fitXY"
|
||||
android:gravity="fill_horizontal"
|
||||
android:src="@android:drawable/divider_horizontal_dark"
|
||||
/>
|
||||
<ListView
|
||||
android:id="@+id/rn_redbox_stack"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/catalyst_redbox_reloadjs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:text="@string/catalyst_reloadjs"
|
||||
android:textColor="@android:color/black"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/catalyst_redbox_details"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_below="@id/catalyst_redbox_divider"
|
||||
android:layout_above="@id/catalyst_redbox_reloadjs"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:padding="8dp"
|
||||
android:textSize="10sp"
|
||||
android:scrollbars="vertical|horizontal"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="Error message"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
android:id="@+id/rn_redbox_reloadjs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/catalyst_reloadjs"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
|
Загрузка…
Ссылка в новой задаче