mirror of
https://codeberg.org/timelimit/opentimelimit-android.git
synced 2025-10-05 02:39:34 +02:00
Improve background loop exception presentation
This commit is contained in:
parent
931b884567
commit
bb0decc808
13 changed files with 149 additions and 25 deletions
|
@ -122,6 +122,14 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.diagnose.exception.DiagnoseExceptionActivity"
|
||||
android:theme="@style/AppTheme.Translucent"
|
||||
android:exported="false"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity=":exception"
|
||||
android:launchMode="singleTop" />
|
||||
|
||||
<!-- system integration -->
|
||||
|
||||
<receiver android:name=".integration.platform.android.receiver.BootReceiver" android:exported="false">
|
||||
|
|
|
@ -220,7 +220,8 @@ data class AppStatusMessage(
|
|||
val title: String,
|
||||
val text: String,
|
||||
val subtext: String? = null,
|
||||
val showSwitchToDefaultUserOption: Boolean = false
|
||||
val showSwitchToDefaultUserOption: Boolean = false,
|
||||
val showErrorMessage: Boolean = false
|
||||
): Parcelable
|
||||
|
||||
data class BatteryStatus(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -26,6 +26,7 @@ import io.timelimit.android.logic.DefaultAppLogic
|
|||
import io.timelimit.android.sync.actions.SignOutAtDeviceAction
|
||||
import io.timelimit.android.sync.actions.apply.ApplyActionUtil
|
||||
import io.timelimit.android.ui.MainActivity
|
||||
import io.timelimit.android.ui.diagnose.exception.DiagnoseExceptionActivity
|
||||
|
||||
class BackgroundActionService: Service() {
|
||||
companion object {
|
||||
|
@ -36,7 +37,7 @@ class BackgroundActionService: Service() {
|
|||
fun prepareRevokeTemporarilyAllowed(context: Context) = Intent(context, BackgroundActionService::class.java)
|
||||
.putExtra(ACTION, ACTION_REVOKE_TEMPORARILY_ALLOWED_APPS)
|
||||
|
||||
fun getSwitchToDefaultUserIntent(context: Context) = PendingIntent.getService(
|
||||
fun getSwitchToDefaultUserIntent(context: Context): PendingIntent = PendingIntent.getService(
|
||||
context,
|
||||
PendingIntentIds.SWITCH_TO_DEFAULT_USER,
|
||||
Intent(context, BackgroundActionService::class.java)
|
||||
|
@ -44,12 +45,22 @@ class BackgroundActionService: Service() {
|
|||
PendingIntentIds.PENDING_INTENT_FLAGS
|
||||
)
|
||||
|
||||
fun getOpenAppIntent(context: Context) = PendingIntent.getActivity(
|
||||
fun getOpenAppIntent(context: Context): PendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
PendingIntentIds.OPEN_MAIN_APP,
|
||||
Intent(context, MainActivity::class.java),
|
||||
PendingIntentIds.PENDING_INTENT_FLAGS
|
||||
)
|
||||
|
||||
fun getOpenAppWithErrorIntent(context: Context): PendingIntent = PendingIntent.getActivities(
|
||||
context,
|
||||
PendingIntentIds.OPEN_MAIN_APP_WITH_ERROR,
|
||||
arrayOf(
|
||||
Intent(context, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
||||
Intent(context, DiagnoseExceptionActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
),
|
||||
PendingIntentIds.PENDING_INTENT_FLAGS
|
||||
)
|
||||
}
|
||||
|
||||
private val notificationManager: NotificationManager by lazy {
|
||||
|
|
|
@ -84,7 +84,12 @@ class BackgroundService: Service() {
|
|||
.setContentTitle(appStatusMessage.title)
|
||||
.setContentText(appStatusMessage.text)
|
||||
.setSubText(appStatusMessage.subtext)
|
||||
.setContentIntent(BackgroundActionService.getOpenAppIntent(context))
|
||||
.setContentIntent(
|
||||
if (appStatusMessage.showErrorMessage)
|
||||
BackgroundActionService.getOpenAppWithErrorIntent(context)
|
||||
else
|
||||
BackgroundActionService.getOpenAppIntent(context)
|
||||
)
|
||||
.setWhen(0)
|
||||
.setShowWhen(false)
|
||||
.setSound(null)
|
||||
|
|
|
@ -29,6 +29,7 @@ object NotificationIds {
|
|||
const val REVOKE_TEMPORARILY_ALLOWED_APPS = 3
|
||||
const val TIME_WARNING = 4
|
||||
const val EXTRA_TIME_STARTED = 5
|
||||
const val OPEN_MAIN_APP_WITH_ERROR = 6
|
||||
}
|
||||
|
||||
object NotificationChannels {
|
||||
|
@ -133,6 +134,7 @@ object PendingIntentIds {
|
|||
const val SWITCH_TO_DEFAULT_USER = 3
|
||||
const val U2F_NFC_DISCOVERY = 4
|
||||
const val U2F_USB_RESPONSE = 5
|
||||
const val OPEN_MAIN_APP_WITH_ERROR = 6
|
||||
|
||||
val PENDING_INTENT_FLAGS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
|
|
|
@ -777,7 +777,8 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
|||
appLogic.platformIntegration.setAppStatusMessage(AppStatusMessage(
|
||||
appLogic.context.getString(R.string.background_logic_error),
|
||||
appLogic.context.getString(R.string.background_logic_error_internal),
|
||||
showSwitchToDefaultUserOption = deviceRelatedData.canSwitchToDefaultUser
|
||||
showSwitchToDefaultUserOption = deviceRelatedData.canSwitchToDefaultUser,
|
||||
showErrorMessage = true
|
||||
))
|
||||
appLogic.platformIntegration.setShowBlockingOverlay(false)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Open TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
* Open TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -29,6 +29,7 @@ import io.timelimit.android.extensions.safeNavigate
|
|||
import io.timelimit.android.livedata.liveDataFromNonNullValue
|
||||
import io.timelimit.android.livedata.liveDataFromNullableValue
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.ui.diagnose.exception.DiagnoseExceptionDialogFragment
|
||||
import io.timelimit.android.ui.main.ActivityViewModelHolder
|
||||
import io.timelimit.android.ui.main.AuthenticationFab
|
||||
import io.timelimit.android.ui.main.FragmentWithCustomTitle
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.timelimit.android.ui.diagnose.exception
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
|
||||
class DiagnoseExceptionActivity: FragmentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val ex = DefaultAppLogic.with(this).backgroundTaskLogic.lastLoopException.value
|
||||
|
||||
if (ex != null) {
|
||||
if (savedInstanceState == null) {
|
||||
DiagnoseExceptionDialogFragment.newInstance(ex, true).show(supportFragmentManager)
|
||||
}
|
||||
} else finish()
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,12 +13,13 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.timelimit.android.ui.diagnose
|
||||
package io.timelimit.android.ui.diagnose.exception
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
|
@ -26,35 +27,26 @@ import androidx.fragment.app.DialogFragment
|
|||
import androidx.fragment.app.FragmentManager
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.extensions.showSafe
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
class DiagnoseExceptionDialogFragment: DialogFragment() {
|
||||
companion object {
|
||||
private const val DIALOG_TAG = "DiagnoseExceptionDialogFragment"
|
||||
private const val EXCEPTION = "ex"
|
||||
private const val FINISH_ON_DISMISS = "finish"
|
||||
|
||||
fun newInstance(exception: Exception) = DiagnoseExceptionDialogFragment().apply {
|
||||
fun newInstance(exception: Exception, finishOnDismiss: Boolean = false) = DiagnoseExceptionDialogFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(EXCEPTION, exception)
|
||||
putBoolean(FINISH_ON_DISMISS, finishOnDismiss)
|
||||
}
|
||||
}
|
||||
|
||||
fun getStackTraceString(tr: Throwable): String = StringWriter().let { sw ->
|
||||
PrintWriter(sw).let { pw ->
|
||||
tr.printStackTrace(pw)
|
||||
pw.flush()
|
||||
}
|
||||
|
||||
sw.toString()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val message = getStackTraceString(arguments!!.getSerializable(EXCEPTION) as Exception)
|
||||
val clipboard = context!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val message = ExceptionUtil.formatInterpreted(requireContext(), requireArguments().getSerializable(EXCEPTION) as Exception)
|
||||
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
|
||||
return AlertDialog.Builder(context!!, theme)
|
||||
return AlertDialog.Builder(requireContext(), theme)
|
||||
.setMessage(message)
|
||||
.setNeutralButton(R.string.diagnose_copy_to_clipboard) { _, _ ->
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("TimeLimit", message))
|
||||
|
@ -65,5 +57,13 @@ class DiagnoseExceptionDialogFragment: DialogFragment() {
|
|||
.create()
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
|
||||
if (requireArguments().getBoolean(FINISH_ON_DISMISS) && isResumed) {
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.timelimit.android.ui.diagnose.exception
|
||||
|
||||
import android.content.Context
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.integration.platform.android.foregroundapp.InstanceIdForegroundAppHelper.InstanceIdException
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
object ExceptionUtil {
|
||||
fun formatInterpreted(context: Context, tr: Throwable): String {
|
||||
val explain = when (tr) {
|
||||
is InstanceIdException.EventsNotSortedByTimestamp -> context.getString(R.string.background_logic_errpr_detailed_instanceid_sorting)
|
||||
else -> null
|
||||
}
|
||||
|
||||
val tr2 = formatSimple(tr)
|
||||
|
||||
return if (explain != null) "$explain\n\n$tr2"
|
||||
else tr2
|
||||
}
|
||||
|
||||
private fun formatSimple(tr: Throwable): String = StringWriter().let { sw ->
|
||||
PrintWriter(sw).let { pw ->
|
||||
tr.printStackTrace(pw)
|
||||
pw.flush()
|
||||
}
|
||||
|
||||
sw.toString()
|
||||
}
|
||||
}
|
|
@ -123,6 +123,12 @@
|
|||
<string name="background_logic_opening_lockscreen">Sperrbildschirm wird geöffnet</string>
|
||||
<string name="background_logic_permission_sanction_title">Berechtigung fehlt</string>
|
||||
<string name="background_logic_permission_sanction_text">nehme an, dass alles verwendet wird</string>
|
||||
<string name="background_logic_errpr_detailed_instanceid_sorting">Es gibt ein Problem mit der
|
||||
Nutzungsstatisitkdatenbank Ihres Gerätes.
|
||||
Deshalb kann TimeLimit nicht zuverlässig erkennen, welche App verwendet wird.
|
||||
Dieses Problem kann nur behoben werden durch vier Tagen Geduld oder das Zurücksetzen des Gerätes.
|
||||
Eine Neuinstallation von TimeLimit hilft NICHT.
|
||||
</string>
|
||||
|
||||
<string name="background_logic_temporarily_allowed_title">Apps wurden vorübergehend erlaubt</string>
|
||||
<string name="background_logic_temporarily_allowed_text">Zum Rückgängig machen hier tippen oder Bildschirm ausschalten</string>
|
||||
|
|
|
@ -154,6 +154,12 @@
|
|||
<string name="background_logic_opening_lockscreen">Opening lock screen</string>
|
||||
<string name="background_logic_permission_sanction_title">Missing permission</string>
|
||||
<string name="background_logic_permission_sanction_text">assuming that everything is used</string>
|
||||
<string name="background_logic_errpr_detailed_instanceid_sorting">There is an issue with
|
||||
the data in the usage stats database of your device.
|
||||
Due to that, TimeLimit can not reliable detect the running Apps.
|
||||
You can do nothing to solve this except waiting for four days or doing a reset of the whole device.
|
||||
Reinstalling TimeLimit does NOT help.
|
||||
</string>
|
||||
|
||||
<string name="background_logic_temporarily_allowed_title">Apps are temporarily allowed</string>
|
||||
<string name="background_logic_temporarily_allowed_text">Tap here or turn screen off to undo</string>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
Open TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
Open TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -34,6 +34,10 @@
|
|||
<item name="colorAccent">@color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Translucent" parent="AppTheme">
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
</style>
|
||||
|
||||
<!-- from https://stackoverflow.com/a/46286184 -->
|
||||
<style name="BottomSheetDialog" parent="Theme.MaterialComponents.DayNight.BottomSheetDialog">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue