mirror of
https://github.com/fankes/termux-app.git
synced 2025-09-05 02:05:25 +08:00
Implement crash handler and reporting
Now whenever the Termux app crashes, the crash report (stacktrace, app and device info) will be logged to ~/crash_log.md file. When the user will reopen the app, a notification will be shown which when clicked will show the crash report content in the ReportActivity. The activity will have important links like email, reddit, github issues of termux app and packages at which the user can optionally report an issue if necessary after copying the crash report text. The ~/crash_log.md file will be moved to ~/crash_log-backup.md so that a notification is not shown again on next startup and can be viewed again via SAF, etc. This will allow reports for bugs that are submitted to have complete and useful info, specially in markdown format, making lives of devs a tad bit easier. Also more bugs that are rare might be submitted since users will have the info to report with and know where to report at. ToDo: - The TermuxConstants.TERMUX_SUPPORT_EMAIL_URL needs to be updated with a valid support email once its set up. The TermuxUtils.getReportIssueMarkdownString() function currently also has "email" lines commented out which will need to be uncommented. - Currently, crashes will only be handled for the main app thread, other threads will have to manually hooked into where necessary.
This commit is contained in:
@@ -34,6 +34,7 @@ import com.termux.R;
|
||||
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
|
||||
import com.termux.app.activities.HelpActivity;
|
||||
import com.termux.app.activities.SettingsActivity;
|
||||
import com.termux.app.crash.CrashUtils;
|
||||
import com.termux.app.settings.preferences.TermuxAppSharedPreferences;
|
||||
import com.termux.app.terminal.TermuxSessionsListViewController;
|
||||
import com.termux.app.terminal.io.TerminalToolbarViewPager;
|
||||
@@ -155,6 +156,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
|
||||
Logger.logDebug(LOG_TAG, "onCreate");
|
||||
|
||||
// Check if a crash happened on last run of the app and show a
|
||||
// notification with the crash details if it did
|
||||
CrashUtils.notifyCrash(this, LOG_TAG);
|
||||
|
||||
// Load termux shared preferences and properties
|
||||
mPreferences = new TermuxAppSharedPreferences(this);
|
||||
mProperties = new TermuxSharedProperties(this);
|
||||
|
@@ -2,6 +2,7 @@ package com.termux.app;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.termux.app.crash.CrashHandler;
|
||||
import com.termux.app.settings.preferences.TermuxAppSharedPreferences;
|
||||
import com.termux.app.utils.Logger;
|
||||
|
||||
@@ -10,10 +11,14 @@ public class TermuxApplication extends Application {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
updateLogLevel();
|
||||
// Set crash handler for the app
|
||||
CrashHandler.setCrashHandler(this);
|
||||
|
||||
// Set log level for the app
|
||||
setLogLevel();
|
||||
}
|
||||
|
||||
private void updateLogLevel() {
|
||||
private void setLogLevel() {
|
||||
// Load the log level from shared preferences and set it to the {@link Loggger.CURRENT_LOG_LEVEL}
|
||||
TermuxAppSharedPreferences preferences = new TermuxAppSharedPreferences(getApplicationContext());
|
||||
preferences.setLogLevel(null, preferences.getLogLevel());
|
||||
|
@@ -18,7 +18,6 @@ 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.app.models.ReportInfo;
|
||||
|
||||
import org.commonmark.node.FencedCodeBlock;
|
||||
@@ -32,6 +31,7 @@ public class ReportActivity extends AppCompatActivity {
|
||||
private static final String EXTRA_REPORT_INFO = "report_info";
|
||||
|
||||
ReportInfo mReportInfo;
|
||||
String mReportMarkdownString;
|
||||
String mReportActivityMarkdownString;
|
||||
|
||||
@Override
|
||||
@@ -131,11 +131,11 @@ public class ReportActivity extends AppCompatActivity {
|
||||
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.title_report_text), mReportActivityMarkdownString);
|
||||
if (mReportMarkdownString != null)
|
||||
ShareUtils.shareText(this, getString(R.string.title_report_text), mReportMarkdownString);
|
||||
} else if (id == R.id.menu_item_copy_report) {
|
||||
if (mReportInfo != null)
|
||||
ShareUtils.copyTextToClipboard(this, mReportActivityMarkdownString, null);
|
||||
if (mReportMarkdownString != null)
|
||||
ShareUtils.copyTextToClipboard(this, mReportMarkdownString, null);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -145,7 +145,16 @@ public class ReportActivity extends AppCompatActivity {
|
||||
* Generate the markdown {@link String} to be shown in {@link ReportActivity}.
|
||||
*/
|
||||
private void generateReportActivityMarkdownString() {
|
||||
mReportActivityMarkdownString = ReportInfo.getReportInfoMarkdownString(this, mReportInfo);
|
||||
mReportMarkdownString = ReportInfo.getReportInfoMarkdownString(mReportInfo);
|
||||
|
||||
mReportActivityMarkdownString = "";
|
||||
if(mReportInfo.reportStringPrefix != null)
|
||||
mReportActivityMarkdownString += mReportInfo.reportStringPrefix;
|
||||
|
||||
mReportActivityMarkdownString += mReportMarkdownString;
|
||||
|
||||
if(mReportInfo.reportStringSuffix != null)
|
||||
mReportActivityMarkdownString += mReportInfo.reportStringSuffix;
|
||||
}
|
||||
|
||||
|
||||
|
34
app/src/main/java/com/termux/app/crash/CrashHandler.java
Normal file
34
app/src/main/java/com/termux/app/crash/CrashHandler.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.termux.app.crash;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Catches uncaught exceptions and logs them.
|
||||
*/
|
||||
public class CrashHandler implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private final Context context;
|
||||
private final Thread.UncaughtExceptionHandler defaultUEH;
|
||||
|
||||
private CrashHandler(final Context context) {
|
||||
this.context = context;
|
||||
this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
|
||||
}
|
||||
|
||||
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
|
||||
CrashUtils.logCrash(context,thread, throwable);
|
||||
defaultUEH.uncaughtException(thread, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default uncaught crash handler of current thread to {@link CrashHandler}.
|
||||
*/
|
||||
public static void setCrashHandler(final Context context) {
|
||||
if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof CrashHandler)) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(context));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
189
app/src/main/java/com/termux/app/crash/CrashUtils.java
Normal file
189
app/src/main/java/com/termux/app/crash/CrashUtils.java
Normal file
@@ -0,0 +1,189 @@
|
||||
package com.termux.app.crash;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.termux.R;
|
||||
import com.termux.app.activities.ReportActivity;
|
||||
import com.termux.app.file.FileUtils;
|
||||
import com.termux.app.models.ReportInfo;
|
||||
import com.termux.app.models.UserAction;
|
||||
import com.termux.app.settings.preferences.TermuxAppSharedPreferences;
|
||||
import com.termux.app.settings.preferences.TermuxPreferenceConstants;
|
||||
import com.termux.app.utils.DataUtils;
|
||||
import com.termux.app.utils.Logger;
|
||||
import com.termux.app.utils.MarkdownUtils;
|
||||
import com.termux.app.utils.NotificationUtils;
|
||||
import com.termux.app.utils.TermuxUtils;
|
||||
|
||||
import com.termux.app.TermuxConstants;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class CrashUtils {
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_ID_CRASH_REPORT_ERRORS = "termux_crash_reports_notification_channel";
|
||||
private static final String NOTIFICATION_CHANNEL_NAME_CRASH_REPORT_ERRORS = TermuxConstants.TERMUX_APP_NAME + " Crash Reports";
|
||||
|
||||
private static final String LOG_TAG = "CrashUtils";
|
||||
|
||||
/**
|
||||
* Log a crash in the crash log file at
|
||||
* {@link TermuxConstants#TERMUX_CRASH_LOG_FILE_PATH}.
|
||||
*
|
||||
* @param context The {@link Context} for operations.
|
||||
* @param thread The {@link Thread} in which the crash happened.
|
||||
* @param thread The {@link Throwable} thrown for the crash.
|
||||
*/
|
||||
public static void logCrash(final Context context, final Thread thread, final Throwable throwable) {
|
||||
|
||||
StringBuilder reportString = new StringBuilder();
|
||||
|
||||
reportString.append("## Crash Details\n");
|
||||
reportString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Crash Thread", thread.toString(), "-"));
|
||||
reportString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Crash Timestamp", TermuxUtils.getCurrentTimeStamp(), "-"));
|
||||
|
||||
reportString.append("\n\n").append(Logger.getStackTracesMarkdownString("Stacktrace", Logger.getStackTraceStringArray(throwable)));
|
||||
reportString.append("\n\n").append(TermuxUtils.getAppInfoMarkdownString(context, true));
|
||||
reportString.append("\n\n").append(TermuxUtils.getDeviceInfoMarkdownString(context));
|
||||
|
||||
// Log report string to logcat
|
||||
Logger.logError(reportString.toString());
|
||||
|
||||
// Write report string to crash log file
|
||||
String errmsg = FileUtils.writeStringToFile(context, "crash log", TermuxConstants.TERMUX_CRASH_LOG_FILE_PATH, Charset.defaultCharset(), reportString.toString(), false);
|
||||
if(errmsg != null) {
|
||||
Logger.logError(LOG_TAG, errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the user of a previous app crash by reading the crash info from the crash log file at
|
||||
* {@link TermuxConstants#TERMUX_CRASH_LOG_FILE_PATH}.
|
||||
*
|
||||
* If the crash log file exists and is not empty and
|
||||
* {@link TermuxPreferenceConstants.TERMUX_APP#KEY_CRASH_REPORT_NOTIFICATIONS_ENABLED} is
|
||||
* enabled, then a notification will be shown for the crash on the
|
||||
* {@link #NOTIFICATION_CHANNEL_NAME_CRASH_REPORT_ERRORS} channel, otherwise nothing will be done.
|
||||
*
|
||||
* After reading from the crash log file, it will be moved to {@link TermuxConstants#TERMUX_CRASH_LOG_BACKUP_FILE_PATH}.
|
||||
*
|
||||
* @param context The {@link Context} for operations.
|
||||
* @param logTagParam The log tag to use for logging.
|
||||
*/
|
||||
public static void notifyCrash(final Context context, final String logTagParam) {
|
||||
if(context == null) return;
|
||||
|
||||
|
||||
TermuxAppSharedPreferences preferences = new TermuxAppSharedPreferences(context);
|
||||
// If user has disabled notifications for crashes
|
||||
if (!preferences.getCrashReportNotificationsEnabled())
|
||||
return;
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
String logTag = DataUtils.getDefaultIfNull(logTagParam, LOG_TAG);
|
||||
|
||||
if(!FileUtils.regularFileExists(TermuxConstants.TERMUX_CRASH_LOG_FILE_PATH, false))
|
||||
return;
|
||||
|
||||
String errmsg;
|
||||
StringBuilder reportStringBuilder = new StringBuilder();
|
||||
|
||||
// Read report string from crash log file
|
||||
errmsg = FileUtils.readStringFromFile(context, "crash log", TermuxConstants.TERMUX_CRASH_LOG_FILE_PATH, Charset.defaultCharset(), reportStringBuilder, false);
|
||||
if(errmsg != null) {
|
||||
Logger.logError(logTag, errmsg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move crash log file to backup location if it exists
|
||||
FileUtils.moveRegularFile(context, "crash log", TermuxConstants.TERMUX_CRASH_LOG_FILE_PATH, TermuxConstants.TERMUX_CRASH_LOG_BACKUP_FILE_PATH, true);
|
||||
if(errmsg != null) {
|
||||
Logger.logError(logTag, errmsg);
|
||||
}
|
||||
|
||||
String reportString = reportStringBuilder.toString();
|
||||
|
||||
if(reportString == null || reportString.isEmpty())
|
||||
return;
|
||||
|
||||
// Send a notification to show the crash log which when clicked will open the {@link ReportActivity}
|
||||
// to show the details of the crash
|
||||
String title = TermuxConstants.TERMUX_APP_NAME + " Crash Report";
|
||||
|
||||
Logger.logDebug(logTag, "The crash log file at \"" + TermuxConstants.TERMUX_CRASH_LOG_FILE_PATH + "\" found. Sending \"" + title + "\" notification.");
|
||||
|
||||
Intent notificationIntent = ReportActivity.newInstance(context, new ReportInfo(UserAction.CRASH_REPORT, logTag, title, null, reportString, "\n\n" + TermuxUtils.getReportIssueMarkdownString(context), true));
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
// Setup the notification channel if not already set up
|
||||
setupCrashReportsNotificationChannel(context);
|
||||
|
||||
// Build the notification
|
||||
Notification.Builder builder = getCrashReportsNotificationBuilder(context, title, null, null, pendingIntent, NotificationUtils.NOTIFICATION_MODE_VIBRATE);
|
||||
if(builder == null) return;
|
||||
|
||||
// Send the notification
|
||||
int nextNotificationId = NotificationUtils.getNextNotificationId(context);
|
||||
NotificationManager notificationManager = NotificationUtils.getNotificationManager(context);
|
||||
if(notificationManager != null)
|
||||
notificationManager.notify(nextNotificationId, builder.build());
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link Notification.Builder} for {@link #NOTIFICATION_CHANNEL_ID_CRASH_REPORT_ERRORS}
|
||||
* and {@link #NOTIFICATION_CHANNEL_NAME_CRASH_REPORT_ERRORS}.
|
||||
*
|
||||
* @param context The {@link Context} for operations.
|
||||
* @param title The title for the notification.
|
||||
* @param notifiationText The second line text of the notification.
|
||||
* @param notificationBigText The full text of the notification that may optionally be styled.
|
||||
* @param pendingIntent The {@link PendingIntent} which should be sent when notification is clicked.
|
||||
* @param notificationMode The notification mode. It must be one of {@code NotificationUtils.NOTIFICATION_MODE_*}.
|
||||
* @return Returns the {@link Notification.Builder}.
|
||||
*/
|
||||
@Nullable
|
||||
public static Notification.Builder getCrashReportsNotificationBuilder(final Context context, final CharSequence title, final CharSequence notifiationText, final CharSequence notificationBigText, final PendingIntent pendingIntent, final int notificationMode) {
|
||||
|
||||
Notification.Builder builder = NotificationUtils.geNotificationBuilder(context,
|
||||
NOTIFICATION_CHANNEL_ID_CRASH_REPORT_ERRORS, Notification.PRIORITY_HIGH,
|
||||
title, notifiationText, notificationBigText, pendingIntent, notificationMode);
|
||||
|
||||
if(builder == null) return null;
|
||||
|
||||
// Enable timestamp
|
||||
builder.setShowWhen(true);
|
||||
|
||||
// Set notification icon
|
||||
builder.setSmallIcon(R.drawable.ic_error_notification);
|
||||
|
||||
// Set background color for small notification icon
|
||||
builder.setColor(0xFF607D8B);
|
||||
|
||||
// Dismiss on click
|
||||
builder.setAutoCancel(true);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the notification channel for {@link #NOTIFICATION_CHANNEL_ID_CRASH_REPORT_ERRORS} and
|
||||
* {@link #NOTIFICATION_CHANNEL_NAME_CRASH_REPORT_ERRORS}.
|
||||
*
|
||||
* @param context The {@link Context} for operations.
|
||||
*/
|
||||
public static void setupCrashReportsNotificationChannel(final Context context) {
|
||||
NotificationUtils.setupNotificationChannel(context, NOTIFICATION_CHANNEL_ID_CRASH_REPORT_ERRORS,
|
||||
NOTIFICATION_CHANNEL_NAME_CRASH_REPORT_ERRORS, NotificationManager.IMPORTANCE_HIGH);
|
||||
}
|
||||
|
||||
}
|
@@ -415,7 +415,7 @@ public class ExecutionCommand {
|
||||
}
|
||||
|
||||
public String geStackTracesMarkdownString() {
|
||||
return Logger.getStackTracesMarkdownString("StackTraces:", Logger.getStackTraceStringArray(throwableList));
|
||||
return Logger.getStackTracesMarkdownString("StackTraces", Logger.getStackTraceStringArray(throwableList));
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package com.termux.app.models;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.termux.app.utils.MarkdownUtils;
|
||||
import com.termux.app.utils.TermuxUtils;
|
||||
|
||||
@@ -15,49 +13,51 @@ public class ReportInfo implements Serializable {
|
||||
public String sender;
|
||||
/** The report title. */
|
||||
public String reportTitle;
|
||||
/** The markdown text for the report. */
|
||||
/** The markdown report text prefix. Will not be part of copy and share operations, etc. */
|
||||
public String reportStringPrefix;
|
||||
/** The markdown report text. */
|
||||
public String reportString;
|
||||
/** The markdown report text suffix. Will not be part of copy and share operations, etc. */
|
||||
public String reportStringSuffix;
|
||||
/** If set to {@code true}, then report, app and device info will be added to the report when
|
||||
* markdown is generated.
|
||||
*/
|
||||
public boolean addReportInfoToMarkdown;
|
||||
/** The timestamp for the report. */
|
||||
public String creationTimestammp;
|
||||
public String reportTimestammp;
|
||||
|
||||
public ReportInfo(UserAction userAction, String sender, String reportTitle, String reportString, boolean addReportInfoToMarkdown) {
|
||||
public ReportInfo(UserAction userAction, String sender, String reportTitle, String reportStringPrefix, String reportString, String reportStringSuffix, boolean addReportInfoToMarkdown) {
|
||||
this.userAction = userAction;
|
||||
this.sender = sender;
|
||||
this.reportTitle = reportTitle;
|
||||
this.reportStringPrefix = reportStringPrefix;
|
||||
this.reportString = reportString;
|
||||
this.reportStringSuffix = reportStringSuffix;
|
||||
this.addReportInfoToMarkdown = addReportInfoToMarkdown;
|
||||
this.creationTimestammp = TermuxUtils.getCurrentTimeStamp();
|
||||
this.reportTimestammp = TermuxUtils.getCurrentTimeStamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a markdown {@link String} for {@link ReportInfo}.
|
||||
*
|
||||
* @param currentPackageContext The context of current package.
|
||||
* @param reportInfo The {@link ReportInfo} to convert.
|
||||
* @return Returns the markdown {@link String}.
|
||||
*/
|
||||
public static String getReportInfoMarkdownString(final Context currentPackageContext, final ReportInfo reportInfo) {
|
||||
public static String getReportInfoMarkdownString(final ReportInfo reportInfo) {
|
||||
if (reportInfo == null) return "null";
|
||||
|
||||
StringBuilder markdownString = new StringBuilder();
|
||||
|
||||
markdownString.append(reportInfo.reportString);
|
||||
|
||||
if(reportInfo.addReportInfoToMarkdown) {
|
||||
markdownString.append("## Report Info\n\n");
|
||||
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("User Action", reportInfo.userAction, "-"));
|
||||
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Sender", reportInfo.sender, "-"));
|
||||
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Creation Timestamp", reportInfo.creationTimestammp, "-"));
|
||||
markdownString.append("\n##\n");
|
||||
|
||||
markdownString.append("\n\n").append(TermuxUtils.getAppInfoMarkdownString(currentPackageContext, true));
|
||||
markdownString.append("\n\n").append(TermuxUtils.getDeviceInfoMarkdownString(currentPackageContext));
|
||||
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Report Timestamp", reportInfo.reportTimestammp, "-"));
|
||||
markdownString.append("\n##\n\n");
|
||||
}
|
||||
|
||||
markdownString.append(reportInfo.reportString);
|
||||
|
||||
return markdownString.toString();
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,8 @@ package com.termux.app.models;
|
||||
|
||||
public enum UserAction {
|
||||
|
||||
PLUGIN_EXECUTION_COMMAND("plugin execution command");
|
||||
PLUGIN_EXECUTION_COMMAND("plugin execution command"),
|
||||
CRASH_REPORT("crash report");
|
||||
|
||||
private final String name;
|
||||
|
||||
|
@@ -117,6 +117,13 @@ public class MarkdownUtils {
|
||||
return "**" + label + "**: " + def + "\n";
|
||||
}
|
||||
|
||||
public static String getLinkMarkdownString(String label, Object object) {
|
||||
if (object != null)
|
||||
return "[" + label + "](" + object + ")";
|
||||
else
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
/** Check following for more info:
|
||||
* https://github.com/noties/Markwon/tree/v4.6.2/app-sample
|
||||
|
@@ -149,7 +149,13 @@ public class PluginUtils {
|
||||
// to show the details of the error
|
||||
String title = TermuxConstants.TERMUX_APP_NAME + " Plugin Execution Command Error";
|
||||
|
||||
Intent notificationIntent = ReportActivity.newInstance(context, new ReportInfo(UserAction.PLUGIN_EXECUTION_COMMAND, logTag, title, ExecutionCommand.getExecutionCommandMarkdownString(executionCommand), true));
|
||||
StringBuilder reportString = new StringBuilder();
|
||||
|
||||
reportString.append(ExecutionCommand.getExecutionCommandMarkdownString(executionCommand));
|
||||
reportString.append("\n\n").append(TermuxUtils.getAppInfoMarkdownString(context, true));
|
||||
reportString.append("\n\n").append(TermuxUtils.getDeviceInfoMarkdownString(context));
|
||||
|
||||
Intent notificationIntent = ReportActivity.newInstance(context, new ReportInfo(UserAction.PLUGIN_EXECUTION_COMMAND, logTag, title, null, reportString.toString(), null,true));
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
// Setup the notification channel if not already set up
|
||||
|
@@ -11,6 +11,7 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import com.termux.R;
|
||||
import com.termux.app.TermuxConstants;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -238,6 +239,49 @@ public class TermuxUtils {
|
||||
return markdownString.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a markdown {@link String} for reporting an issue.
|
||||
*
|
||||
* @param context The context for operations.
|
||||
* @return Returns the markdown {@link String}.
|
||||
*/
|
||||
public static String getReportIssueMarkdownString(@NonNull final Context context) {
|
||||
if (context == null) return "null";
|
||||
|
||||
StringBuilder markdownString = new StringBuilder();
|
||||
|
||||
markdownString.append("## Report Issue");
|
||||
|
||||
markdownString.append("\n\n").append(context.getString(R.string.msg_report_issue)).append("\n");
|
||||
|
||||
//markdownString.append("\n\n### Email\n");
|
||||
//markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_SUPPORT_EMAIL, TermuxConstants.TERMUX_SUPPORT_EMAIL_MAILTO_URL)).append(" ");
|
||||
|
||||
markdownString.append("\n\n### Reddit\n");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_REDDIT_SUBREDDIT, TermuxConstants.TERMUX_REDDIT_SUBREDDIT_URL)).append(" ");
|
||||
|
||||
markdownString.append("\n\n### Github Issues for Termux apps\n");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_APP_NAME, TermuxConstants.TERMUX_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_API_APP_NAME, TermuxConstants.TERMUX_API_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_BOOT_APP_NAME, TermuxConstants.TERMUX_BOOT_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_FLOAT_APP_NAME, TermuxConstants.TERMUX_FLOAT_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_STYLING_APP_NAME, TermuxConstants.TERMUX_STYLING_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_TASKER_APP_NAME, TermuxConstants.TERMUX_TASKER_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_WIDGET_APP_NAME, TermuxConstants.TERMUX_WIDGET_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
|
||||
markdownString.append("\n\n### Github Issues for Termux packages\n");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_GAME_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_GAME_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_SCIENCE_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_SCIENCE_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_ROOT_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_ROOT_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_UNSTABLE_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_UNSTABLE_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_X11_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_X11_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
||||
|
||||
markdownString.append("\n##\n");
|
||||
|
||||
return markdownString.toString();
|
||||
}
|
||||
|
||||
public static Properties getSystemProperties() {
|
||||
Properties systemProperties = new Properties();
|
||||
|
||||
@@ -311,7 +355,7 @@ public class TermuxUtils {
|
||||
|
||||
public static String getCurrentTimeStamp() {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return df.format(new Date());
|
||||
}
|
||||
|
@@ -158,6 +158,8 @@
|
||||
<string name="title_share_with">Share With</string>
|
||||
<string name="title_report_text">Report Text</string>
|
||||
|
||||
<string name="msg_report_issue">If you think this report should be reported, then copy its text from the options menu (3-dots on top right) and post an issue on one of the following links at which the report belongs at.</string>
|
||||
|
||||
|
||||
|
||||
<!-- Termux PermissionUtils -->
|
||||
|
Reference in New Issue
Block a user