diff --git a/app/build.gradle b/app/build.gradle index 6442b6a..566f68b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,8 +30,8 @@ android { applicationId "io.timelimit.android" minSdkVersion 26 targetSdkVersion 34 - versionCode 217 - versionName "7.0.1" + versionCode 218 + versionName "7.1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" kapt { arguments { @@ -50,6 +50,7 @@ android { buildFeatures { compose true viewBinding true + buildConfig true } flavorDimensions 'api', 'channel', 'server' @@ -155,7 +156,7 @@ android { } composeOptions { - kotlinCompilerExtensionVersion = "1.5.5" + kotlinCompilerExtensionVersion = "1.5.7" } } @@ -167,21 +168,21 @@ dependencies { def nav_version = "2.5.3" def room_version = "2.6.1" def work_version = '2.9.0' - def paging_version = "3.3.0" + def paging_version = "3.3.1" implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.20" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.21" implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.core:core:1.13.1' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation "com.google.android.material:material:1.12.0" implementation 'androidx.compose.material:material:1.6.8' - implementation 'androidx.activity:activity-compose:1.9.0' + implementation 'androidx.activity:activity-compose:1.9.1' implementation "com.google.accompanist:accompanist-flowlayout:0.30.0" implementation 'androidx.compose.material:material-icons-extended:1.6.8' debugImplementation "androidx.compose.ui:ui-tooling:1.6.8" - implementation 'androidx.fragment:fragment-ktx:1.8.1' + implementation 'androidx.fragment:fragment-ktx:1.8.2' implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui:$nav_version" @@ -198,7 +199,7 @@ dependencies { implementation "androidx.work:work-runtime-ktx:$work_version" // androidTestImplementation "android.arch.work:work-testing:$work_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' testImplementation 'junit:junit:4.13.2' @@ -215,7 +216,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp-tls:4.9.3' implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3' - googleApiImplementation "com.android.billingclient:billing-ktx:6.2.1" + googleApiImplementation "com.android.billingclient:billing-ktx:7.0.0" implementation('io.socket:socket.io-client:2.0.0') { exclude group: 'org.json', module: 'json' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8528919..7bb37a4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ 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 efc5251..da3327f 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 @@ -219,7 +219,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 df56620..7296da5 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 @@ -29,6 +29,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 import io.timelimit.android.ui.notification.NotificationAreaSync class BackgroundActionService: Service() { @@ -44,7 +45,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) @@ -57,14 +58,24 @@ class BackgroundActionService: Service() { .putExtra(EXTRA_NOTIFICATION_TYPE, type) .putExtra(EXTRA_NOTIFICATION_ID, id) - 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 getSyncNotificationsPendingIntent(context: Context) = PendingIntent.getService( + 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 + ) + + fun getSyncNotificationsPendingIntent(context: Context): PendingIntent = PendingIntent.getService( context, PendingIntentIds.SYNC_NOTIFICATIONS, Intent(context, BackgroundActionService::class.java) 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 f9d5188..7222758 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 @@ -86,7 +86,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 f979373..42f2e67 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 @@ -254,6 +254,7 @@ object PendingIntentIds { const val OPEN_UPDATER = 6 const val U2F_NFC_DISCOVERY = 7 const val U2F_USB_RESPONSE = 8 + const val OPEN_MAIN_APP_WITH_ERROR = 9 val DYNAMIC_NOTIFICATION_RANGE = 100..10000 val PENDING_INTENT_FLAGS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 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 e8e07ed..9659eee 100644 --- a/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt +++ b/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt @@ -904,7 +904,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/exception/DiagnoseExceptionActivity.kt b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionActivity.kt new file mode 100644 index 0000000..f44fa9a --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionActivity.kt @@ -0,0 +1,36 @@ +/* + * 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.activity.compose.setContent +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) setContent { + DiagnoseExceptionDialog( + message = ExceptionUtil.formatInterpreted(this, ex), + close = { finish() } + ) + } else finish() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionDialogFragment.kt b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionDialogFragment.kt index 8fbbe26..de92e59 100644 --- a/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionDialogFragment.kt +++ b/app/src/main/java/io/timelimit/android/ui/diagnose/exception/DiagnoseExceptionDialogFragment.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2023 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 @@ -37,7 +37,7 @@ class DiagnoseExceptionDialogFragment: DialogFragment() { } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val message = ExceptionUtil.format(requireArguments().getSerializable(EXCEPTION) as Exception) + val message = ExceptionUtil.formatInterpreted(requireContext(), requireArguments().getSerializable(EXCEPTION) as Exception) return AlertDialog.Builder(requireContext(), theme) .setMessage(message) 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 index 1c59581..14a2c67 100644 --- 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 @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2023 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 @@ -15,11 +15,26 @@ */ 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 format(tr: Throwable): String = StringWriter().let { sw -> + 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() diff --git a/app/src/main/java/io/timelimit/android/ui/model/account/AccountDeletion.kt b/app/src/main/java/io/timelimit/android/ui/model/account/AccountDeletion.kt index 56a7cb3..6c823b7 100644 --- a/app/src/main/java/io/timelimit/android/ui/model/account/AccountDeletion.kt +++ b/app/src/main/java/io/timelimit/android/ui/model/account/AccountDeletion.kt @@ -181,7 +181,7 @@ object AccountDeletion { ) if (result == SnackbarResult.ActionPerformed) updateState { - it.copy(errorDialog = ExceptionUtil.format(ex)) + it.copy(errorDialog = ExceptionUtil.formatInterpreted(logic.context, ex)) } } } finally { diff --git a/app/src/main/java/io/timelimit/android/ui/model/mailauthentication/MailAuthentication.kt b/app/src/main/java/io/timelimit/android/ui/model/mailauthentication/MailAuthentication.kt index 5309685..c6ba0d5 100644 --- a/app/src/main/java/io/timelimit/android/ui/model/mailauthentication/MailAuthentication.kt +++ b/app/src/main/java/io/timelimit/android/ui/model/mailauthentication/MailAuthentication.kt @@ -160,7 +160,7 @@ object MailAuthentication { ) if (result == SnackbarResult.ActionPerformed) { - val message = ExceptionUtil.format(ex) + val message = ExceptionUtil.formatInterpreted(logic.context, ex) updateState { it.withError(error = ErrorDialog.ExceptionDetails(message)) } } diff --git a/app/src/main/java/io/timelimit/android/ui/model/managedevice/DeviceOwnerHandling.kt b/app/src/main/java/io/timelimit/android/ui/model/managedevice/DeviceOwnerHandling.kt index d002c03..6c024d3 100644 --- a/app/src/main/java/io/timelimit/android/ui/model/managedevice/DeviceOwnerHandling.kt +++ b/app/src/main/java/io/timelimit/android/ui/model/managedevice/DeviceOwnerHandling.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2023 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 @@ -181,7 +181,7 @@ object DeviceOwnerHandling { ) if (result == SnackbarResult.ActionPerformed) updateState { - it.copy(dialog = OwnerState.ErrorDialog(ExceptionUtil.format(ex))) + it.copy(dialog = OwnerState.ErrorDialog(ExceptionUtil.formatInterpreted(logic.context, ex))) } } } diff --git a/app/src/main/java/io/timelimit/android/ui/model/setup/SetupParentHandling.kt b/app/src/main/java/io/timelimit/android/ui/model/setup/SetupParentHandling.kt index 9c5cced..b29c7cc 100644 --- a/app/src/main/java/io/timelimit/android/ui/model/setup/SetupParentHandling.kt +++ b/app/src/main/java/io/timelimit/android/ui/model/setup/SetupParentHandling.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2023 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 @@ -363,7 +363,7 @@ object SetupParentHandling { ) if (result == SnackbarResult.ActionPerformed) updateState { - it.copy(error = ExceptionUtil.format(ex)) + it.copy(error = ExceptionUtil.formatInterpreted(logic.context, ex)) } } } finally { diff --git a/app/src/main/play/de-DE/whatsnew b/app/src/main/play/de-DE/whatsnew index 335524b..afeb2da 100644 --- a/app/src/main/play/de-DE/whatsnew +++ b/app/src/main/play/de-DE/whatsnew @@ -1,2 +1,2 @@ +- verbesserter Umgang mit Fehlern im Hintergrund - enthaltene Komponenten aktualisiert -- Probleme bei der Anmeldung behoben diff --git a/app/src/main/play/en-US/whatsnew b/app/src/main/play/en-US/whatsnew index 4e85af0..4ff2027 100644 --- a/app/src/main/play/en-US/whatsnew +++ b/app/src/main/play/en-US/whatsnew @@ -1,2 +1,2 @@ +- improved handling of errors in the background - update contained software -- fix issues during login diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 881f9d4..cf95126 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -197,6 +197,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 79a5e6b..880d308 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -240,6 +240,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 209ea0f..3e45a3a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -32,6 +32,10 @@ @color/white + +