Compare commits

...

6 commits

Author SHA1 Message Date
Jonas Lochmann
e01cf09f51
Release 7.1.0 2024-07-29 02:00:00 +02:00
Jonas Lochmann
6e5bee8966
Update billing library 2024-07-29 02:00:00 +02:00
Jonas Lochmann
5544883cc3
Migrate the buildfeatures.buildconfig flag 2024-07-29 02:00:00 +02:00
Jonas Lochmann
1d9a1f1071
Improve background loop exception presentation 2024-07-29 02:00:00 +02:00
Jonas Lochmann
ac889c142e
Update dependencies 2024-07-29 02:00:00 +02:00
Jonas Lochmann
eb680a4f42
Update buildtools 2024-07-29 02:00:00 +02:00
21 changed files with 129 additions and 35 deletions

View file

@ -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'

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
TimeLimit Copyright <C> 2019 - 2023 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
the Free Software Foundation version 3 of the License.
@ -124,6 +124,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">

View file

@ -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(

View file

@ -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
@ -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)

View file

@ -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)

View file

@ -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) {

View file

@ -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)
}

View file

@ -0,0 +1,36 @@
/*
* 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.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()
}
}

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 - 2023 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
@ -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)

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 - 2023 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
@ -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()

View file

@ -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 {

View file

@ -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)) }
}

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 - 2023 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
@ -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)))
}
}
}

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 - 2023 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
@ -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 {

View file

@ -1,2 +1,2 @@
- verbesserter Umgang mit Fehlern im Hintergrund
- enthaltene Komponenten aktualisiert
- Probleme bei der Anmeldung behoben

View file

@ -1,2 +1,2 @@
- improved handling of errors in the background
- update contained software
- fix issues during login

View file

@ -197,6 +197,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>

View file

@ -240,6 +240,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>

View file

@ -32,6 +32,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>

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 - 2023 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
@ -15,10 +15,10 @@
*/
plugins {
id 'com.android.application' version '8.5.0' apply false
id 'com.android.library' version '8.5.0' apply false
id 'org.jetbrains.kotlin.android' version "1.9.20" apply false
id 'com.google.devtools.ksp' version '1.8.21-1.0.11' apply false
id 'com.android.application' version '8.5.1' apply false
id 'com.android.library' version '8.5.1' apply false
id 'org.jetbrains.kotlin.android' version "1.9.21" apply false
id 'com.google.devtools.ksp' version '1.9.21-1.0.16' apply false
id 'androidx.navigation.safeargs' version '2.6.0' apply false
id 'com.squareup.wire' version '4.4.3' apply false
}

View file

@ -7,7 +7,6 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.useAndroidX=true
android.defaults.buildfeatures.buildconfig=true
org.gradle.jvmargs=-Xmx4096m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit