mirror of
https://github.com/fankes/termux-app.git
synced 2025-09-07 03:05:18 +08:00
Add ReportActivity and ReportInfo
This implements the framework to report info to users. This may include reporting failure or result of commands or any exceptions that are raised. The ReportInfo provides 5 fields: - userAction: The user action that was being processed for which the report was generated. - sender: The internal app component that sent the report. - title: The report title. - reportString: The markdown text for the report. - addReportAndDeviceDetails: If set to true, then report and device details will be added to the report. This should provide the basics parameters for showing a report to the user. The ReportActivity also allows user to copy and share the report. In future this can also be used to allow users to easily email or post crash reports to github for Termux app crashes instead of going through logcat.
This commit is contained in:
@@ -101,6 +101,12 @@
|
||||
android:label="@string/title_activity_termux_settings"
|
||||
android:theme="@style/Theme.AppCompat.Light.DarkActionBar" />
|
||||
|
||||
<activity
|
||||
android:name=".app.activities.ReportActivity"
|
||||
android:theme="@style/Theme.AppCompat.TermuxReportActivity"
|
||||
android:documentLaunchMode="intoExisting"
|
||||
/>
|
||||
|
||||
<activity
|
||||
android:name=".filepicker.TermuxFileReceiverActivity"
|
||||
android:excludeFromRecents="true"
|
||||
|
190
app/src/main/java/com/termux/app/activities/ReportActivity.java
Normal file
190
app/src/main/java/com/termux/app/activities/ReportActivity.java
Normal file
@@ -0,0 +1,190 @@
|
||||
package com.termux.app.activities;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.termux.R;
|
||||
import com.termux.app.TermuxConstants;
|
||||
import com.termux.app.utils.MarkdownUtils;
|
||||
import com.termux.app.utils.ShareUtils;
|
||||
import com.termux.app.utils.TermuxUtils;
|
||||
import com.termux.models.ReportInfo;
|
||||
|
||||
import org.commonmark.node.FencedCodeBlock;
|
||||
|
||||
import io.noties.markwon.Markwon;
|
||||
import io.noties.markwon.recycler.MarkwonAdapter;
|
||||
import io.noties.markwon.recycler.SimpleEntry;
|
||||
|
||||
public class ReportActivity extends AppCompatActivity {
|
||||
|
||||
private static final String ARG_REPORT_INFO = "report_info";
|
||||
|
||||
ReportInfo mReportInfo;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_report);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
if (toolbar != null) {
|
||||
setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
Bundle bundle = null;
|
||||
Intent intent = getIntent();
|
||||
if(intent != null)
|
||||
bundle = intent.getExtras();
|
||||
else if(savedInstanceState != null)
|
||||
bundle = savedInstanceState;
|
||||
|
||||
updateUI(bundle);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
|
||||
if(intent != null)
|
||||
updateUI(intent.getExtras());
|
||||
}
|
||||
|
||||
private void updateUI(Bundle bundle) {
|
||||
|
||||
if (bundle == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
mReportInfo = (ReportInfo) bundle.getSerializable(ARG_REPORT_INFO);
|
||||
|
||||
if (mReportInfo == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
if (mReportInfo.reportTitle != null)
|
||||
actionBar.setTitle(mReportInfo.reportTitle);
|
||||
else
|
||||
actionBar.setTitle(TermuxConstants.TERMUX_APP_NAME + " App Report");
|
||||
}
|
||||
|
||||
|
||||
RecyclerView recyclerView = findViewById(R.id.recycler_view);
|
||||
|
||||
final Markwon markwon = MarkdownUtils.getRecyclerMarkwonBuilder(this);
|
||||
|
||||
final MarkwonAdapter adapter = MarkwonAdapter.builderTextViewIsRoot(R.layout.activity_report_adapter_node_default)
|
||||
.include(FencedCodeBlock.class, SimpleEntry.create(R.layout.activity_report_adapter_node_code_block, R.id.text_view))
|
||||
.build();
|
||||
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
adapter.setMarkdown(markwon, mReportInfo.reportString + getReportAndDeviceDetailsMarkdownString());
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putSerializable(ARG_REPORT_INFO, mReportInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||
final MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_report, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Remove activity from recents menu on back button press
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.menu_item_share_report) {
|
||||
if (mReportInfo != null)
|
||||
ShareUtils.shareText(this, getString(R.string.report_text), mReportInfo.reportString);
|
||||
} else if (id == R.id.menu_item_copy_report) {
|
||||
if (mReportInfo != null)
|
||||
ShareUtils.copyTextToClipboard(this, mReportInfo.reportString, null);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a markdown {@link String} for {@link #mReportInfo} and device details.
|
||||
*
|
||||
* @return Returns the markdown {@link String}.
|
||||
*/
|
||||
private String getReportAndDeviceDetailsMarkdownString() {
|
||||
if(!mReportInfo.addReportAndDeviceDetails) return "";
|
||||
|
||||
StringBuilder markdownString = new StringBuilder();
|
||||
|
||||
markdownString.append("\n\n### Report And Device Details\n\n");
|
||||
|
||||
if (mReportInfo != null) {
|
||||
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("User Action", mReportInfo.userAction, "-"));
|
||||
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Sender", mReportInfo.sender, "-"));
|
||||
}
|
||||
|
||||
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Timestamp", TermuxUtils.getCurrentTimeStamp(), "-"));
|
||||
|
||||
markdownString.append("\n\n").append(TermuxUtils.getDeviceDetailsMarkdownString(this));
|
||||
|
||||
markdownString.append("\n##\n");
|
||||
|
||||
return markdownString.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void startReportActivity(@NonNull final Context context, @NonNull final ReportInfo reportInfo) {
|
||||
if(context == null) return;
|
||||
context.startActivity(newInstance(context, reportInfo));
|
||||
}
|
||||
|
||||
public static Intent newInstance(@NonNull final Context context, @NonNull final ReportInfo reportInfo) {
|
||||
if(context == null) return null;
|
||||
|
||||
Intent intent = new Intent(context, ReportActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putSerializable(ARG_REPORT_INFO, reportInfo);
|
||||
intent.putExtras(bundle);
|
||||
|
||||
// Note that ReportActivity task has documentLaunchMode="intoExisting" set in AndroidManifest.xml
|
||||
// which has equivalent behaviour to the following. The following dynamic way doesn't seem to
|
||||
// work for notification pending intent, i.e separate task isn't created and activity is
|
||||
// launched in the same task as TermuxActivity.
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
return intent;
|
||||
}
|
||||
|
||||
}
|
26
app/src/main/java/com/termux/models/ReportInfo.java
Normal file
26
app/src/main/java/com/termux/models/ReportInfo.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.termux.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class ReportInfo implements Serializable {
|
||||
|
||||
/** The user action that was being processed for which the report was generated. */
|
||||
public UserAction userAction;
|
||||
/** The internal app component that sent the report. */
|
||||
public String sender;
|
||||
/** The report title. */
|
||||
public String reportTitle;
|
||||
/** The markdown text for the report. */
|
||||
public String reportString;
|
||||
/** If set to {@code true}, then report and device details will be added to the report. */
|
||||
public boolean addReportAndDeviceDetails;
|
||||
|
||||
public ReportInfo(UserAction userAction, String sender, String reportTitle, String reportString, boolean addReportAndDeviceDetails) {
|
||||
this.userAction = userAction;
|
||||
this.sender = sender;
|
||||
this.reportTitle = reportTitle;
|
||||
this.reportString = reportString;
|
||||
this.addReportAndDeviceDetails = addReportAndDeviceDetails;
|
||||
}
|
||||
|
||||
}
|
17
app/src/main/java/com/termux/models/UserAction.java
Normal file
17
app/src/main/java/com/termux/models/UserAction.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.termux.models;
|
||||
|
||||
public enum UserAction {
|
||||
|
||||
PLUGIN_EXECUTION_COMMAND("plugin execution command");
|
||||
|
||||
private final String name;
|
||||
|
||||
UserAction(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
5
app/src/main/res/drawable/ic_copy.xml
Normal file
5
app/src/main/res/drawable/ic_copy.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_share.xml
Normal file
5
app/src/main/res/drawable/ic_share.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||
</vector>
|
21
app/src/main/res/layout/activity_report.xml
Normal file
21
app/src/main/res/layout/activity_report.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
layout="@layout/toolbar_layout"
|
||||
android:id="@+id/toolbar_layout"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
android:paddingTop="@dimen/content_padding"
|
||||
android:paddingBottom="36dip" />
|
||||
|
||||
</LinearLayout>
|
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:scrollbarStyle="outsideInset">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/background_markdown_code_block"
|
||||
android:fontFamily="monospace"
|
||||
android:lineSpacingExtra="2dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingTop="8dip"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingBottom="8dip"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textSize="12sp"
|
||||
android:textIsSelectable="true" />
|
||||
|
||||
</HorizontalScrollView>
|
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dip"
|
||||
android:layout_marginRight="16dip"
|
||||
android:breakStrategy="simple"
|
||||
android:hyphenationFrequency="none"
|
||||
android:lineSpacingExtra="2dip"
|
||||
android:paddingTop="8dip"
|
||||
android:paddingBottom="8dip"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="#000"
|
||||
android:textSize="12sp"
|
||||
android:textIsSelectable="true" />
|
22
app/src/main/res/layout/toolbar_layout.xml
Normal file
22
app/src/main/res/layout/toolbar_layout.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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:id="@+id/toolbar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimaryDark"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
|
||||
app:titleTextAppearance="@style/Toolbar.Title">
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</LinearLayout>
|
15
app/src/main/res/menu/menu_report.xml
Normal file
15
app/src/main/res/menu/menu_report.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_item_share_report"
|
||||
android:icon="@drawable/ic_share"
|
||||
android:title="@string/share"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/menu_item_copy_report"
|
||||
android:icon="@drawable/ic_copy"
|
||||
android:title="@string/copy"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
5
app/src/main/res/values/colors.xml
Normal file
5
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="background_markdown_code_inline">#1F000000</color>
|
||||
<color name="background_markdown_code_block">#0F000000</color>
|
||||
</resources>
|
10
app/src/main/res/values/dimens.xml
Normal file
10
app/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
|
||||
<dimen name="content_padding">8dip</dimen>
|
||||
<dimen name="content_padding_double">16dip</dimen>
|
||||
<dimen name="content_padding_half">4dip</dimen>
|
||||
</resources>
|
@@ -89,6 +89,10 @@
|
||||
<string name="run_command_service_invalid_command_path">Invalid coommand path to RunCommandService: \"%1$s\"</string>
|
||||
<string name="run_command_service_allow_external_apps_ungranted_warning">RunCommandService require allow-external-apps property to be set to \"true\" in &TERMUX_PROPERTIES_PRIMARY_PATH_SHORT; file.</string>
|
||||
|
||||
<string name="share">Share</string>
|
||||
<string name="share_with">Share With</string>
|
||||
<string name="copy">Copy</string>
|
||||
<string name="report_text">Report Text</string>
|
||||
|
||||
|
||||
|
||||
@@ -96,8 +100,6 @@
|
||||
<string name="settings">Settings</string>
|
||||
<string name="title_activity_termux_settings">&TERMUX_APP_NAME; Settings</string>
|
||||
|
||||
|
||||
|
||||
<!-- Debugging Preferences -->
|
||||
<string name="debugging_preferences">Debugging</string>
|
||||
|
||||
@@ -118,7 +120,6 @@
|
||||
<string name="terminal_view_key_logging_off">Logs will not have entries for terminal view keys. (Default)</string>
|
||||
<string name="terminal_view_key_logging_on">Logcat logs will have entries for terminal view keys. These are very verbose and should be disabled under normal circumstances or will cause performance issues.</string>
|
||||
|
||||
|
||||
<!-- Plugin Error Notifications -->
|
||||
<string name="plugin_error_notifications_title">Plugin Error Notifications</string>
|
||||
<string name="plugin_error_notifications_off">Disable flashes and notifications for plugin errors.</string>
|
||||
|
@@ -21,10 +21,6 @@
|
||||
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TermuxAlertDialogStyle" parent="@android:style/Theme.Material.Light.Dialog.Alert">
|
||||
<!-- Seen in buttons on alert dialog: -->
|
||||
<item name="android:colorAccent">#212121</item>
|
||||
</style>
|
||||
|
||||
<!-- See https://developer.android.com/training/material/theme.html for how to customize the Material theme. -->
|
||||
<!-- NOTE: Cannot use "Light." since it hides the terminal scrollbar on the default black background. -->
|
||||
@@ -46,4 +42,20 @@
|
||||
<item name="android:windowAllowReturnTransitionOverlap">true</item>
|
||||
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="Theme.AppCompat.TermuxReportActivity" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="colorPrimaryDark">#FF0000</item>
|
||||
</style>
|
||||
|
||||
<style name="Toolbar.Title" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="TermuxAlertDialogStyle" parent="@android:style/Theme.Material.Light.Dialog.Alert">
|
||||
<!-- Seen in buttons on alert dialog: -->
|
||||
<item name="android:colorAccent">#212121</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
Reference in New Issue
Block a user