mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Adjust for Android U
This commit is contained in:
parent
37b3ea08e9
commit
43db6f4d2e
14 changed files with 74 additions and 18 deletions
|
@ -27,7 +27,7 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "io.timelimit.android"
|
applicationId "io.timelimit.android"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 33
|
targetSdkVersion 34
|
||||||
versionCode 205
|
versionCode 205
|
||||||
versionName "6.14.2"
|
versionName "6.14.2"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
tools:ignore="ProtectedPermissions" />
|
tools:ignore="ProtectedPermissions" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
|
||||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
<!-- for the categories which are limited to a wifi network -->
|
<!-- for the categories which are limited to a wifi network -->
|
||||||
|
@ -136,6 +137,7 @@
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
|
android:foregroundServiceType="systemExempted"
|
||||||
android:name=".integration.platform.android.BackgroundService"
|
android:name=".integration.platform.android.BackgroundService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
|
|
@ -361,7 +361,13 @@ data class Device(
|
||||||
(currentProtectionLevel != ProtectionLevel.DeviceOwner)
|
(currentProtectionLevel != ProtectionLevel.DeviceOwner)
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
val isImportant = hasAnyManipulation || missingPermissionAtQOrLater || didReportUninstall
|
val missingDeviceAdminPermission =
|
||||||
|
platformType == DevicePlatform.ANDROID &&
|
||||||
|
platformLevel >= 2 &&
|
||||||
|
currentProtectionLevel == ProtectionLevel.None
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
val isImportant = hasAnyManipulation || missingPermissionAtQOrLater || missingDeviceAdminPermission || didReportUninstall
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class NetworkTime {
|
enum class NetworkTime {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,12 +18,17 @@ package io.timelimit.android.integration.platform.android
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
|
import android.app.admin.DevicePolicyManager
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Build.VERSION
|
||||||
|
import android.os.Build.VERSION_CODES
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.getSystemService
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
import io.timelimit.android.integration.platform.AppStatusMessage
|
import io.timelimit.android.integration.platform.AppStatusMessage
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
|
@ -35,9 +40,10 @@ class BackgroundService: Service() {
|
||||||
fun setStatusMessage(status: AppStatusMessage?, context: Context) {
|
fun setStatusMessage(status: AppStatusMessage?, context: Context) {
|
||||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
val intent = Intent(context, BackgroundService::class.java)
|
val intent = Intent(context, BackgroundService::class.java)
|
||||||
|
val isRestricted = isBackgroundActivityRestricted(context)
|
||||||
|
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
if (isBackgroundActivityRestricted(context)) {
|
if (isRestricted) {
|
||||||
val notification = buildNotification(status, context)
|
val notification = buildNotification(status, context)
|
||||||
|
|
||||||
notificationManager.notify(NotificationIds.APP_STATUS, notification)
|
notificationManager.notify(NotificationIds.APP_STATUS, notification)
|
||||||
|
@ -50,7 +56,7 @@ class BackgroundService: Service() {
|
||||||
} else {
|
} else {
|
||||||
context.stopService(intent)
|
context.stopService(intent)
|
||||||
|
|
||||||
if (isBackgroundActivityRestricted(context)) {
|
if (isRestricted) {
|
||||||
notificationManager.cancel(NotificationIds.APP_STATUS)
|
notificationManager.cancel(NotificationIds.APP_STATUS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,13 +65,21 @@ class BackgroundService: Service() {
|
||||||
fun isBackgroundActivityRestricted(context: Context): Boolean {
|
fun isBackgroundActivityRestricted(context: Context): Boolean {
|
||||||
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (VERSION.SDK_INT >= VERSION_CODES.P) {
|
||||||
return activityManager.isBackgroundRestricted
|
if (activityManager.isBackgroundRestricted) {
|
||||||
} else {
|
return true
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
if (!context.getSystemService<DevicePolicyManager>()!!.isAdminActive(ComponentName(context, AdminReceiver::class.java))) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildNotification(appStatusMessage: AppStatusMessage, context: Context) = NotificationCompat.Builder(context, NotificationChannels.APP_STATUS)
|
private fun buildNotification(appStatusMessage: AppStatusMessage, context: Context) = NotificationCompat.Builder(context, NotificationChannels.APP_STATUS)
|
||||||
.setSmallIcon(R.drawable.ic_stat_timelapse)
|
.setSmallIcon(R.drawable.ic_stat_timelapse)
|
||||||
.setContentTitle(appStatusMessage.title)
|
.setContentTitle(appStatusMessage.title)
|
||||||
|
|
|
@ -37,6 +37,7 @@ import io.timelimit.android.extensions.MinuteOfDay
|
||||||
import io.timelimit.android.extensions.nextBlockedMinuteOfWeek
|
import io.timelimit.android.extensions.nextBlockedMinuteOfWeek
|
||||||
import io.timelimit.android.integration.platform.*
|
import io.timelimit.android.integration.platform.*
|
||||||
import io.timelimit.android.integration.platform.android.AccessibilityService
|
import io.timelimit.android.integration.platform.android.AccessibilityService
|
||||||
|
import io.timelimit.android.integration.platform.android.BackgroundService
|
||||||
import io.timelimit.android.livedata.*
|
import io.timelimit.android.livedata.*
|
||||||
import io.timelimit.android.logic.blockingreason.AppBaseHandling
|
import io.timelimit.android.logic.blockingreason.AppBaseHandling
|
||||||
import io.timelimit.android.logic.blockingreason.CategoryHandlingCache
|
import io.timelimit.android.logic.blockingreason.CategoryHandlingCache
|
||||||
|
@ -178,6 +179,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
|
|
||||||
private suspend fun backgroundServiceLoop() {
|
private suspend fun backgroundServiceLoop() {
|
||||||
val realTime = RealTime.newInstance()
|
val realTime = RealTime.newInstance()
|
||||||
|
var skipBackgroundRestrictionChecks = 0
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
val backgroundServiceInterval = when (slowMainLoop) {
|
val backgroundServiceInterval = when (slowMainLoop) {
|
||||||
|
@ -244,6 +246,24 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
|
|
||||||
// loop logic
|
// loop logic
|
||||||
try {
|
try {
|
||||||
|
if (skipBackgroundRestrictionChecks <= 0) {
|
||||||
|
if (BackgroundService.isBackgroundActivityRestricted(appLogic.context)) {
|
||||||
|
commitUsedTimeUpdaters()
|
||||||
|
undisturbedCategoryUsageCounter.reset()
|
||||||
|
|
||||||
|
appLogic.platformIntegration.setShowBlockingOverlay(false)
|
||||||
|
appLogic.platformIntegration.setAppStatusMessage(
|
||||||
|
AppStatusMessage(
|
||||||
|
appLogic.context.getString(R.string.background_logic_error),
|
||||||
|
appLogic.context.getString(R.string.background_logic_error_permission)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
appLogic.timeApi.sleep(BACKGROUND_SERVICE_INTERVAL_LONG)
|
||||||
|
continue
|
||||||
|
} else skipBackgroundRestrictionChecks = 128
|
||||||
|
} else skipBackgroundRestrictionChecks--
|
||||||
|
|
||||||
// get the current time
|
// get the current time
|
||||||
appLogic.realTimeLogic.getRealTime(realTime)
|
appLogic.realTimeLogic.getRealTime(realTime)
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ class NFCU2FManager (val parent: U2fManager, context: Context) {
|
||||||
private val nfcReceiverIntent = PendingIntent.getBroadcast(
|
private val nfcReceiverIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
PendingIntentIds.U2F_NFC_DISCOVERY,
|
PendingIntentIds.U2F_NFC_DISCOVERY,
|
||||||
Intent(nfcReceiverAction),
|
Intent(nfcReceiverAction).setPackage(context.packageName),
|
||||||
PendingIntentIds.PENDING_INTENT_FLAGS_ALLOW_MUTATION
|
PendingIntentIds.PENDING_INTENT_FLAGS_ALLOW_MUTATION
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ class UsbU2FManager (val parent: U2fManager, context: Context) {
|
||||||
private val permissionResponseIntent = PendingIntent.getBroadcast(
|
private val permissionResponseIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
PendingIntentIds.U2F_USB_RESPONSE,
|
PendingIntentIds.U2F_USB_RESPONSE,
|
||||||
Intent(permissionResponseAction),
|
Intent(permissionResponseAction).setPackage(context.packageName),
|
||||||
PendingIntentIds.PENDING_INTENT_FLAGS_ALLOW_MUTATION
|
PendingIntentIds.PENDING_INTENT_FLAGS_ALLOW_MUTATION
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,8 @@ fun PermissionGoals(status: PermissionScreenContent.Status) {
|
||||||
PermissionGoal(
|
PermissionGoal(
|
||||||
stringResource(R.string.manage_device_permission_goal_limit_title),
|
stringResource(R.string.manage_device_permission_goal_limit_title),
|
||||||
status.usageStats != RuntimePermissionStatus.NotGranted &&
|
status.usageStats != RuntimePermissionStatus.NotGranted &&
|
||||||
(!status.isQOrLater || status.overlay == RuntimePermissionStatus.Granted || status.accessibility),
|
(!status.isQOrLater || status.overlay == RuntimePermissionStatus.Granted || status.accessibility) &&
|
||||||
|
(status.androidPlatformLevel < 2 || status.protectionLevel != ProtectionLevel.None)
|
||||||
) {
|
) {
|
||||||
if (status.usageStats != RuntimePermissionStatus.NotRequired) FlowRow {
|
if (status.usageStats != RuntimePermissionStatus.NotRequired) FlowRow {
|
||||||
Text(stringResource(R.string.manage_device_permission_goal_needs))
|
Text(stringResource(R.string.manage_device_permission_goal_needs))
|
||||||
|
@ -52,8 +53,13 @@ fun PermissionGoals(status: PermissionScreenContent.Status) {
|
||||||
PermissionIcon(SystemPermission.AccessibilityService, status.accessibility)
|
PermissionIcon(SystemPermission.AccessibilityService, status.accessibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.usageStats == RuntimePermissionStatus.NotRequired && !status.isQOrLater)
|
FlowRow {
|
||||||
Text(stringResource(R.string.manage_device_permission_goal_reached_by_old_android))
|
Text(stringResource(
|
||||||
|
if (status.androidPlatformLevel >= 2) R.string.manage_device_permission_goal_needs
|
||||||
|
else R.string.manage_device_permission_goal_eventually_needs_future
|
||||||
|
))
|
||||||
|
PermissionIcon(SystemPermission.DeviceAdmin, status.protectionLevel != ProtectionLevel.None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PermissionGoal(
|
PermissionGoal(
|
||||||
|
|
|
@ -32,7 +32,8 @@ data class PermissionScreenContent(
|
||||||
val usageStats: RuntimePermissionStatus,
|
val usageStats: RuntimePermissionStatus,
|
||||||
val overlay: RuntimePermissionStatus,
|
val overlay: RuntimePermissionStatus,
|
||||||
val accessibility: Boolean,
|
val accessibility: Boolean,
|
||||||
val isQOrLater: Boolean
|
val isQOrLater: Boolean,
|
||||||
|
val androidPlatformLevel: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Dialog(
|
data class Dialog(
|
||||||
|
|
|
@ -412,7 +412,9 @@ object OverviewHandling {
|
||||||
data class UserList(val list: List<UserItem>, val canAdd: Boolean, val canShowMore: Boolean)
|
data class UserList(val list: List<UserItem>, val canAdd: Boolean, val canShowMore: Boolean)
|
||||||
data class DeviceItem(val device: Device, val userName: String?, val userType: UserType?, val isCurrentDevice: Boolean, val isConnected: Boolean) {
|
data class DeviceItem(val device: Device, val userName: String?, val userType: UserType?, val isCurrentDevice: Boolean, val isConnected: Boolean) {
|
||||||
val isMissingRequiredPermission = userType == UserType.Child && (
|
val isMissingRequiredPermission = userType == UserType.Child && (
|
||||||
device.currentUsageStatsPermission == RuntimePermissionStatus.NotGranted || device.missingPermissionAtQOrLater)
|
device.currentUsageStatsPermission == RuntimePermissionStatus.NotGranted ||
|
||||||
|
device.missingPermissionAtQOrLater ||
|
||||||
|
device.missingDeviceAdminPermission)
|
||||||
}
|
}
|
||||||
data class DeviceList(val list: List<DeviceItem>, val canAdd: Boolean, val canShowMore: OverviewState.DeviceList?)
|
data class DeviceList(val list: List<DeviceItem>, val canAdd: Boolean, val canShowMore: OverviewState.DeviceList?)
|
||||||
}
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
package io.timelimit.android.ui.model.managedevice
|
package io.timelimit.android.ui.model.managedevice
|
||||||
|
|
||||||
import io.timelimit.android.data.model.Device
|
import io.timelimit.android.data.model.Device
|
||||||
|
import io.timelimit.android.data.model.DevicePlatform
|
||||||
import io.timelimit.android.logic.AppLogic
|
import io.timelimit.android.logic.AppLogic
|
||||||
import io.timelimit.android.ui.manage.device.manage.permission.PermissionScreenContent
|
import io.timelimit.android.ui.manage.device.manage.permission.PermissionScreenContent
|
||||||
import io.timelimit.android.ui.model.ActivityCommand
|
import io.timelimit.android.ui.model.ActivityCommand
|
||||||
|
@ -82,7 +83,8 @@ object ManageDevicePermissions {
|
||||||
usageStats = device.currentUsageStatsPermission,
|
usageStats = device.currentUsageStatsPermission,
|
||||||
overlay = device.currentOverlayPermission,
|
overlay = device.currentOverlayPermission,
|
||||||
accessibility = device.accessibilityServiceEnabled,
|
accessibility = device.accessibilityServiceEnabled,
|
||||||
isQOrLater = device.qOrLater
|
isQOrLater = device.qOrLater,
|
||||||
|
androidPlatformLevel = if (device.platformType == DevicePlatform.ANDROID) device.platformLevel else 0
|
||||||
)
|
)
|
||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
}
|
}
|
|
@ -108,7 +108,8 @@ object SetupLocalModePermissions {
|
||||||
usageStats = platformIntegration.getForegroundAppPermissionStatus(),
|
usageStats = platformIntegration.getForegroundAppPermissionStatus(),
|
||||||
overlay = platformIntegration.getDrawOverOtherAppsPermissionStatus(true),
|
overlay = platformIntegration.getDrawOverOtherAppsPermissionStatus(true),
|
||||||
accessibility = platformIntegration.isAccessibilityServiceEnabled(),
|
accessibility = platformIntegration.isAccessibilityServiceEnabled(),
|
||||||
isQOrLater = AndroidVersion.qOrLater
|
isQOrLater = AndroidVersion.qOrLater,
|
||||||
|
androidPlatformLevel = AndroidVersion.platformLevel
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1033,6 +1033,7 @@
|
||||||
<string name="manage_device_permission_goal_manipulation_protection_unavailable">in der aktuellen Umgebung nicht verfügbar</string>
|
<string name="manage_device_permission_goal_manipulation_protection_unavailable">in der aktuellen Umgebung nicht verfügbar</string>
|
||||||
<string name="manage_device_permission_goal_manipulation_protection_check_remotely">öffnen Sie diese Ansicht am entsprechenden Gerät</string>
|
<string name="manage_device_permission_goal_manipulation_protection_check_remotely">öffnen Sie diese Ansicht am entsprechenden Gerät</string>
|
||||||
<string name="manage_device_permission_goal_eventually_needs">benötigt evtl.</string>
|
<string name="manage_device_permission_goal_eventually_needs">benötigt evtl.</string>
|
||||||
|
<string name="manage_device_permission_goal_eventually_needs_future">benötigt zukünftig</string>
|
||||||
<string name="manage_device_permission_goal_needs">benötigt</string>
|
<string name="manage_device_permission_goal_needs">benötigt</string>
|
||||||
<string name="manage_device_permission_goal_or">oder</string>
|
<string name="manage_device_permission_goal_or">oder</string>
|
||||||
<string name="manage_device_permission_goal_reached_by_old_android">benötigt wegen alter Android-Version Nichts weiter</string>
|
<string name="manage_device_permission_goal_reached_by_old_android">benötigt wegen alter Android-Version Nichts weiter</string>
|
||||||
|
|
|
@ -1082,6 +1082,7 @@
|
||||||
<string name="manage_device_permission_goal_manipulation_protection_unavailable">unavailable in the current environment</string>
|
<string name="manage_device_permission_goal_manipulation_protection_unavailable">unavailable in the current environment</string>
|
||||||
<string name="manage_device_permission_goal_manipulation_protection_check_remotely">open this screen at the device</string>
|
<string name="manage_device_permission_goal_manipulation_protection_check_remotely">open this screen at the device</string>
|
||||||
<string name="manage_device_permission_goal_eventually_needs">eventually needs</string>
|
<string name="manage_device_permission_goal_eventually_needs">eventually needs</string>
|
||||||
|
<string name="manage_device_permission_goal_eventually_needs_future">needs in the future</string>
|
||||||
<string name="manage_device_permission_goal_needs">needs</string>
|
<string name="manage_device_permission_goal_needs">needs</string>
|
||||||
<string name="manage_device_permission_goal_or">or</string>
|
<string name="manage_device_permission_goal_or">or</string>
|
||||||
<string name="manage_device_permission_goal_reached_by_old_android">needs nothing due to old Android version</string>
|
<string name="manage_device_permission_goal_reached_by_old_android">needs nothing due to old Android version</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue