mirror of
https://github.com/jrpie/Launcher.git
synced 2025-05-15 15:01:53 +02:00
add crash handler
This commit is contained in:
parent
7a874ef89f
commit
04330ff407
12 changed files with 270 additions and 7 deletions
|
@ -8,6 +8,7 @@
|
||||||
tools:ignore="QueryAllPackagesPermission" />
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
|
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
|
||||||
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".Application"
|
android:name=".Application"
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/launcherBaseTheme"
|
android:theme="@style/launcherBaseTheme"
|
||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.widgets.manage.ManageWidgetPanelsActivity"
|
android:name=".ui.widgets.manage.ManageWidgetPanelsActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
@ -80,6 +82,9 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.LegalInfoActivity"
|
android:name=".ui.LegalInfoActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.ReportCrashActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".actions.lock.LauncherDeviceAdmin"
|
android:name=".actions.lock.LauncherDeviceAdmin"
|
||||||
|
@ -110,5 +115,4 @@
|
||||||
android:resource="@xml/accessibility_service_config" />
|
android:resource="@xml/accessibility_service_config" />
|
||||||
</service>
|
</service>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -25,6 +25,7 @@ import de.jrpie.android.launcher.preferences.resetPreferences
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
|
||||||
const val APP_WIDGET_HOST_ID = 42;
|
const val APP_WIDGET_HOST_ID = 42;
|
||||||
|
@ -106,6 +107,11 @@ class Application : android.app.Application() {
|
||||||
// TODO Error: Invalid resource ID 0x00000000.
|
// TODO Error: Invalid resource ID 0x00000000.
|
||||||
// DynamicColors.applyToActivitiesIfAvailable(this)
|
// DynamicColors.applyToActivitiesIfAvailable(this)
|
||||||
|
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
|
||||||
|
sendCrashNotification(this@Application, throwable)
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||||
torchManager = TorchManager(this)
|
torchManager = TorchManager(this)
|
||||||
|
@ -157,6 +163,8 @@ class Application : android.app.Application() {
|
||||||
removeUnusedShortcuts(this)
|
removeUnusedShortcuts(this)
|
||||||
}
|
}
|
||||||
loadApps()
|
loadApps()
|
||||||
|
|
||||||
|
createNotificationChannels(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCustomAppNames(): HashMap<AbstractAppInfo, String> {
|
fun getCustomAppNames(): HashMap<AbstractAppInfo, String> {
|
||||||
|
|
|
@ -6,9 +6,6 @@ import android.app.role.RoleManager
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.appwidget.AppWidgetManager
|
|
||||||
import android.appwidget.AppWidgetProvider
|
|
||||||
import android.appwidget.AppWidgetProviderInfo
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.LauncherApps
|
import android.content.pm.LauncherApps
|
||||||
|
@ -227,3 +224,13 @@ fun copyToClipboard(context: Context, text: String) {
|
||||||
val clipData = ClipData.newPlainText("Debug Info", text)
|
val clipData = ClipData.newPlainText("Debug Info", text)
|
||||||
clipboardManager.setPrimaryClip(clipData)
|
clipboardManager.setPrimaryClip(clipData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun writeEmail(context: Context, to: String, subject: String, text: String) {
|
||||||
|
val intent = Intent(Intent.ACTION_SENDTO)
|
||||||
|
intent.setData("mailto:".toUri())
|
||||||
|
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(to))
|
||||||
|
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, text)
|
||||||
|
context.startActivity(Intent.createChooser(intent, context.getString(R.string.send_email)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
87
app/src/main/java/de/jrpie/android/launcher/Notifications.kt
Normal file
87
app/src/main/java/de/jrpie/android/launcher/Notifications.kt
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package de.jrpie.android.launcher
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import de.jrpie.android.launcher.ui.EXTRA_CRASH_LOG
|
||||||
|
import de.jrpie.android.launcher.ui.ReportCrashActivity
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
private val NOTIFICATION_CHANNEL_CRASH = "launcher:crash"
|
||||||
|
|
||||||
|
fun createNotificationChannels(context: Context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val notificationChannel = NotificationChannel(
|
||||||
|
NOTIFICATION_CHANNEL_CRASH,
|
||||||
|
context.getString(R.string.notification_channel_crash),
|
||||||
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
|
)
|
||||||
|
|
||||||
|
val notificationManager =
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
notificationManager.createNotificationChannel(notificationChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestNotificationPermission(activity: Activity) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val permission =
|
||||||
|
(activity.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED)
|
||||||
|
|
||||||
|
if (!permission) {
|
||||||
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
arrayOf( android.Manifest.permission.POST_NOTIFICATIONS ),
|
||||||
|
1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendCrashNotification(context: Context, throwable: Throwable) {
|
||||||
|
val stringWriter = StringWriter()
|
||||||
|
val printWriter = PrintWriter(stringWriter)
|
||||||
|
throwable.printStackTrace(printWriter)
|
||||||
|
|
||||||
|
val intent = Intent(context, ReportCrashActivity::class.java)
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
intent.putExtra(EXTRA_CRASH_LOG, stringWriter.toString())
|
||||||
|
|
||||||
|
val pendingIntent = PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
Random.nextInt(),
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
|
||||||
|
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_CRASH)
|
||||||
|
.setSmallIcon(R.drawable.baseline_bug_report_24)
|
||||||
|
.setContentTitle(context.getString(R.string.notification_crash_title))
|
||||||
|
.setContentText(context.getString(R.string.notification_crash_explanation))
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setAutoCancel(false)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
|
|
||||||
|
val notificationManager = NotificationManagerCompat.from(context)
|
||||||
|
try {
|
||||||
|
notificationManager.notify(
|
||||||
|
0,
|
||||||
|
builder.build()
|
||||||
|
)
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
Log.e("Crash Notification", "Could not send notification")
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersio
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
|
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion4
|
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion4
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
|
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
|
||||||
|
import de.jrpie.android.launcher.sendCrashNotification
|
||||||
import de.jrpie.android.launcher.ui.HomeActivity
|
import de.jrpie.android.launcher.ui.HomeActivity
|
||||||
import de.jrpie.android.launcher.widgets.ClockWidget
|
import de.jrpie.android.launcher.widgets.ClockWidget
|
||||||
import de.jrpie.android.launcher.widgets.DebugInfoWidget
|
import de.jrpie.android.launcher.widgets.DebugInfoWidget
|
||||||
|
@ -76,6 +77,7 @@ fun migratePreferencesToNewVersion(context: Context) {
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Unable to restore preferences:\n${e.stackTrace}")
|
Log.e(TAG, "Unable to restore preferences:\n${e.stackTrace}")
|
||||||
|
sendCrashNotification(context, e)
|
||||||
resetPreferences(context)
|
resetPreferences(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package de.jrpie.android.launcher.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import de.jrpie.android.launcher.R
|
||||||
|
import de.jrpie.android.launcher.copyToClipboard
|
||||||
|
import de.jrpie.android.launcher.databinding.ActivityReportCrashBinding
|
||||||
|
import de.jrpie.android.launcher.getDeviceInfo
|
||||||
|
import de.jrpie.android.launcher.openInBrowser
|
||||||
|
import de.jrpie.android.launcher.writeEmail
|
||||||
|
|
||||||
|
const val EXTRA_CRASH_LOG = "crashLog"
|
||||||
|
|
||||||
|
class ReportCrashActivity : AppCompatActivity() {
|
||||||
|
// We don't know what caused the crash, so this Activity should use as little functionality as possible.
|
||||||
|
// In particular it is not a UIObject (and hence looks quite ugly)
|
||||||
|
private lateinit var binding: ActivityReportCrashBinding
|
||||||
|
private var report: String? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
// Initialise layout
|
||||||
|
binding = ActivityReportCrashBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
setTitle(R.string.report_crash_title)
|
||||||
|
setSupportActionBar(binding.reportCrashAppbar)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(false)
|
||||||
|
|
||||||
|
report = intent.getStringExtra(EXTRA_CRASH_LOG)
|
||||||
|
|
||||||
|
binding.reportCrashButtonCopy.setOnClickListener {
|
||||||
|
copyToClipboard(this,
|
||||||
|
"Device Info:\n${getDeviceInfo()}\n\nCrash Log:\n${report}")
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.reportCrashButtonMail.setOnClickListener {
|
||||||
|
writeEmail(
|
||||||
|
this,
|
||||||
|
getString(R.string.settings_meta_report_bug_mail),
|
||||||
|
"Crash in μLauncher",
|
||||||
|
"Hi!\nUnfortunately, μLauncher crashed:\n" +
|
||||||
|
"\nDevice Info\n\n${getDeviceInfo()}\n\n" +
|
||||||
|
"\nCrash Log\n\n${report}\n" +
|
||||||
|
"\nAdditional Information\n\n" +
|
||||||
|
"[Please add additional information: What did you do when the crash happened? Do you know how to trigger it? ... ]"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
binding.reportCrashButtonReport.setOnClickListener {
|
||||||
|
openInBrowser(
|
||||||
|
getString(R.string.settings_meta_report_bug_link),
|
||||||
|
this
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment
|
||||||
import de.jrpie.android.launcher.BuildConfig.VERSION_CODE
|
import de.jrpie.android.launcher.BuildConfig.VERSION_CODE
|
||||||
import de.jrpie.android.launcher.databinding.Tutorial5FinishBinding
|
import de.jrpie.android.launcher.databinding.Tutorial5FinishBinding
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
|
import de.jrpie.android.launcher.requestNotificationPermission
|
||||||
import de.jrpie.android.launcher.setDefaultHomeScreen
|
import de.jrpie.android.launcher.setDefaultHomeScreen
|
||||||
import de.jrpie.android.launcher.ui.UIObject
|
import de.jrpie.android.launcher.ui.UIObject
|
||||||
|
|
||||||
|
@ -31,8 +32,10 @@ class TutorialFragment5Finish : Fragment(), UIObject {
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super<Fragment>.onStart()
|
super<Fragment>.onStart()
|
||||||
super<UIObject>.onStart()
|
super<UIObject>.onStart()
|
||||||
|
requestNotificationPermission(requireActivity())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun setOnClicks() {
|
override fun setOnClicks() {
|
||||||
super.setOnClicks()
|
super.setOnClicks()
|
||||||
binding.tutorialFinishButtonStart.setOnClickListener { finishTutorial() }
|
binding.tutorialFinishButtonStart.setOnClickListener { finishTutorial() }
|
||||||
|
@ -44,6 +47,7 @@ class TutorialFragment5Finish : Fragment(), UIObject {
|
||||||
LauncherPreferences.internal().startedTime(System.currentTimeMillis() / 1000L)
|
LauncherPreferences.internal().startedTime(System.currentTimeMillis() / 1000L)
|
||||||
}
|
}
|
||||||
context?.let { setDefaultHomeScreen(it, checkDefault = true) }
|
context?.let { setDefaultHomeScreen(it, checkDefault = true) }
|
||||||
|
|
||||||
activity?.finish()
|
activity?.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
app/src/main/res/drawable/baseline_bug_report_24.xml
Normal file
11
app/src/main/res/drawable/baseline_bug_report_24.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:textColor"
|
||||||
|
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z" />
|
||||||
|
|
||||||
|
</vector>
|
59
app/src/main/res/layout/activity_report_crash.xml
Normal file
59
app/src/main/res/layout/activity_report_crash.xml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context=".ui.ReportCrashActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:background="@null"
|
||||||
|
app:elevation="0dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/report_crash_appbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" >
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="10dp">
|
||||||
|
|
||||||
|
<de.jrpie.android.launcher.ui.util.HtmlTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/crash_info" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/report_crash_button_copy"
|
||||||
|
android:text="@string/report_crash_button_copy"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<Space
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="20dp" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/report_crash_button_mail"
|
||||||
|
android:text="@string/report_crash_button_mail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/report_crash_button_report"
|
||||||
|
android:text="@string/report_crash_button_report"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -162,6 +162,7 @@
|
||||||
-->
|
-->
|
||||||
<string name="settings_meta_link_github" translatable="false">https://github.com/jrpie/Launcher</string>
|
<string name="settings_meta_link_github" translatable="false">https://github.com/jrpie/Launcher</string>
|
||||||
<string name="settings_meta_report_bug_link" translatable="false">https://github.com/jrpie/Launcher/issues/new?template=bug_report.yaml</string>
|
<string name="settings_meta_report_bug_link" translatable="false">https://github.com/jrpie/Launcher/issues/new?template=bug_report.yaml</string>
|
||||||
|
<string name="settings_meta_report_bug_mail" translatable="false">android-launcher-crash@jrpie.de</string>
|
||||||
<string name="settings_meta_report_vulnerability_link" translatable="false">https://github.com/jrpie/Launcher/security/policy</string>
|
<string name="settings_meta_report_vulnerability_link" translatable="false">https://github.com/jrpie/Launcher/security/policy</string>
|
||||||
<string name="settings_meta_fork_contact_url" translatable="false">https://s.jrpie.de/contact</string>
|
<string name="settings_meta_fork_contact_url" translatable="false">https://s.jrpie.de/contact</string>
|
||||||
<string name="settings_meta_privacy_url" translatable="false">https://s.jrpie.de/android-legal</string>
|
<string name="settings_meta_privacy_url" translatable="false">https://s.jrpie.de/android-legal</string>
|
||||||
|
|
|
@ -419,5 +419,28 @@
|
||||||
<string name="list_other_open_widget_panel">Open Widget Panel</string>
|
<string name="list_other_open_widget_panel">Open Widget Panel</string>
|
||||||
<string name="alert_widget_panel_not_found">This widget panel no longer exists.</string>
|
<string name="alert_widget_panel_not_found">This widget panel no longer exists.</string>
|
||||||
<string name="settings_launcher_section_widgets">Widgets</string>
|
<string name="settings_launcher_section_widgets">Widgets</string>
|
||||||
|
<string name="notification_crash_title">μLauncher crashed</string>
|
||||||
|
<string name="notification_crash_explanation">Sorry! Click for more information.</string>
|
||||||
|
<string name="crash_info"><![CDATA[
|
||||||
|
Looks like something went wrong, sorry about that!<br><br>
|
||||||
|
For privacy reasons, crash logs are not collected automatically.<br>
|
||||||
|
However logs are very useful for debugging, so I would be very grateful if you could send me the attached log by mail
|
||||||
|
or create a bug report on github.<br><br>
|
||||||
|
Note that crash logs might contain <strong>sensitive information</strong>, e.g. the name of an app you tried to launch.
|
||||||
|
Please <strong>redact</strong> such information before sending the report.
|
||||||
|
<h2>What can I do now?</h2>
|
||||||
|
If this bug appears again and again, you can try several things:
|
||||||
|
<ul>
|
||||||
|
<li>Force stop μLauncher</li>
|
||||||
|
<li>Clear μLauncher\'s storage (<strong>Your settings will be lost!</strong>)</li>
|
||||||
|
<li>Install an older version (<a href=\"https://github.com/jrpie/Launcher/releases\">GitHub</a>, <a href=\"https://f-droid.org/en/packages/de.jrpie.android.launcher\">F-Droid</a>)</li>
|
||||||
|
</ul>
|
||||||
|
]]>
|
||||||
|
</string>
|
||||||
|
<string name="report_crash_button_copy">Copy crash report to clipboard</string>
|
||||||
|
<string name="report_crash_button_mail">Send report by mail</string>
|
||||||
|
<string name="report_crash_button_report">Create bug report on GitHub</string>
|
||||||
|
<string name="report_crash_title">μLauncher crashed</string>
|
||||||
|
<string name="send_email">Send Email</string>
|
||||||
|
<string name="notification_channel_crash">Crashes and Debug Information</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '2.0.0'
|
ext.kotlin_version = '2.0.0'
|
||||||
ext.android_plugin_version = '8.9.2'
|
ext.android_plugin_version = '8.10.0'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -10,7 +10,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.9.2'
|
classpath 'com.android.tools.build:gradle:8.10.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "com.android.tools.build:gradle:$android_plugin_version"
|
classpath "com.android.tools.build:gradle:$android_plugin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue