mirror of
https://codeberg.org/timelimit/opentimelimit-android.git
synced 2025-10-05 10:49:29 +02:00
Allow interpreting a device reboot as manipulation
This commit is contained in:
parent
c37b888b56
commit
212aaafd78
20 changed files with 726 additions and 21 deletions
455
app/schemas/io.timelimit.android.data.RoomDatabase/4.json
Normal file
455
app/schemas/io.timelimit.android.data.RoomDatabase/4.json
Normal file
|
@ -0,0 +1,455 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 4,
|
||||||
|
"identityHash": "193ef7d6dcc9fbda20e13c8d49c5295f",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "user",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `password` TEXT NOT NULL, `type` TEXT NOT NULL, `timezone` TEXT NOT NULL, `disable_limits_until` INTEGER NOT NULL, `category_for_not_assigned_apps` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "password",
|
||||||
|
"columnName": "password",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timeZone",
|
||||||
|
"columnName": "timezone",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "disableLimitsUntil",
|
||||||
|
"columnName": "disable_limits_until",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "categoryForNotAssignedApps",
|
||||||
|
"columnName": "category_for_not_assigned_apps",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "device",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `model` TEXT NOT NULL, `added_at` INTEGER NOT NULL, `current_user_id` TEXT NOT NULL, `current_protection_level` TEXT NOT NULL, `highest_permission_level` TEXT NOT NULL, `current_usage_stats_permission` TEXT NOT NULL, `highest_usage_stats_permission` TEXT NOT NULL, `current_notification_access_permission` TEXT NOT NULL, `highest_notification_access_permission` TEXT NOT NULL, `current_app_version` INTEGER NOT NULL, `highest_app_version` INTEGER NOT NULL, `tried_disabling_device_admin` INTEGER NOT NULL, `did_reboot` INTEGER NOT NULL, `had_manipulation` INTEGER NOT NULL, `consider_reboot_manipulation` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "model",
|
||||||
|
"columnName": "model",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "addedAt",
|
||||||
|
"columnName": "added_at",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "currentUserId",
|
||||||
|
"columnName": "current_user_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "currentProtectionLevel",
|
||||||
|
"columnName": "current_protection_level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "highestProtectionLevel",
|
||||||
|
"columnName": "highest_permission_level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "currentUsageStatsPermission",
|
||||||
|
"columnName": "current_usage_stats_permission",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "highestUsageStatsPermission",
|
||||||
|
"columnName": "highest_usage_stats_permission",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "currentNotificationAccessPermission",
|
||||||
|
"columnName": "current_notification_access_permission",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "highestNotificationAccessPermission",
|
||||||
|
"columnName": "highest_notification_access_permission",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "currentAppVersion",
|
||||||
|
"columnName": "current_app_version",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "highestAppVersion",
|
||||||
|
"columnName": "highest_app_version",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "manipulationTriedDisablingDeviceAdmin",
|
||||||
|
"columnName": "tried_disabling_device_admin",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "manipulationDidReboot",
|
||||||
|
"columnName": "did_reboot",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hadManipulation",
|
||||||
|
"columnName": "had_manipulation",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "considerRebootManipulation",
|
||||||
|
"columnName": "consider_reboot_manipulation",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "app",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `title` TEXT NOT NULL, `launchable` INTEGER NOT NULL, `recommendation` TEXT NOT NULL, PRIMARY KEY(`package_name`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "packageName",
|
||||||
|
"columnName": "package_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isLaunchable",
|
||||||
|
"columnName": "launchable",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "recommendation",
|
||||||
|
"columnName": "recommendation",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"package_name"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_app_package_name",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"package_name"
|
||||||
|
],
|
||||||
|
"createSql": "CREATE INDEX `index_app_package_name` ON `${TABLE_NAME}` (`package_name`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "category_app",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`category_id` TEXT NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`category_id`, `package_name`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "categoryId",
|
||||||
|
"columnName": "category_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "packageName",
|
||||||
|
"columnName": "package_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"category_id",
|
||||||
|
"package_name"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_category_app_category_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"category_id"
|
||||||
|
],
|
||||||
|
"createSql": "CREATE INDEX `index_category_app_category_id` ON `${TABLE_NAME}` (`category_id`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_category_app_package_name",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"package_name"
|
||||||
|
],
|
||||||
|
"createSql": "CREATE INDEX `index_category_app_package_name` ON `${TABLE_NAME}` (`package_name`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "category",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `child_id` TEXT NOT NULL, `title` TEXT NOT NULL, `blocked_times` TEXT NOT NULL, `extra_time` INTEGER NOT NULL, `temporarily_blocked` INTEGER NOT NULL, `parent_category_id` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "childId",
|
||||||
|
"columnName": "child_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockedMinutesInWeek",
|
||||||
|
"columnName": "blocked_times",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "extraTimeInMillis",
|
||||||
|
"columnName": "extra_time",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "temporarilyBlocked",
|
||||||
|
"columnName": "temporarily_blocked",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parentCategoryId",
|
||||||
|
"columnName": "parent_category_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "used_time",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`day_of_epoch` INTEGER NOT NULL, `used_time` INTEGER NOT NULL, `category_id` TEXT NOT NULL, PRIMARY KEY(`category_id`, `day_of_epoch`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "dayOfEpoch",
|
||||||
|
"columnName": "day_of_epoch",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "usedMillis",
|
||||||
|
"columnName": "used_time",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "categoryId",
|
||||||
|
"columnName": "category_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"category_id",
|
||||||
|
"day_of_epoch"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "time_limit_rule",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `category_id` TEXT NOT NULL, `apply_to_extra_time_usage` INTEGER NOT NULL, `day_mask` INTEGER NOT NULL, `max_time` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "categoryId",
|
||||||
|
"columnName": "category_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "applyToExtraTimeUsage",
|
||||||
|
"columnName": "apply_to_extra_time_usage",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dayMask",
|
||||||
|
"columnName": "day_mask",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "maximumTimeInMillis",
|
||||||
|
"columnName": "max_time",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "config",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "key",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "value",
|
||||||
|
"columnName": "value",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "temporarily_allowed_app",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, PRIMARY KEY(`package_name`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "packageName",
|
||||||
|
"columnName": "package_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"package_name"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"193ef7d6dcc9fbda20e13c8d49c5295f\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,4 +15,11 @@ object DatabaseMigrations {
|
||||||
database.execSQL("ALTER TABLE `category` ADD COLUMN `parent_category_id` TEXT NOT NULL DEFAULT \"\"")
|
database.execSQL("ALTER TABLE `category` ADD COLUMN `parent_category_id` TEXT NOT NULL DEFAULT \"\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val MIGRATE_TO_V4 = object: Migration(3, 4) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE `device` ADD COLUMN `did_reboot` INTEGER NOT NULL DEFAULT 0")
|
||||||
|
database.execSQL("ALTER TABLE `device` ADD COLUMN `consider_reboot_manipulation` INTEGER NOT NULL DEFAULT 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -31,7 +31,7 @@ import io.timelimit.android.data.model.*
|
||||||
TimeLimitRule::class,
|
TimeLimitRule::class,
|
||||||
ConfigurationItem::class,
|
ConfigurationItem::class,
|
||||||
TemporarilyAllowedApp::class
|
TemporarilyAllowedApp::class
|
||||||
], version = 3)
|
], version = 4)
|
||||||
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
||||||
companion object {
|
companion object {
|
||||||
private val lock = Object()
|
private val lock = Object()
|
||||||
|
@ -68,7 +68,8 @@ abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration()
|
||||||
.addMigrations(
|
.addMigrations(
|
||||||
DatabaseMigrations.MIGRATE_TO_V2,
|
DatabaseMigrations.MIGRATE_TO_V2,
|
||||||
DatabaseMigrations.MIGRATE_TO_V3
|
DatabaseMigrations.MIGRATE_TO_V3,
|
||||||
|
DatabaseMigrations.MIGRATE_TO_V4
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,12 @@ data class Device(
|
||||||
val highestAppVersion: Int,
|
val highestAppVersion: Int,
|
||||||
@ColumnInfo(name = "tried_disabling_device_admin")
|
@ColumnInfo(name = "tried_disabling_device_admin")
|
||||||
val manipulationTriedDisablingDeviceAdmin: Boolean,
|
val manipulationTriedDisablingDeviceAdmin: Boolean,
|
||||||
|
@ColumnInfo(name = "did_reboot")
|
||||||
|
val manipulationDidReboot: Boolean,
|
||||||
@ColumnInfo(name = "had_manipulation")
|
@ColumnInfo(name = "had_manipulation")
|
||||||
val hadManipulation: Boolean
|
val hadManipulation: Boolean,
|
||||||
|
@ColumnInfo(name = "consider_reboot_manipulation")
|
||||||
|
val considerRebootManipulation: Boolean
|
||||||
): JsonSerializable {
|
): JsonSerializable {
|
||||||
companion object {
|
companion object {
|
||||||
private const val ID = "id"
|
private const val ID = "id"
|
||||||
|
@ -79,7 +83,9 @@ data class Device(
|
||||||
private const val CURRENT_APP_VERSION = "ac"
|
private const val CURRENT_APP_VERSION = "ac"
|
||||||
private const val HIGHEST_APP_VERSION = "am"
|
private const val HIGHEST_APP_VERSION = "am"
|
||||||
private const val TRIED_DISABLING_DEVICE_ADMIN = "tdda"
|
private const val TRIED_DISABLING_DEVICE_ADMIN = "tdda"
|
||||||
|
private const val MANIPULATION_DID_REBOOT = "mdr"
|
||||||
private const val HAD_MANIPULATION = "hm"
|
private const val HAD_MANIPULATION = "hm"
|
||||||
|
private const val CONSIDER_REBOOT_A_MANIPULATION = "cram"
|
||||||
|
|
||||||
fun parse(reader: JsonReader): Device {
|
fun parse(reader: JsonReader): Device {
|
||||||
var id: String? = null
|
var id: String? = null
|
||||||
|
@ -96,7 +102,9 @@ data class Device(
|
||||||
var currentAppVersion: Int? = null
|
var currentAppVersion: Int? = null
|
||||||
var highestAppVersion: Int? = null
|
var highestAppVersion: Int? = null
|
||||||
var manipulationTriedDisablingDeviceAdmin: Boolean? = null
|
var manipulationTriedDisablingDeviceAdmin: Boolean? = null
|
||||||
|
var manipulationDidReboot: Boolean = false
|
||||||
var hadManipulation: Boolean? = null
|
var hadManipulation: Boolean? = null
|
||||||
|
var considerRebootManipulation = false
|
||||||
|
|
||||||
reader.beginObject()
|
reader.beginObject()
|
||||||
|
|
||||||
|
@ -116,7 +124,9 @@ data class Device(
|
||||||
CURRENT_APP_VERSION -> currentAppVersion = reader.nextInt()
|
CURRENT_APP_VERSION -> currentAppVersion = reader.nextInt()
|
||||||
HIGHEST_APP_VERSION -> highestAppVersion = reader.nextInt()
|
HIGHEST_APP_VERSION -> highestAppVersion = reader.nextInt()
|
||||||
TRIED_DISABLING_DEVICE_ADMIN -> manipulationTriedDisablingDeviceAdmin = reader.nextBoolean()
|
TRIED_DISABLING_DEVICE_ADMIN -> manipulationTriedDisablingDeviceAdmin = reader.nextBoolean()
|
||||||
|
MANIPULATION_DID_REBOOT -> manipulationDidReboot = reader.nextBoolean()
|
||||||
HAD_MANIPULATION -> hadManipulation = reader.nextBoolean()
|
HAD_MANIPULATION -> hadManipulation = reader.nextBoolean()
|
||||||
|
CONSIDER_REBOOT_A_MANIPULATION -> considerRebootManipulation = reader.nextBoolean()
|
||||||
else -> reader.skipValue()
|
else -> reader.skipValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +148,9 @@ data class Device(
|
||||||
currentAppVersion = currentAppVersion!!,
|
currentAppVersion = currentAppVersion!!,
|
||||||
highestAppVersion = highestAppVersion!!,
|
highestAppVersion = highestAppVersion!!,
|
||||||
manipulationTriedDisablingDeviceAdmin = manipulationTriedDisablingDeviceAdmin!!,
|
manipulationTriedDisablingDeviceAdmin = manipulationTriedDisablingDeviceAdmin!!,
|
||||||
hadManipulation = hadManipulation!!
|
manipulationDidReboot = manipulationDidReboot,
|
||||||
|
hadManipulation = hadManipulation!!,
|
||||||
|
considerRebootManipulation = considerRebootManipulation
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +196,9 @@ data class Device(
|
||||||
writer.name(CURRENT_APP_VERSION).value(currentAppVersion)
|
writer.name(CURRENT_APP_VERSION).value(currentAppVersion)
|
||||||
writer.name(HIGHEST_APP_VERSION).value(highestAppVersion)
|
writer.name(HIGHEST_APP_VERSION).value(highestAppVersion)
|
||||||
writer.name(TRIED_DISABLING_DEVICE_ADMIN).value(manipulationTriedDisablingDeviceAdmin)
|
writer.name(TRIED_DISABLING_DEVICE_ADMIN).value(manipulationTriedDisablingDeviceAdmin)
|
||||||
|
writer.name(MANIPULATION_DID_REBOOT).value(manipulationDidReboot)
|
||||||
writer.name(HAD_MANIPULATION).value(hadManipulation)
|
writer.name(HAD_MANIPULATION).value(hadManipulation)
|
||||||
|
writer.name(CONSIDER_REBOOT_A_MANIPULATION).value(considerRebootManipulation)
|
||||||
|
|
||||||
writer.endObject()
|
writer.endObject()
|
||||||
}
|
}
|
||||||
|
@ -203,7 +217,8 @@ data class Device(
|
||||||
manipulationOfUsageStats ||
|
manipulationOfUsageStats ||
|
||||||
manipulationOfNotificationAccess ||
|
manipulationOfNotificationAccess ||
|
||||||
manipulationOfAppVersion ||
|
manipulationOfAppVersion ||
|
||||||
manipulationTriedDisablingDeviceAdmin
|
manipulationTriedDisablingDeviceAdmin ||
|
||||||
|
manipulationDidReboot
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
val hasAnyManipulation = hasActiveManipulationWarning || hadManipulation
|
val hasAnyManipulation = hasActiveManipulationWarning || hadManipulation
|
||||||
|
|
|
@ -24,7 +24,7 @@ class BootReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
|
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
|
||||||
// this starts the logic (if not yet done)
|
// this starts the logic (if not yet done)
|
||||||
DefaultAppLogic.with(context)
|
DefaultAppLogic.with(context).backgroundTaskLogic.reportDeviceReboot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,9 @@ class AppSetupLogic(private val appLogic: AppLogic) {
|
||||||
currentAppVersion = 0,
|
currentAppVersion = 0,
|
||||||
highestAppVersion = 0,
|
highestAppVersion = 0,
|
||||||
manipulationTriedDisablingDeviceAdmin = false,
|
manipulationTriedDisablingDeviceAdmin = false,
|
||||||
hadManipulation = false
|
manipulationDidReboot = false,
|
||||||
|
hadManipulation = false,
|
||||||
|
considerRebootManipulation = false
|
||||||
)
|
)
|
||||||
|
|
||||||
appLogic.database.device().addDeviceSync(device)
|
appLogic.database.device().addDeviceSync(device)
|
||||||
|
|
|
@ -402,10 +402,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
if (deviceEntry != null) {
|
if (deviceEntry != null) {
|
||||||
if (deviceEntry.currentAppVersion != currentAppVersion) {
|
if (deviceEntry.currentAppVersion != currentAppVersion) {
|
||||||
ApplyActionUtil.applyAppLogicAction(
|
ApplyActionUtil.applyAppLogicAction(
|
||||||
UpdateDeviceStatusAction(
|
UpdateDeviceStatusAction.empty.copy(
|
||||||
newProtectionLevel = null,
|
|
||||||
newUsageStatsPermissionStatus = null,
|
|
||||||
newNotificationAccessPermission = null,
|
|
||||||
newAppVersion = currentAppVersion
|
newAppVersion = currentAppVersion
|
||||||
),
|
),
|
||||||
appLogic
|
appLogic
|
||||||
|
@ -432,6 +429,21 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
|
|
||||||
private val syncDeviceStatusLock = Mutex()
|
private val syncDeviceStatusLock = Mutex()
|
||||||
|
|
||||||
|
fun reportDeviceReboot() {
|
||||||
|
runAsync {
|
||||||
|
val deviceEntry = appLogic.deviceEntry.waitForNullableValue()
|
||||||
|
|
||||||
|
if (deviceEntry?.considerRebootManipulation == true) {
|
||||||
|
ApplyActionUtil.applyAppLogicAction(
|
||||||
|
UpdateDeviceStatusAction.empty.copy(
|
||||||
|
didReboot = true
|
||||||
|
),
|
||||||
|
appLogic
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun syncDeviceStatus() {
|
private suspend fun syncDeviceStatus() {
|
||||||
syncDeviceStatusLock.withLock {
|
syncDeviceStatusLock.withLock {
|
||||||
val deviceEntry = appLogic.deviceEntry.waitForNullableValue()
|
val deviceEntry = appLogic.deviceEntry.waitForNullableValue()
|
||||||
|
@ -441,14 +453,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
val usageStatsPermission = appLogic.platformIntegration.getForegroundAppPermissionStatus()
|
val usageStatsPermission = appLogic.platformIntegration.getForegroundAppPermissionStatus()
|
||||||
val notificationAccess = appLogic.platformIntegration.getNotificationAccessPermissionStatus()
|
val notificationAccess = appLogic.platformIntegration.getNotificationAccessPermissionStatus()
|
||||||
|
|
||||||
val emptyChanges = UpdateDeviceStatusAction(
|
var changes = UpdateDeviceStatusAction.empty
|
||||||
newProtectionLevel = null,
|
|
||||||
newUsageStatsPermissionStatus = null,
|
|
||||||
newNotificationAccessPermission = null,
|
|
||||||
newAppVersion = null
|
|
||||||
)
|
|
||||||
|
|
||||||
var changes = emptyChanges
|
|
||||||
|
|
||||||
if (protectionLevel != deviceEntry.currentProtectionLevel) {
|
if (protectionLevel != deviceEntry.currentProtectionLevel) {
|
||||||
changes = changes.copy(
|
changes = changes.copy(
|
||||||
|
@ -472,7 +477,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes != emptyChanges) {
|
if (changes != UpdateDeviceStatusAction.empty) {
|
||||||
ApplyActionUtil.applyAppLogicAction(changes, appLogic)
|
ApplyActionUtil.applyAppLogicAction(changes, appLogic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,8 +155,19 @@ data class UpdateDeviceStatusAction(
|
||||||
val newProtectionLevel: ProtectionLevel?,
|
val newProtectionLevel: ProtectionLevel?,
|
||||||
val newUsageStatsPermissionStatus: RuntimePermissionStatus?,
|
val newUsageStatsPermissionStatus: RuntimePermissionStatus?,
|
||||||
val newNotificationAccessPermission: NewPermissionStatus?,
|
val newNotificationAccessPermission: NewPermissionStatus?,
|
||||||
val newAppVersion: Int?
|
val newAppVersion: Int?,
|
||||||
|
val didReboot: Boolean
|
||||||
): AppLogicAction() {
|
): AppLogicAction() {
|
||||||
|
companion object {
|
||||||
|
val empty = UpdateDeviceStatusAction(
|
||||||
|
newProtectionLevel = null,
|
||||||
|
newUsageStatsPermissionStatus = null,
|
||||||
|
newNotificationAccessPermission = null,
|
||||||
|
newAppVersion = null,
|
||||||
|
didReboot = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (newAppVersion != null && newAppVersion < 0) {
|
if (newAppVersion != null && newAppVersion < 0) {
|
||||||
throw IllegalArgumentException()
|
throw IllegalArgumentException()
|
||||||
|
@ -171,6 +182,7 @@ data class IgnoreManipulationAction(
|
||||||
val ignoreAppDowngrade: Boolean,
|
val ignoreAppDowngrade: Boolean,
|
||||||
val ignoreNotificationAccessManipulation: Boolean,
|
val ignoreNotificationAccessManipulation: Boolean,
|
||||||
val ignoreUsageStatsAccessManipulation: Boolean,
|
val ignoreUsageStatsAccessManipulation: Boolean,
|
||||||
|
val ignoreReboot: Boolean,
|
||||||
val ignoreHadManipulation: Boolean
|
val ignoreHadManipulation: Boolean
|
||||||
): ParentAction() {
|
): ParentAction() {
|
||||||
init {
|
init {
|
||||||
|
@ -182,6 +194,7 @@ data class IgnoreManipulationAction(
|
||||||
(!ignoreAppDowngrade) &&
|
(!ignoreAppDowngrade) &&
|
||||||
(!ignoreNotificationAccessManipulation) &&
|
(!ignoreNotificationAccessManipulation) &&
|
||||||
(!ignoreUsageStatsAccessManipulation) &&
|
(!ignoreUsageStatsAccessManipulation) &&
|
||||||
|
(!ignoreReboot) &&
|
||||||
(!ignoreHadManipulation)
|
(!ignoreHadManipulation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +211,12 @@ data class SetDeviceUserAction(val deviceId: String, val userId: String): Parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class SetConsiderRebootManipulationAction(val deviceId: String, val considerRebootManipulation: Boolean): ParentAction() {
|
||||||
|
init {
|
||||||
|
IdGenerator.assertIdValid(deviceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class UpdateCategoryBlockedTimesAction(val categoryId: String, val blockedTimes: ImmutableBitmask): ParentAction() {
|
data class UpdateCategoryBlockedTimesAction(val categoryId: String, val blockedTimes: ImmutableBitmask): ParentAction() {
|
||||||
init {
|
init {
|
||||||
IdGenerator.assertIdValid(categoryId)
|
IdGenerator.assertIdValid(categoryId)
|
||||||
|
|
|
@ -161,6 +161,12 @@ object LocalDatabaseAppLogicActionDispatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.didReboot && device.considerRebootManipulation) {
|
||||||
|
device = device.copy(
|
||||||
|
manipulationDidReboot = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
database.device().updateDeviceEntry(device)
|
database.device().updateDeviceEntry(device)
|
||||||
|
|
||||||
if (device.hasActiveManipulationWarning) {
|
if (device.hasActiveManipulationWarning) {
|
||||||
|
|
|
@ -271,6 +271,10 @@ object LocalDatabaseParentActionDispatcher {
|
||||||
deviceEntry = deviceEntry.copy(highestUsageStatsPermission = deviceEntry.currentUsageStatsPermission)
|
deviceEntry = deviceEntry.copy(highestUsageStatsPermission = deviceEntry.currentUsageStatsPermission)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.ignoreReboot) {
|
||||||
|
deviceEntry = deviceEntry.copy(manipulationDidReboot = false)
|
||||||
|
}
|
||||||
|
|
||||||
if (action.ignoreHadManipulation) {
|
if (action.ignoreHadManipulation) {
|
||||||
deviceEntry = deviceEntry.copy(hadManipulation = false)
|
deviceEntry = deviceEntry.copy(hadManipulation = false)
|
||||||
}
|
}
|
||||||
|
@ -324,6 +328,16 @@ object LocalDatabaseParentActionDispatcher {
|
||||||
timezone = action.timezone
|
timezone = action.timezone
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is SetConsiderRebootManipulationAction -> {
|
||||||
|
val deviceEntry = database.device().getDeviceByIdSync(action.deviceId)
|
||||||
|
?: throw IllegalArgumentException("device not found")
|
||||||
|
|
||||||
|
database.device().updateDeviceEntry(
|
||||||
|
deviceEntry.copy(
|
||||||
|
considerRebootManipulation = action.considerRebootManipulation
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}.let { }
|
}.let { }
|
||||||
|
|
||||||
database.setTransactionSuccessful()
|
database.setTransactionSuccessful()
|
||||||
|
|
|
@ -270,6 +270,13 @@ class ManageDeviceFragment : Fragment(), FragmentWithCustomTitle {
|
||||||
lifecycleOwner = this
|
lifecycleOwner = this
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ManageDeviceRebootManipulationView.bind(
|
||||||
|
view = binding.deviceRebootManipulation,
|
||||||
|
lifecycleOwner = this,
|
||||||
|
deviceEntry = deviceEntry,
|
||||||
|
auth = auth
|
||||||
|
)
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ object ManageDeviceManipulation {
|
||||||
binding.hasManipulatedDeviceAdmin = device?.manipulationOfProtectionLevel ?: false
|
binding.hasManipulatedDeviceAdmin = device?.manipulationOfProtectionLevel ?: false
|
||||||
binding.hasManipulatedUsageStatsAccess = device?.manipulationOfUsageStats ?: false
|
binding.hasManipulatedUsageStatsAccess = device?.manipulationOfUsageStats ?: false
|
||||||
binding.hasManipulatedNotificationAccess = device?.manipulationOfNotificationAccess ?: false
|
binding.hasManipulatedNotificationAccess = device?.manipulationOfNotificationAccess ?: false
|
||||||
|
binding.hasManipulationReboot = device?.manipulationDidReboot ?: false
|
||||||
binding.hasHadManipulation = (device?.hadManipulation ?: false) and (! (device?.hasActiveManipulationWarning ?: false))
|
binding.hasHadManipulation = (device?.hadManipulation ?: false) and (! (device?.hasActiveManipulationWarning ?: false))
|
||||||
binding.hasAnyManipulation = device?.hasAnyManipulation ?: false
|
binding.hasAnyManipulation = device?.hasAnyManipulation ?: false
|
||||||
})
|
})
|
||||||
|
@ -61,6 +62,7 @@ object ManageDeviceManipulation {
|
||||||
binding.deviceAdminDisabledCheckbox,
|
binding.deviceAdminDisabledCheckbox,
|
||||||
binding.usageAccessCheckbox,
|
binding.usageAccessCheckbox,
|
||||||
binding.notificationAccessCheckbox,
|
binding.notificationAccessCheckbox,
|
||||||
|
binding.rebootCheckbox,
|
||||||
binding.hadManipulationCheckbox
|
binding.hadManipulationCheckbox
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -79,6 +81,7 @@ object ManageDeviceManipulation {
|
||||||
ignoreDeviceAdminManipulationAttempt = binding.deviceAdminDisableAttemptCheckbox.isChecked && binding.hasTriedManipulatingDeviceAdmin == true,
|
ignoreDeviceAdminManipulationAttempt = binding.deviceAdminDisableAttemptCheckbox.isChecked && binding.hasTriedManipulatingDeviceAdmin == true,
|
||||||
ignoreDeviceAdminManipulation = binding.deviceAdminDisabledCheckbox.isChecked && binding.hasManipulatedDeviceAdmin == true,
|
ignoreDeviceAdminManipulation = binding.deviceAdminDisabledCheckbox.isChecked && binding.hasManipulatedDeviceAdmin == true,
|
||||||
ignoreAppDowngrade = binding.appVersionCheckbox.isChecked && binding.hasManipulatedAppVersion == true,
|
ignoreAppDowngrade = binding.appVersionCheckbox.isChecked && binding.hasManipulatedAppVersion == true,
|
||||||
|
ignoreReboot = binding.rebootCheckbox.isChecked && binding.hasManipulationReboot == true,
|
||||||
ignoreHadManipulation = binding.hadManipulationCheckbox.isChecked || (
|
ignoreHadManipulation = binding.hadManipulationCheckbox.isChecked || (
|
||||||
device.hadManipulation and device.hasActiveManipulationWarning
|
device.hadManipulation and device.hasActiveManipulationWarning
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Open TimeLimit Copyright <C> 2019 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.manage.device.manage
|
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import io.timelimit.android.data.model.Device
|
||||||
|
import io.timelimit.android.databinding.ManageDeviceRebootManipulationViewBinding
|
||||||
|
import io.timelimit.android.sync.actions.SetConsiderRebootManipulationAction
|
||||||
|
import io.timelimit.android.ui.main.ActivityViewModel
|
||||||
|
|
||||||
|
object ManageDeviceRebootManipulationView {
|
||||||
|
fun bind(
|
||||||
|
view: ManageDeviceRebootManipulationViewBinding,
|
||||||
|
deviceEntry: LiveData<Device?>,
|
||||||
|
lifecycleOwner: LifecycleOwner,
|
||||||
|
auth: ActivityViewModel
|
||||||
|
) {
|
||||||
|
deviceEntry.observe(lifecycleOwner, Observer { device ->
|
||||||
|
val checked = device?.considerRebootManipulation ?: false
|
||||||
|
|
||||||
|
view.checkbox.setOnCheckedChangeListener { _, _ -> }
|
||||||
|
view.checkbox.isChecked = checked
|
||||||
|
view.checkbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
if (isChecked != checked) {
|
||||||
|
if (
|
||||||
|
device != null &&
|
||||||
|
!auth.tryDispatchParentAction(
|
||||||
|
SetConsiderRebootManipulationAction(
|
||||||
|
deviceId = device.id,
|
||||||
|
considerRebootManipulation = isChecked
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
view.checkbox.isChecked = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -379,6 +379,9 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<include android:id="@+id/device_reboot_manipulation"
|
||||||
|
layout="@layout/manage_device_reboot_manipulation_view" />
|
||||||
|
|
||||||
<include android:id="@+id/troubleshooting_view"
|
<include android:id="@+id/troubleshooting_view"
|
||||||
layout="@layout/manage_device_troubleshooting_view" />
|
layout="@layout/manage_device_troubleshooting_view" />
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,10 @@
|
||||||
name="hasManipulatedAppVersion"
|
name="hasManipulatedAppVersion"
|
||||||
type="Boolean" />
|
type="Boolean" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="hasManipulationReboot"
|
||||||
|
type="Boolean" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="hasHadManipulation"
|
name="hasHadManipulation"
|
||||||
type="Boolean" />
|
type="Boolean" />
|
||||||
|
@ -111,6 +115,13 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:visibility="@{safeUnbox(hasManipulationReboot) ? View.VISIBLE : View.GONE}"
|
||||||
|
android:id="@+id/reboot_checkbox"
|
||||||
|
android:text="@string/manage_device_manipulation_reboot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:visibility="@{safeUnbox(hasHadManipulation) ? View.VISIBLE : View.GONE}"
|
android:visibility="@{safeUnbox(hasHadManipulation) ? View.VISIBLE : View.GONE}"
|
||||||
android:id="@+id/had_manipulation_checkbox"
|
android:id="@+id/had_manipulation_checkbox"
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Open TimeLimit Copyright <C> 2019 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/>.
|
||||||
|
-->
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textAppearance="?android:textAppearanceLarge"
|
||||||
|
android:text="@string/manage_device_reboot_manipulation_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textAppearance="?android:textAppearanceMedium"
|
||||||
|
android:text="@string/manage_device_reboot_manipulation_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:text="@string/manage_device_reboot_manipulation_checkbox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
</layout>
|
|
@ -24,6 +24,7 @@
|
||||||
<string name="manage_device_manipulation_usage_stats_access">Nutzungsdatenzugriff</string>
|
<string name="manage_device_manipulation_usage_stats_access">Nutzungsdatenzugriff</string>
|
||||||
<string name="manage_device_manipulation_notification_access">Benachrichtigungszugriff</string>
|
<string name="manage_device_manipulation_notification_access">Benachrichtigungszugriff</string>
|
||||||
<string name="manage_device_manipulation_app_version">ältere App-Version installiert</string>
|
<string name="manage_device_manipulation_app_version">ältere App-Version installiert</string>
|
||||||
|
<string name="manage_device_manipulation_reboot">Gerät wurde neu gestartet</string>
|
||||||
<string name="manage_device_manipulation_existed">Es gab eine Manipulation, die wieder beendet wurde</string>
|
<string name="manage_device_manipulation_existed">Es gab eine Manipulation, die wieder beendet wurde</string>
|
||||||
<string name="manage_device_manipulation_btn_ignore">Warnungen ignorieren</string>
|
<string name="manage_device_manipulation_btn_ignore">Warnungen ignorieren</string>
|
||||||
<string name="manage_device_manipulation_toast_nothing_selected">Sie müssen zuerst Warnungen wählen, die Sie ignorieren möchten</string>
|
<string name="manage_device_manipulation_toast_nothing_selected">Sie müssen zuerst Warnungen wählen, die Sie ignorieren möchten</string>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Open TimeLimit Copyright <C> 2019 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/>.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
<string name="manage_device_reboot_manipulation_title">Neustart als Manipulation einstufen</string>
|
||||||
|
<string name="manage_device_reboot_manipulation_text">
|
||||||
|
Wenn TimeLimit nach einem Neustart nicht schnell genug startet,
|
||||||
|
dann können Sie hier einstellen,
|
||||||
|
dass das als Manipulation gewertet werden soll.
|
||||||
|
</string>
|
||||||
|
<string name="manage_device_reboot_manipulation_checkbox">Neustart als Manipulation einstufen</string>
|
||||||
|
</resources>
|
|
@ -24,6 +24,7 @@
|
||||||
<string name="manage_device_manipulation_usage_stats_access">usage stats access</string>
|
<string name="manage_device_manipulation_usage_stats_access">usage stats access</string>
|
||||||
<string name="manage_device_manipulation_notification_access">notification access</string>
|
<string name="manage_device_manipulation_notification_access">notification access</string>
|
||||||
<string name="manage_device_manipulation_app_version">older App version installed</string>
|
<string name="manage_device_manipulation_app_version">older App version installed</string>
|
||||||
|
<string name="manage_device_manipulation_reboot">device was rebooted</string>
|
||||||
<string name="manage_device_manipulation_existed">there was a manipulation which was stopped again</string>
|
<string name="manage_device_manipulation_existed">there was a manipulation which was stopped again</string>
|
||||||
<string name="manage_device_manipulation_btn_ignore">ignore warnings</string>
|
<string name="manage_device_manipulation_btn_ignore">ignore warnings</string>
|
||||||
<string name="manage_device_manipulation_toast_nothing_selected">You have to select warnings to ignore first</string>
|
<string name="manage_device_manipulation_toast_nothing_selected">You have to select warnings to ignore first</string>
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Open TimeLimit Copyright <C> 2019 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/>.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
<string name="manage_device_reboot_manipulation_title">Consider reboot a manipulation</string>
|
||||||
|
<string name="manage_device_reboot_manipulation_text">
|
||||||
|
If TimeLimit does not start fast enough after a reboot, then you can
|
||||||
|
enable considering that as a manipulation.
|
||||||
|
</string>
|
||||||
|
<string name="manage_device_reboot_manipulation_checkbox">consider reboot a manipulation</string>
|
||||||
|
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue