mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-05 10:49:26 +02:00
Add battery limit feature
This commit is contained in:
parent
3d4fa21183
commit
f0cd6b92c0
35 changed files with 1481 additions and 58 deletions
816
app/schemas/io.timelimit.android.data.RoomDatabase/23.json
Normal file
816
app/schemas/io.timelimit.android.data.RoomDatabase/23.json
Normal file
|
@ -0,0 +1,816 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 23,
|
||||
"identityHash": "1d39f67895d7506bbfac727ac075efb3",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "user",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `password` TEXT NOT NULL, `second_password_salt` TEXT NOT NULL, `type` TEXT NOT NULL, `timezone` TEXT NOT NULL, `disable_limits_until` INTEGER NOT NULL, `mail` TEXT NOT NULL, `current_device` TEXT NOT NULL, `category_for_not_assigned_apps` TEXT NOT NULL, `relax_primary_device` INTEGER NOT NULL, `mail_notification_flags` INTEGER NOT NULL, `blocked_times` 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": "secondPasswordSalt",
|
||||
"columnName": "second_password_salt",
|
||||
"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": "mail",
|
||||
"columnName": "mail",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "currentDevice",
|
||||
"columnName": "current_device",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "categoryForNotAssignedApps",
|
||||
"columnName": "category_for_not_assigned_apps",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "relaxPrimaryDevice",
|
||||
"columnName": "relax_primary_device",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mailNotificationFlags",
|
||||
"columnName": "mail_notification_flags",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "blockedTimes",
|
||||
"columnName": "blocked_times",
|
||||
"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, `apps_version` TEXT NOT NULL, `network_time` 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, `had_manipulation_flags` INTEGER NOT NULL, `did_report_uninstall` INTEGER NOT NULL, `is_user_kept_signed_in` INTEGER NOT NULL, `show_device_connected` INTEGER NOT NULL, `default_user` TEXT NOT NULL, `default_user_timeout` INTEGER NOT NULL, `consider_reboot_manipulation` INTEGER NOT NULL, `current_overlay_permission` TEXT NOT NULL, `highest_overlay_permission` TEXT NOT NULL, `current_accessibility_service_permission` INTEGER NOT NULL, `was_accessibility_service_permission` INTEGER NOT NULL, `enable_activity_level_blocking` INTEGER NOT NULL, `q_or_later` 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": "installedAppsVersion",
|
||||
"columnName": "apps_version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "networkTime",
|
||||
"columnName": "network_time",
|
||||
"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": "hadManipulationFlags",
|
||||
"columnName": "had_manipulation_flags",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "didReportUninstall",
|
||||
"columnName": "did_report_uninstall",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isUserKeptSignedIn",
|
||||
"columnName": "is_user_kept_signed_in",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "showDeviceConnected",
|
||||
"columnName": "show_device_connected",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "defaultUser",
|
||||
"columnName": "default_user",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "defaultUserTimeout",
|
||||
"columnName": "default_user_timeout",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "considerRebootManipulation",
|
||||
"columnName": "consider_reboot_manipulation",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "currentOverlayPermission",
|
||||
"columnName": "current_overlay_permission",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "highestOverlayPermission",
|
||||
"columnName": "highest_overlay_permission",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accessibilityServiceEnabled",
|
||||
"columnName": "current_accessibility_service_permission",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "wasAccessibilityServiceEnabled",
|
||||
"columnName": "was_accessibility_service_permission",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enableActivityLevelBlocking",
|
||||
"columnName": "enable_activity_level_blocking",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "qOrLater",
|
||||
"columnName": "q_or_later",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "app",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`device_id` TEXT NOT NULL, `package_name` TEXT NOT NULL, `title` TEXT NOT NULL, `launchable` INTEGER NOT NULL, `recommendation` TEXT NOT NULL, PRIMARY KEY(`device_id`, `package_name`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "deviceId",
|
||||
"columnName": "device_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
"device_id",
|
||||
"package_name"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_app_device_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"device_id"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_app_device_id` ON `${TABLE_NAME}` (`device_id`)"
|
||||
},
|
||||
{
|
||||
"name": "index_app_package_name",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"package_name"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `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 IF NOT EXISTS `index_category_app_category_id` ON `${TABLE_NAME}` (`category_id`)"
|
||||
},
|
||||
{
|
||||
"name": "index_category_app_package_name",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"package_name"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `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, `base_version` TEXT NOT NULL, `apps_version` TEXT NOT NULL, `rules_version` TEXT NOT NULL, `usedtimes_version` TEXT NOT NULL, `parent_category_id` TEXT NOT NULL, `block_all_notifications` INTEGER NOT NULL, `time_warnings` INTEGER NOT NULL, `min_battery_charging` INTEGER NOT NULL, `min_battery_mobile` INTEGER 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": "baseVersion",
|
||||
"columnName": "base_version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "assignedAppsVersion",
|
||||
"columnName": "apps_version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timeLimitRulesVersion",
|
||||
"columnName": "rules_version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "usedTimesVersion",
|
||||
"columnName": "usedtimes_version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentCategoryId",
|
||||
"columnName": "parent_category_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "blockAllNotifications",
|
||||
"columnName": "block_all_notifications",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timeWarnings",
|
||||
"columnName": "time_warnings",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minBatteryLevelWhileCharging",
|
||||
"columnName": "min_battery_charging",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minBatteryLevelMobile",
|
||||
"columnName": "min_battery_mobile",
|
||||
"affinity": "INTEGER",
|
||||
"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}` (`device_id` TEXT NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`device_id`, `package_name`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "deviceId",
|
||||
"columnName": "device_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "packageName",
|
||||
"columnName": "package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"device_id",
|
||||
"package_name"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "pending_sync_action",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sequence_number` INTEGER NOT NULL, `action` TEXT NOT NULL, `integrity` TEXT NOT NULL, `scheduled_for_upload` INTEGER NOT NULL, `type` TEXT NOT NULL, `user_id` TEXT NOT NULL, PRIMARY KEY(`sequence_number`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "sequenceNumber",
|
||||
"columnName": "sequence_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "encodedAction",
|
||||
"columnName": "action",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "integrity",
|
||||
"columnName": "integrity",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scheduledForUpload",
|
||||
"columnName": "scheduled_for_upload",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "user_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"sequence_number"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_pending_sync_action_scheduled_for_upload",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"scheduled_for_upload"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_pending_sync_action_scheduled_for_upload` ON `${TABLE_NAME}` (`scheduled_for_upload`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "app_activity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`device_id` TEXT NOT NULL, `app_package_name` TEXT NOT NULL, `activity_class_name` TEXT NOT NULL, `activity_title` TEXT NOT NULL, PRIMARY KEY(`device_id`, `app_package_name`, `activity_class_name`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "deviceId",
|
||||
"columnName": "device_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "appPackageName",
|
||||
"columnName": "app_package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "activityClassName",
|
||||
"columnName": "activity_class_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "activity_title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"device_id",
|
||||
"app_package_name",
|
||||
"activity_class_name"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "notification",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` INTEGER NOT NULL, `id` TEXT NOT NULL, `first_notify_time` INTEGER NOT NULL, `dismissed` INTEGER NOT NULL, PRIMARY KEY(`type`, `id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "firstNotifyTime",
|
||||
"columnName": "first_notify_time",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDismissed",
|
||||
"columnName": "dismissed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "allowed_contact",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `phone` TEXT NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "phone",
|
||||
"columnName": "phone",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"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, '1d39f67895d7506bbfac727ac075efb3')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -159,4 +159,11 @@ object DatabaseMigrations {
|
|||
database.execSQL("ALTER TABLE `user` ADD COLUMN `blocked_times` TEXT NOT NULL DEFAULT \"\"")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATE_TO_V23 = object: Migration(22, 23) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE `category` ADD COLUMN `min_battery_charging` INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("ALTER TABLE `category` ADD COLUMN `min_battery_mobile` INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -35,7 +35,7 @@ import io.timelimit.android.data.model.*
|
|||
AppActivity::class,
|
||||
Notification::class,
|
||||
AllowedContact::class
|
||||
], version = 22)
|
||||
], version = 23)
|
||||
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
||||
companion object {
|
||||
private val lock = Object()
|
||||
|
@ -91,7 +91,8 @@ abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database
|
|||
DatabaseMigrations.MIGRATE_TO_V19,
|
||||
DatabaseMigrations.MIGRATE_TO_V20,
|
||||
DatabaseMigrations.MIGRATE_TO_V21,
|
||||
DatabaseMigrations.MIGRATE_TO_V22
|
||||
DatabaseMigrations.MIGRATE_TO_V22,
|
||||
DatabaseMigrations.MIGRATE_TO_V23
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -57,7 +57,11 @@ data class Category(
|
|||
@ColumnInfo(name = "block_all_notifications")
|
||||
val blockAllNotifications: Boolean,
|
||||
@ColumnInfo(name = "time_warnings")
|
||||
val timeWarnings: Int
|
||||
val timeWarnings: Int,
|
||||
@ColumnInfo(name = "min_battery_charging")
|
||||
val minBatteryLevelWhileCharging: Int,
|
||||
@ColumnInfo(name = "min_battery_mobile")
|
||||
val minBatteryLevelMobile: Int
|
||||
): JsonSerializable {
|
||||
companion object {
|
||||
const val MINUTES_PER_DAY = 60 * 24
|
||||
|
@ -76,6 +80,8 @@ data class Category(
|
|||
private const val PARENT_CATEGORY_ID = "pc"
|
||||
private const val BlOCK_ALL_NOTIFICATIONS = "ban"
|
||||
private const val TIME_WARNINGS = "tw"
|
||||
private const val MIN_BATTERY_CHARGING = "minBatteryCharging"
|
||||
private const val MIN_BATTERY_MOBILE = "minBatteryMobile"
|
||||
|
||||
fun parse(reader: JsonReader): Category {
|
||||
var id: String? = null
|
||||
|
@ -92,6 +98,8 @@ data class Category(
|
|||
var parentCategoryId = ""
|
||||
var blockAllNotifications = false
|
||||
var timeWarnings = 0
|
||||
var minBatteryCharging = 0
|
||||
var minBatteryMobile = 0
|
||||
|
||||
reader.beginObject()
|
||||
|
||||
|
@ -110,6 +118,8 @@ data class Category(
|
|||
PARENT_CATEGORY_ID -> parentCategoryId = reader.nextString()
|
||||
BlOCK_ALL_NOTIFICATIONS -> blockAllNotifications = reader.nextBoolean()
|
||||
TIME_WARNINGS -> timeWarnings = reader.nextInt()
|
||||
MIN_BATTERY_CHARGING -> minBatteryCharging = reader.nextInt()
|
||||
MIN_BATTERY_MOBILE -> minBatteryMobile = reader.nextInt()
|
||||
else -> reader.skipValue()
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +139,9 @@ data class Category(
|
|||
usedTimesVersion = usedTimesVersion!!,
|
||||
parentCategoryId = parentCategoryId,
|
||||
blockAllNotifications = blockAllNotifications,
|
||||
timeWarnings = timeWarnings
|
||||
timeWarnings = timeWarnings,
|
||||
minBatteryLevelWhileCharging = minBatteryCharging,
|
||||
minBatteryLevelMobile = minBatteryMobile
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -145,6 +157,14 @@ data class Category(
|
|||
if (title.isEmpty()) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
if (minBatteryLevelMobile < 0 || minBatteryLevelWhileCharging < 0) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
if (minBatteryLevelMobile > 100 || minBatteryLevelWhileCharging > 100) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(writer: JsonWriter) {
|
||||
|
@ -163,6 +183,8 @@ data class Category(
|
|||
writer.name(PARENT_CATEGORY_ID).value(parentCategoryId)
|
||||
writer.name(BlOCK_ALL_NOTIFICATIONS).value(blockAllNotifications)
|
||||
writer.name(TIME_WARNINGS).value(timeWarnings)
|
||||
writer.name(MIN_BATTERY_CHARGING).value(minBatteryLevelWhileCharging)
|
||||
writer.name(MIN_BATTERY_MOBILE).value(minBatteryLevelMobile)
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -17,6 +17,7 @@ package io.timelimit.android.integration.platform
|
|||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.TypeConverter
|
||||
import io.timelimit.android.data.model.App
|
||||
import io.timelimit.android.data.model.AppActivity
|
||||
|
@ -62,6 +63,9 @@ abstract class PlatformIntegration(
|
|||
// returns true on success
|
||||
abstract fun setLockTaskPackages(packageNames: List<String>): Boolean
|
||||
|
||||
abstract fun getBatteryStatus(): BatteryStatus
|
||||
abstract fun getBatteryStatusLive(): LiveData<BatteryStatus>
|
||||
|
||||
var installedAppsChangeListener: Runnable? = null
|
||||
}
|
||||
|
||||
|
@ -193,3 +197,18 @@ data class AppStatusMessage(
|
|||
val subtext: String? = null,
|
||||
val showSwitchToDefaultUserOption: Boolean = false
|
||||
): Parcelable
|
||||
|
||||
data class BatteryStatus(
|
||||
val charging: Boolean,
|
||||
val level: Int
|
||||
) {
|
||||
companion object {
|
||||
val dummy = BatteryStatus(false, 0)
|
||||
}
|
||||
|
||||
init {
|
||||
if (level < 0 || level > 100) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -38,6 +38,7 @@ import android.view.KeyEvent
|
|||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import io.timelimit.android.BuildConfig
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.coroutines.runAsyncExpectForever
|
||||
|
@ -79,6 +80,7 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio
|
|||
private val notificationManager = this.context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
private val deviceAdmin = ComponentName(context.applicationContext, AdminReceiver::class.java)
|
||||
private val overlay = OverlayUtil(context as Application)
|
||||
private val battery = BatteryStatusUtil(context)
|
||||
|
||||
init {
|
||||
AppsChangeListener.registerBroadcastReceiver(this.context, object : BroadcastReceiver() {
|
||||
|
@ -442,4 +444,7 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBatteryStatus(): BatteryStatus = battery.status.value!!
|
||||
override fun getBatteryStatusLive(): LiveData<BatteryStatus> = battery.status
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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.integration.platform.android
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.BatteryManager
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.timelimit.android.integration.platform.BatteryStatus
|
||||
|
||||
class BatteryStatusUtil(context: Context) {
|
||||
private val statusInternal = MutableLiveData<BatteryStatus>().apply { value = BatteryStatus.dummy }
|
||||
val status: LiveData<BatteryStatus> = statusInternal
|
||||
|
||||
init {
|
||||
context.applicationContext.registerReceiver(object: BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val charging = run {
|
||||
val status: Int = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
|
||||
|
||||
status == BatteryManager.BATTERY_STATUS_CHARGING
|
||||
|| status == BatteryManager.BATTERY_STATUS_FULL
|
||||
}
|
||||
|
||||
val level = run {
|
||||
val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
|
||||
val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
|
||||
|
||||
(level * 100 / scale).coerceIn(0, 100)
|
||||
}
|
||||
|
||||
statusInternal.value = BatteryStatus(
|
||||
charging = charging,
|
||||
level = level
|
||||
)
|
||||
}
|
||||
}, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -112,6 +112,7 @@ class NotificationListener: NotificationListenerService() {
|
|||
BlockingReason.MissingNetworkTime -> getString(R.string.lock_reason_short_missing_network_time)
|
||||
BlockingReason.RequiresCurrentDevice -> getString(R.string.lock_reason_short_requires_current_device)
|
||||
BlockingReason.NotificationsAreBlocked -> getString(R.string.lock_reason_short_notification_blocking)
|
||||
BlockingReason.BatteryLimit -> getString(R.string.lock_reason_short_battery_limit)
|
||||
BlockingReason.None -> throw IllegalStateException()
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -16,6 +16,8 @@
|
|||
package io.timelimit.android.integration.platform.dummy
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.timelimit.android.data.model.App
|
||||
import io.timelimit.android.data.model.AppActivity
|
||||
import io.timelimit.android.integration.platform.*
|
||||
|
@ -33,6 +35,7 @@ class DummyIntegration(
|
|||
var lastAppStatusMessage: AppStatusMessage? = null
|
||||
var launchLockScreenForPackage: String? = null
|
||||
var showRevokeTemporarilyAllowedNotification = false
|
||||
val batteryStatus = MutableLiveData<BatteryStatus>().apply { value = BatteryStatus(true, 100) }
|
||||
|
||||
override fun getLocalApps(deviceId: String): Collection<App> {
|
||||
return localApps.map{ it.copy(deviceId = deviceId) }
|
||||
|
@ -159,4 +162,7 @@ class DummyIntegration(
|
|||
override fun setEnableSystemLockdown(enableLockdown: Boolean) = false
|
||||
|
||||
override fun setLockTaskPackages(packageNames: List<String>) = false
|
||||
|
||||
override fun getBatteryStatus(): BatteryStatus = batteryStatus.value!!
|
||||
override fun getBatteryStatusLive(): LiveData<BatteryStatus> = batteryStatus
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -179,7 +179,9 @@ class AppSetupLogic(private val appLogic: AppLogic) {
|
|||
usedTimesVersion = "",
|
||||
parentCategoryId = "",
|
||||
blockAllNotifications = false,
|
||||
timeWarnings = 0
|
||||
timeWarnings = 0,
|
||||
minBatteryLevelWhileCharging = 0,
|
||||
minBatteryLevelMobile = 0
|
||||
))
|
||||
|
||||
appLogic.database.category().addCategory(Category(
|
||||
|
@ -195,7 +197,9 @@ class AppSetupLogic(private val appLogic: AppLogic) {
|
|||
usedTimesVersion = "",
|
||||
parentCategoryId = "",
|
||||
blockAllNotifications = false,
|
||||
timeWarnings = 0
|
||||
timeWarnings = 0,
|
||||
minBatteryLevelWhileCharging = 0,
|
||||
minBatteryLevelMobile = 0
|
||||
))
|
||||
|
||||
// add default allowed apps
|
||||
|
|
|
@ -36,6 +36,7 @@ import io.timelimit.android.integration.platform.ProtectionLevel
|
|||
import io.timelimit.android.integration.platform.android.AccessibilityService
|
||||
import io.timelimit.android.integration.platform.android.AndroidIntegrationApps
|
||||
import io.timelimit.android.livedata.*
|
||||
import io.timelimit.android.logic.extension.isCategoryAllowed
|
||||
import io.timelimit.android.sync.actions.UpdateDeviceStatusAction
|
||||
import io.timelimit.android.sync.actions.apply.ApplyActionUtil
|
||||
import io.timelimit.android.ui.IsAppInForeground
|
||||
|
@ -261,6 +262,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
|||
|
||||
// get the current status
|
||||
val isScreenOn = appLogic.platformIntegration.isScreenOn()
|
||||
val batteryStatus = appLogic.platformIntegration.getBatteryStatus()
|
||||
|
||||
appLogic.defaultUserLogic.reportScreenOn(isScreenOn)
|
||||
|
||||
|
@ -335,6 +337,10 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
|||
if (category == null) {
|
||||
usedTimeUpdateHelper?.commit(appLogic)
|
||||
|
||||
openLockscreen(foregroundAppPackageName, foregroundAppActivityName)
|
||||
} else if ((!batteryStatus.isCategoryAllowed(category)) || (!batteryStatus.isCategoryAllowed(parentCategory))) {
|
||||
usedTimeUpdateHelper?.commit(appLogic)
|
||||
|
||||
openLockscreen(foregroundAppPackageName, foregroundAppActivityName)
|
||||
} else if (category.temporarilyBlocked or (parentCategory?.temporarilyBlocked == true)) {
|
||||
usedTimeUpdateHelper?.commit(appLogic)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -26,6 +26,7 @@ import io.timelimit.android.date.getMinuteOfWeek
|
|||
import io.timelimit.android.integration.platform.android.AndroidIntegrationApps
|
||||
import io.timelimit.android.integration.time.TimeApi
|
||||
import io.timelimit.android.livedata.*
|
||||
import io.timelimit.android.logic.extension.isCategoryAllowed
|
||||
import java.util.*
|
||||
|
||||
enum class BlockingReason {
|
||||
|
@ -37,7 +38,8 @@ enum class BlockingReason {
|
|||
TimeOverExtraTimeCanBeUsedLater,
|
||||
MissingNetworkTime,
|
||||
RequiresCurrentDevice,
|
||||
NotificationsAreBlocked
|
||||
NotificationsAreBlocked,
|
||||
BatteryLimit
|
||||
}
|
||||
|
||||
enum class BlockingLevel {
|
||||
|
@ -74,6 +76,7 @@ class BlockingReasonUtil(private val appLogic: AppLogic) {
|
|||
}
|
||||
|
||||
private val enableActivityLevelFiltering = appLogic.deviceEntry.map { it?.enableActivityLevelBlocking ?: false }
|
||||
private val batteryLevel = appLogic.platformIntegration.getBatteryStatusLive()
|
||||
|
||||
fun getBlockingReason(packageName: String, activityName: String?): LiveData<BlockingReasonDetail> {
|
||||
// check precondition that the app is running
|
||||
|
@ -211,7 +214,7 @@ class BlockingReasonUtil(private val appLogic: AppLogic) {
|
|||
else
|
||||
liveDataFromValue(false)
|
||||
|
||||
val nextLevel = getBlockingReasonStep4Point7(category, child, timeZone, isParentCategory, blockingLevel)
|
||||
val nextLevel = getBlockingReasonStep4Point6(category, child, timeZone, isParentCategory, blockingLevel)
|
||||
|
||||
return shouldBlockNotifications.switchMap { blockNotifications ->
|
||||
nextLevel.map { blockingReason ->
|
||||
|
@ -229,6 +232,24 @@ class BlockingReasonUtil(private val appLogic: AppLogic) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getBlockingReasonStep4Point6(category: Category, child: User, timeZone: TimeZone, isParentCategory: Boolean, blockingLevel: BlockingLevel): LiveData<BlockingReason> {
|
||||
val next = getBlockingReasonStep4Point7(category, child, timeZone, isParentCategory, blockingLevel)
|
||||
|
||||
return if (category.minBatteryLevelWhileCharging == 0 && category.minBatteryLevelMobile == 0) {
|
||||
next
|
||||
} else {
|
||||
val batteryLevelOk = batteryLevel.map { it.isCategoryAllowed(category) }.ignoreUnchanged()
|
||||
|
||||
batteryLevelOk.switchMap { ok ->
|
||||
if (ok) {
|
||||
next
|
||||
} else {
|
||||
liveDataFromValue(BlockingReason.BatteryLimit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBlockingReasonStep4Point7(category: Category, child: User, timeZone: TimeZone, isParentCategory: Boolean, blockingLevel: BlockingLevel): LiveData<BlockingReason> {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(LOG_TAG, "step 4.7")
|
||||
|
@ -264,7 +285,7 @@ class BlockingReasonUtil(private val appLogic: AppLogic) {
|
|||
if (parentCategory == null) {
|
||||
liveDataFromValue(BlockingReason.None)
|
||||
} else {
|
||||
getBlockingReasonStep4Point7(parentCategory, child, timeZone, true, blockingLevel)
|
||||
getBlockingReasonStep4Point6(parentCategory, child, timeZone, true, blockingLevel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -23,6 +23,7 @@ import io.timelimit.android.BuildConfig
|
|||
import io.timelimit.android.data.model.*
|
||||
import io.timelimit.android.date.DateInTimezone
|
||||
import io.timelimit.android.livedata.*
|
||||
import io.timelimit.android.logic.extension.isCategoryAllowed
|
||||
import java.util.*
|
||||
|
||||
class CategoriesBlockingReasonUtil(private val appLogic: AppLogic) {
|
||||
|
@ -32,6 +33,7 @@ class CategoriesBlockingReasonUtil(private val appLogic: AppLogic) {
|
|||
|
||||
private val blockingReason = BlockingReasonUtil(appLogic)
|
||||
private val temporarilyTrustedTimeInMillis = blockingReason.getTemporarilyTrustedTimeInMillis()
|
||||
private val batteryLevel = appLogic.platformIntegration.getBatteryStatusLive()
|
||||
|
||||
// NOTE: this ignores the current device rule
|
||||
fun getCategoryBlockingReasons(
|
||||
|
@ -129,25 +131,31 @@ class CategoriesBlockingReasonUtil(private val appLogic: AppLogic) {
|
|||
areLimitsTemporarilyDisabled: LiveData<Boolean>
|
||||
): LiveData<BlockingReason> {
|
||||
return category.switchMap { category ->
|
||||
if (category.temporarilyBlocked) {
|
||||
liveDataFromValue(BlockingReason.TemporarilyBlocked)
|
||||
} else {
|
||||
areLimitsTemporarilyDisabled.switchMap { areLimitsTemporarilyDisabled ->
|
||||
if (areLimitsTemporarilyDisabled) {
|
||||
liveDataFromValue(BlockingReason.None)
|
||||
} else {
|
||||
checkCategoryBlockedTimeAreas(
|
||||
temporarilyTrustedMinuteOfWeek = temporarilyTrustedMinuteOfWeek,
|
||||
blockedMinutesInWeek = category.blockedMinutesInWeek.dataNotToModify
|
||||
).switchMap { blockedTimeAreasReason ->
|
||||
if (blockedTimeAreasReason != BlockingReason.None) {
|
||||
liveDataFromValue(blockedTimeAreasReason)
|
||||
} else {
|
||||
checkCategoryTimeLimitRules(
|
||||
temporarilyTrustedDate = temporarilyTrustedDate,
|
||||
category = category,
|
||||
rules = appLogic.database.timeLimitRules().getTimeLimitRulesByCategory(category.id)
|
||||
)
|
||||
val batteryOk = batteryLevel.map { it.isCategoryAllowed(category) }.ignoreUnchanged()
|
||||
|
||||
batteryOk.switchMap { ok ->
|
||||
if (!ok) {
|
||||
liveDataFromValue(BlockingReason.BatteryLimit)
|
||||
} else if (category.temporarilyBlocked) {
|
||||
liveDataFromValue(BlockingReason.TemporarilyBlocked)
|
||||
} else {
|
||||
areLimitsTemporarilyDisabled.switchMap { areLimitsTemporarilyDisabled ->
|
||||
if (areLimitsTemporarilyDisabled) {
|
||||
liveDataFromValue(BlockingReason.None)
|
||||
} else {
|
||||
checkCategoryBlockedTimeAreas(
|
||||
temporarilyTrustedMinuteOfWeek = temporarilyTrustedMinuteOfWeek,
|
||||
blockedMinutesInWeek = category.blockedMinutesInWeek.dataNotToModify
|
||||
).switchMap { blockedTimeAreasReason ->
|
||||
if (blockedTimeAreasReason != BlockingReason.None) {
|
||||
liveDataFromValue(blockedTimeAreasReason)
|
||||
} else {
|
||||
checkCategoryTimeLimitRules(
|
||||
temporarilyTrustedDate = temporarilyTrustedDate,
|
||||
category = category,
|
||||
rules = appLogic.database.timeLimitRules().getTimeLimitRulesByCategory(category.id)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.logic.extension
|
||||
|
||||
import io.timelimit.android.data.model.Category
|
||||
import io.timelimit.android.integration.platform.BatteryStatus
|
||||
|
||||
fun BatteryStatus.isCategoryAllowed(category: Category?): Boolean {
|
||||
return if (category == null) {
|
||||
true
|
||||
} else if (this.charging) {
|
||||
this.level >= category.minBatteryLevelWhileCharging
|
||||
} else {
|
||||
this.level >= category.minBatteryLevelMobile
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -309,7 +309,9 @@ object ApplyServerDataStatus {
|
|||
timeLimitRulesVersion = "",
|
||||
usedTimesVersion = "",
|
||||
parentCategoryId = newCategory.parentCategoryId,
|
||||
timeWarnings = newCategory.timeWarnings
|
||||
timeWarnings = newCategory.timeWarnings,
|
||||
minBatteryLevelMobile = newCategory.minBatteryLevelMobile,
|
||||
minBatteryLevelWhileCharging = newCategory.minBatteryLevelCharging
|
||||
))
|
||||
} else {
|
||||
val updatedCategory = oldCategory.copy(
|
||||
|
@ -321,7 +323,9 @@ object ApplyServerDataStatus {
|
|||
blockAllNotifications = newCategory.blockAllNotifications,
|
||||
baseVersion = newCategory.baseDataVersion,
|
||||
parentCategoryId = newCategory.parentCategoryId,
|
||||
timeWarnings = newCategory.timeWarnings
|
||||
timeWarnings = newCategory.timeWarnings,
|
||||
minBatteryLevelMobile = newCategory.minBatteryLevelMobile,
|
||||
minBatteryLevelWhileCharging = newCategory.minBatteryLevelCharging
|
||||
)
|
||||
|
||||
if (updatedCategory != oldCategory) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -677,7 +677,47 @@ data class SetParentCategory(val categoryId: String, val parentCategory: String)
|
|||
writer.endObject()
|
||||
}
|
||||
}
|
||||
data class UpdateCategoryBatteryLimit(val categoryId: String, val chargingLimit: Int?, val mobileLimit: Int?): ParentAction() {
|
||||
companion object {
|
||||
private const val TYPE_VALUE = "UPDATE_CATEGORY_BATTERY_LIMIT"
|
||||
private const val CATEGORY_ID = "categoryId"
|
||||
private const val CHARGE_LIMIT = "chargeLimit"
|
||||
private const val MOBILE_LIMIT = "mobileLimit"
|
||||
}
|
||||
|
||||
init {
|
||||
IdGenerator.assertIdValid(categoryId)
|
||||
|
||||
if (chargingLimit != null) {
|
||||
if (chargingLimit < 0 || chargingLimit > 100) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
if (mobileLimit != null) {
|
||||
if (mobileLimit < 0 || mobileLimit > 100) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(writer: JsonWriter) {
|
||||
writer.beginObject()
|
||||
|
||||
writer.name(TYPE).value(TYPE_VALUE)
|
||||
writer.name(CATEGORY_ID).value(categoryId)
|
||||
|
||||
if (chargingLimit != null) {
|
||||
writer.name(CHARGE_LIMIT).value(chargingLimit)
|
||||
}
|
||||
|
||||
if (mobileLimit != null) {
|
||||
writer.name(MOBILE_LIMIT).value(mobileLimit)
|
||||
}
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
}
|
||||
// DeviceDao
|
||||
|
||||
data class UpdateDeviceStatusAction(
|
||||
|
|
|
@ -65,6 +65,7 @@ object ActionParser {
|
|||
// UpdateCategoryBlockAllNotificationsAction
|
||||
// UpdateEnableActivityLevelBlocking
|
||||
// UpdateCategoryTimeWarningsAction
|
||||
// UpdateCategoryBatteryLimit
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -80,7 +80,9 @@ object LocalDatabaseParentActionDispatcher {
|
|||
usedTimesVersion = "",
|
||||
parentCategoryId = "",
|
||||
blockAllNotifications = false,
|
||||
timeWarnings = 0
|
||||
timeWarnings = 0,
|
||||
minBatteryLevelWhileCharging = 0,
|
||||
minBatteryLevelMobile = 0
|
||||
))
|
||||
}
|
||||
is DeleteCategoryAction -> {
|
||||
|
@ -539,6 +541,17 @@ object LocalDatabaseParentActionDispatcher {
|
|||
)
|
||||
)
|
||||
}
|
||||
is UpdateCategoryBatteryLimit -> {
|
||||
val categoryEntry = database.category().getCategoryByIdSync(action.categoryId)
|
||||
?: throw IllegalArgumentException("can not update battery limit for a category which does not exist")
|
||||
|
||||
database.category().updateCategorySync(
|
||||
categoryEntry.copy(
|
||||
minBatteryLevelWhileCharging = action.chargingLimit ?: categoryEntry.minBatteryLevelWhileCharging,
|
||||
minBatteryLevelMobile = action.mobileLimit ?: categoryEntry.minBatteryLevelMobile
|
||||
)
|
||||
)
|
||||
}
|
||||
}.let { }
|
||||
|
||||
database.setTransactionSuccessful()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -339,7 +339,9 @@ data class ServerUpdatedCategoryBaseData(
|
|||
val baseDataVersion: String,
|
||||
val parentCategoryId: String,
|
||||
val blockAllNotifications: Boolean,
|
||||
val timeWarnings: Int
|
||||
val timeWarnings: Int,
|
||||
val minBatteryLevelCharging: Int,
|
||||
val minBatteryLevelMobile: Int
|
||||
) {
|
||||
companion object {
|
||||
private const val CATEGORY_ID = "categoryId"
|
||||
|
@ -352,6 +354,8 @@ data class ServerUpdatedCategoryBaseData(
|
|||
private const val PARENT_CATEGORY_ID = "parentCategoryId"
|
||||
private const val BLOCK_ALL_NOTIFICATIONS = "blockAllNotifications"
|
||||
private const val TIME_WARNINGS = "timeWarnings"
|
||||
private const val MIN_BATTERY_LEVEL_MOBILE = "mblMobile"
|
||||
private const val MIN_BATTERY_LEVEL_CHARGING = "mblCharging"
|
||||
|
||||
fun parse(reader: JsonReader): ServerUpdatedCategoryBaseData {
|
||||
var categoryId: String? = null
|
||||
|
@ -365,6 +369,8 @@ data class ServerUpdatedCategoryBaseData(
|
|||
// added later -> default values
|
||||
var blockAllNotifications = false
|
||||
var timeWarnings = 0
|
||||
var minBatteryLevelCharging = 0
|
||||
var minBatteryLevelMobile = 0
|
||||
|
||||
reader.beginObject()
|
||||
while (reader.hasNext()) {
|
||||
|
@ -379,6 +385,8 @@ data class ServerUpdatedCategoryBaseData(
|
|||
PARENT_CATEGORY_ID -> parentCategoryId = reader.nextString()
|
||||
BLOCK_ALL_NOTIFICATIONS -> blockAllNotifications = reader.nextBoolean()
|
||||
TIME_WARNINGS -> timeWarnings = reader.nextInt()
|
||||
MIN_BATTERY_LEVEL_CHARGING -> minBatteryLevelCharging = reader.nextInt()
|
||||
MIN_BATTERY_LEVEL_MOBILE -> minBatteryLevelMobile = reader.nextInt()
|
||||
else -> reader.skipValue()
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +402,9 @@ data class ServerUpdatedCategoryBaseData(
|
|||
baseDataVersion = baseDataVersion!!,
|
||||
parentCategoryId = parentCategoryId!!,
|
||||
blockAllNotifications = blockAllNotifications,
|
||||
timeWarnings = timeWarnings
|
||||
timeWarnings = timeWarnings,
|
||||
minBatteryLevelCharging = minBatteryLevelCharging,
|
||||
minBatteryLevelMobile = minBatteryLevelMobile
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.Observer
|
||||
import io.timelimit.android.databinding.DiagnoseBatteryFragmentBinding
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
|
||||
class DiagnoseBatteryFragment : Fragment() {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val binding = DiagnoseBatteryFragmentBinding.inflate(inflater, container, false)
|
||||
val logic = DefaultAppLogic.with(context!!)
|
||||
|
||||
logic.platformIntegration.getBatteryStatusLive().observe(this, Observer {
|
||||
binding.charging = it.charging
|
||||
binding.level = it.level
|
||||
})
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -54,6 +54,13 @@ class DiagnoseMainFragment : Fragment() {
|
|||
)
|
||||
}
|
||||
|
||||
binding.diagnoseBatteryButton.setOnClickListener {
|
||||
navigation.safeNavigate(
|
||||
DiagnoseMainFragmentDirections.actionDiagnoseMainFragmentToDiagnoseBatteryFragment(),
|
||||
R.id.diagnoseMainFragment
|
||||
)
|
||||
}
|
||||
|
||||
binding.diagnoseFgaButton.setOnClickListener {
|
||||
navigation.safeNavigate(
|
||||
DiagnoseMainFragmentDirections.actionDiagnoseMainFragmentToDiagnoseForegroundAppFragment(),
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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.category.settings
|
||||
|
||||
import android.widget.SeekBar
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.data.model.Category
|
||||
import io.timelimit.android.databinding.CategoryBatteryLimitViewBinding
|
||||
import io.timelimit.android.livedata.ignoreUnchanged
|
||||
import io.timelimit.android.livedata.map
|
||||
import io.timelimit.android.sync.actions.UpdateCategoryBatteryLimit
|
||||
import io.timelimit.android.ui.main.ActivityViewModel
|
||||
|
||||
object CategoryBatteryLimitView {
|
||||
fun bind(
|
||||
binding: CategoryBatteryLimitViewBinding,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
category: LiveData<Category?>,
|
||||
auth: ActivityViewModel,
|
||||
categoryId: String
|
||||
) {
|
||||
binding.seekbarCharging.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
|
||||
override fun onStartTrackingTouch(p0: SeekBar?) = Unit
|
||||
override fun onStopTrackingTouch(p0: SeekBar?) = Unit
|
||||
|
||||
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
|
||||
binding.minLevelCharging = p1 * 10
|
||||
}
|
||||
})
|
||||
|
||||
binding.seekbarMobile.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
|
||||
override fun onStartTrackingTouch(p0: SeekBar?) = Unit
|
||||
override fun onStopTrackingTouch(p0: SeekBar?) = Unit
|
||||
|
||||
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
|
||||
binding.minLevelMobile = p1 * 10
|
||||
}
|
||||
})
|
||||
|
||||
category.map {
|
||||
it?.run { it.minBatteryLevelMobile / 10 to it.minBatteryLevelWhileCharging / 10 }
|
||||
}.ignoreUnchanged().observe(lifecycleOwner, Observer {
|
||||
if (it != null) {
|
||||
val (mobile, charging) = it
|
||||
|
||||
binding.seekbarMobile.progress = mobile
|
||||
binding.seekbarCharging.progress = charging
|
||||
}
|
||||
})
|
||||
|
||||
binding.confirmBtn.setOnClickListener {
|
||||
if (
|
||||
auth.tryDispatchParentAction(
|
||||
UpdateCategoryBatteryLimit(
|
||||
categoryId = categoryId,
|
||||
mobileLimit = binding.seekbarMobile.progress * 10,
|
||||
chargingLimit = binding.seekbarCharging.progress * 10
|
||||
)
|
||||
)
|
||||
) {
|
||||
Snackbar.make(binding.root, R.string.category_settings_battery_limit_confirm_toast, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2020 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
|
||||
|
@ -61,6 +61,14 @@ class CategorySettingsFragment : Fragment() {
|
|||
auth = auth
|
||||
)
|
||||
|
||||
CategoryBatteryLimitView.bind(
|
||||
binding = binding.batteryLimit,
|
||||
lifecycleOwner = this,
|
||||
category = categoryEntry,
|
||||
auth = auth,
|
||||
categoryId = params.categoryId
|
||||
)
|
||||
|
||||
ParentCategoryView.bind(
|
||||
binding = binding.parentCategory,
|
||||
lifecycleOwner = this,
|
||||
|
|
88
app/src/main/res/layout/category_battery_limit_view.xml
Normal file
88
app/src/main/res/layout/category_battery_limit_view.xml
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 - 2020 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"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="minLevelCharging"
|
||||
type="int" />
|
||||
|
||||
<variable
|
||||
name="minLevelMobile"
|
||||
type="int" />
|
||||
</data>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardUseCompatPadding="true">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
android:text="@string/category_settings_battery_limit_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_settings_battery_limit_description"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:text="@{@string/category_settings_battery_limit_charging(minLevelCharging)}"
|
||||
tools:text="@string/category_settings_battery_limit_charging"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<SeekBar
|
||||
android:max="10"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/seekbar_charging" />
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:text="@{@string/category_settings_battery_limit_mobile(minLevelMobile)}"
|
||||
tools:text="@string/category_settings_battery_limit_mobile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<SeekBar
|
||||
android:max="10"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/seekbar_mobile" />
|
||||
|
||||
<Button
|
||||
android:text="@string/generic_save"
|
||||
android:id="@+id/confirm_btn"
|
||||
android:layout_gravity="end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</layout>
|
65
app/src/main/res/layout/diagnose_battery_fragment.xml
Normal file
65
app/src/main/res/layout/diagnose_battery_fragment.xml
Normal file
|
@ -0,0 +1,65 @@
|
|||
<!--
|
||||
TimeLimit Copyright <C> 2019 - 2020 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:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context="io.timelimit.android.ui.diagnose.DiagnoseBatteryFragment">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="charging"
|
||||
type="boolean" />
|
||||
|
||||
<variable
|
||||
name="level"
|
||||
type="int" />
|
||||
</data>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_margin="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardUseCompatPadding="true">
|
||||
<LinearLayout
|
||||
android:padding="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:text="@string/diagnose_bat_title"
|
||||
android:textAppearance="?android:textAppearanceLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
tools:text="@string/diagnose_bat_status"
|
||||
android:text="@{@string/diagnose_bat_status(charging ? @string/diagnose_bat_status_charging : @string/diagnose_bat_status_mobile)}"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
tools:text="@string/diagnose_bat_level"
|
||||
android:text="@{@string/diagnose_bat_level(level)}"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</layout>
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -70,6 +70,9 @@
|
|||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<include android:id="@+id/battery_limit"
|
||||
layout="@layout/category_battery_limit_view" />
|
||||
|
||||
<include android:id="@+id/time_warnings"
|
||||
layout="@layout/category_time_warnings_view" />
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -44,6 +44,12 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/diagnose_battery_button"
|
||||
android:text="@string/diagnose_bat_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/diagnose_fga_button"
|
||||
android:text="@string/diagnose_fga_title"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -223,6 +223,14 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:visibility="@{reason == BlockingReason.BatteryLimit ? View.VISIBLE : View.GONE}"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:text="@{@string/lock_reason_battery_limit(blockedKindLabel)}"
|
||||
tools:text="@string/lock_reason_battery_limit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ProgressBar
|
||||
android:visibility="@{reason == null ? View.VISIBLE : View.GONE}"
|
||||
android:padding="8dp"
|
||||
|
|
|
@ -346,6 +346,13 @@
|
|||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_diagnoseMainFragment_to_diagnoseBatteryFragment"
|
||||
app:destination="@id/diagnoseBatteryFragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/diagnoseClockFragment"
|
||||
|
@ -427,4 +434,9 @@
|
|||
android:name="io.timelimit.android.ui.payment.StayAwesomeFragment"
|
||||
android:label="stay_awesome_fragment"
|
||||
tools:layout="@layout/stay_awesome_fragment" />
|
||||
<fragment
|
||||
android:id="@+id/diagnoseBatteryFragment"
|
||||
android:name="io.timelimit.android.ui.diagnose.DiagnoseBatteryFragment"
|
||||
android:label="diagnose_battery_fragment"
|
||||
tools:layout="@layout/diagnose_battery_fragment" />
|
||||
</navigation>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -33,4 +33,11 @@
|
|||
<string name="category_settings_parent_category_assigned_to">Diese Kategorie ist eine Unterkategorie von %s.</string>
|
||||
<string name="category_settings_parent_category_button">Oberkategorie wählen</string>
|
||||
<string name="category_settings_parent_category_none">keine Oberkategorie</string>
|
||||
|
||||
<string name="category_settings_battery_limit_title">Akkulimit</string>
|
||||
<string name="category_settings_battery_limit_description">Hiermit kann ein Mindestakkustand festgelegt werden,
|
||||
der erreicht sein muss, damit diese Kategorie genutzt werden kann.</string>
|
||||
<string name="category_settings_battery_limit_charging">Beim Aufladen muss der Akku mindestens zu %d\%% voll sein</string>
|
||||
<string name="category_settings_battery_limit_mobile">Beim Entladen müssen mindestens %d\%% des Akkus verbleiben</string>
|
||||
<string name="category_settings_battery_limit_confirm_toast">Das Akkulimit wurde gespeichert</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -59,4 +59,10 @@
|
|||
<string name="diagnose_exf_isc">Systemverbindungsstatus ignorieren</string>
|
||||
|
||||
<string name="diagnose_bg_task_loop_ex">Hintergrundaufgabenschleifenfehler</string>
|
||||
|
||||
<string name="diagnose_bat_title">Akku</string>
|
||||
<string name="diagnose_bat_status">Status: %s</string>
|
||||
<string name="diagnose_bat_status_charging">Aufladen/ voll</string>
|
||||
<string name="diagnose_bat_status_mobile">Entladen</string>
|
||||
<string name="diagnose_bat_level">Akkustand: %d\%%</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -75,6 +75,11 @@
|
|||
muss immer ein einziges Gerät für die Nutzung zeitbegrenzter Apps gewählt werden.
|
||||
Momentan ist nicht dieses Gerät ausgewählt.
|
||||
</string>
|
||||
<string name="lock_reason_battery_limit">
|
||||
Diese %s ist in einer Kategorie, für die ein Akkulimit eingerichtet wurde
|
||||
und dieses Akkulimit wurde erreicht.
|
||||
Zum Wiederfreigeben muss der Akku aufgeladen werden.
|
||||
</string>
|
||||
|
||||
<string name="lock_reason_short_no_category">keine Kategorie</string>
|
||||
<string name="lock_reason_short_temporarily_blocked">vorübergehend gesperrt</string>
|
||||
|
@ -83,6 +88,7 @@
|
|||
<string name="lock_reason_short_missing_network_time">Netzwerkzeit erforderlich</string>
|
||||
<string name="lock_reason_short_requires_current_device">nicht als aktuelles Gerät gewählt</string>
|
||||
<string name="lock_reason_short_notification_blocking">alle Benachrichtigungen werden blockiert</string>
|
||||
<string name="lock_reason_short_battery_limit">Akkulimit unterschritten</string>
|
||||
|
||||
<string name="lock_overlay_warning">
|
||||
Öffnen des Sperrbildschirms fehlgeschlagen.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -34,4 +34,10 @@
|
|||
<string name="category_settings_parent_category_assigned_to">This category is a child category of %s now.</string>
|
||||
<string name="category_settings_parent_category_button">Select parent category</string>
|
||||
<string name="category_settings_parent_category_none">no parent category</string>
|
||||
|
||||
<string name="category_settings_battery_limit_title">Battery limit</string>
|
||||
<string name="category_settings_battery_limit_description">This allows to set minimum battery levels for this category</string>
|
||||
<string name="category_settings_battery_limit_charging">While charging, the battery must have at least %d\%%</string>
|
||||
<string name="category_settings_battery_limit_mobile">While discharging, the battery must have at least %d\%%</string>
|
||||
<string name="category_settings_battery_limit_confirm_toast">The battery limit was saved</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -59,4 +59,10 @@
|
|||
<string name="diagnose_exf_isc">Ignore system connection status</string>
|
||||
|
||||
<string name="diagnose_bg_task_loop_ex">Background task loop exception</string>
|
||||
|
||||
<string name="diagnose_bat_title">Battery</string>
|
||||
<string name="diagnose_bat_status">Status: %s</string>
|
||||
<string name="diagnose_bat_status_charging">Charging/ full</string>
|
||||
<string name="diagnose_bat_status_mobile">Not charging</string>
|
||||
<string name="diagnose_bat_level">Level: %d\%%</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2020 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.
|
||||
|
@ -79,6 +79,11 @@
|
|||
there must be one current device at which limited Apps can be used.
|
||||
This device is not selected as current device now.
|
||||
</string>
|
||||
<string name="lock_reason_battery_limit">
|
||||
This %s is part of a category which has got a battery limit and this
|
||||
battery limit was reached.
|
||||
To unlock it, charge the battery.
|
||||
</string>
|
||||
|
||||
<string name="lock_reason_short_no_category">no category</string>
|
||||
<string name="lock_reason_short_temporarily_blocked">temporarily blocked</string>
|
||||
|
@ -87,6 +92,7 @@
|
|||
<string name="lock_reason_short_missing_network_time">missing network time</string>
|
||||
<string name="lock_reason_short_requires_current_device">device must be the current device</string>
|
||||
<string name="lock_reason_short_notification_blocking">all notifications are blocked</string>
|
||||
<string name="lock_reason_short_battery_limit">battery limit reached</string>
|
||||
|
||||
<string name="lock_overlay_warning">
|
||||
Failed to open the lock screen.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue