diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 84e544a..eb947aa 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -122,6 +122,14 @@
+
+
diff --git a/app/src/main/java/io/timelimit/android/integration/platform/PlatformIntegration.kt b/app/src/main/java/io/timelimit/android/integration/platform/PlatformIntegration.kt
index 6039007..07b5ca3 100644
--- a/app/src/main/java/io/timelimit/android/integration/platform/PlatformIntegration.kt
+++ b/app/src/main/java/io/timelimit/android/integration/platform/PlatformIntegration.kt
@@ -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(
diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundActionService.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundActionService.kt
index b0e0bf9..925082b 100644
--- a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundActionService.kt
+++ b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundActionService.kt
@@ -1,5 +1,5 @@
/*
- * TimeLimit Copyright 2019 - 2022 Jonas Lochmann
+ * TimeLimit Copyright 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 {
diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt
index 370c5db..43c83bc 100644
--- a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt
+++ b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt
@@ -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)
diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/Notification.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/Notification.kt
index 761b754..44d1aea 100644
--- a/app/src/main/java/io/timelimit/android/integration/platform/android/Notification.kt
+++ b/app/src/main/java/io/timelimit/android/integration/platform/android/Notification.kt
@@ -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
diff --git a/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt b/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt
index e80a192..76727a1 100644
--- a/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt
+++ b/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt
@@ -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)
}
diff --git a/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseMainFragment.kt b/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseMainFragment.kt
index b4c7452..dc4bfd2 100644
--- a/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseMainFragment.kt
+++ b/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseMainFragment.kt
@@ -1,5 +1,5 @@
/*
- * Open TimeLimit Copyright 2019 - 2022 Jonas Lochmann
+ * Open TimeLimit Copyright 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
diff --git a/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionActivity.kt b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionActivity.kt
new file mode 100644
index 0000000..766e5b2
--- /dev/null
+++ b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionActivity.kt
@@ -0,0 +1,34 @@
+/*
+ * TimeLimit Copyright 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 .
+ */
+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()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseExceptionDialogFragment.kt b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionDialogFragment.kt
similarity index 68%
rename from app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseExceptionDialogFragment.kt
rename to app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionDialogFragment.kt
index 7e8965f..e7fde69 100644
--- a/app/src/main/java/io/timelimit/android/ui/diagnose/DiagnoseExceptionDialogFragment.kt
+++ b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionDialogFragment.kt
@@ -1,5 +1,5 @@
/*
- * TimeLimit Copyright 2019 Jonas Lochmann
+ * TimeLimit Copyright 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 .
*/
-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)
}
\ No newline at end of file
diff --git a/app/src/main/java/io/timelimit/android/ui/diagnose/exception/ExceptionUtil.kt b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/ExceptionUtil.kt
new file mode 100644
index 0000000..14a2c67
--- /dev/null
+++ b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/ExceptionUtil.kt
@@ -0,0 +1,45 @@
+/*
+ * TimeLimit Copyright 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 .
+ */
+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()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index b375ce2..aeeae39 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -123,6 +123,12 @@
Sperrbildschirm wird geöffnet
Berechtigung fehlt
nehme an, dass alles verwendet wird
+ 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.
+
Apps wurden vorübergehend erlaubt
Zum Rückgängig machen hier tippen oder Bildschirm ausschalten
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f8ff76d..1c523a9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -154,6 +154,12 @@
Opening lock screen
Missing permission
assuming that everything is used
+ 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.
+
Apps are temporarily allowed
Tap here or turn screen off to undo
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 9f33201..87de540 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,5 +1,5 @@