mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Add new time picker mode
This commit is contained in:
parent
dc711f4c60
commit
5e694f0c1c
13 changed files with 1074 additions and 45 deletions
759
app/schemas/io.timelimit.android.data.RoomDatabase/19.json
Normal file
759
app/schemas/io.timelimit.android.data.RoomDatabase/19.json
Normal file
|
@ -0,0 +1,759 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 19,
|
||||||
|
"identityHash": "a7aedc16546b129da089b18988dce47f",
|
||||||
|
"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, 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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, `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": "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 `index_app_device_id` ON `${TABLE_NAME}` (`device_id`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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, `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, 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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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 `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": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, \"a7aedc16546b129da089b18988dce47f\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -133,4 +133,12 @@ object DatabaseMigrations {
|
||||||
database.execSQL("ALTER TABLE `category` ADD COLUMN `time_warnings` INTEGER NOT NULL DEFAULT 0")
|
database.execSQL("ALTER TABLE `category` ADD COLUMN `time_warnings` INTEGER NOT NULL DEFAULT 0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val MIGRATE_TO_V19 = object: Migration(18, 19) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
// this is empty
|
||||||
|
//
|
||||||
|
// a new possible enum value was added, the version upgrade enables the downgrade mechanism
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ import io.timelimit.android.data.model.*
|
||||||
PendingSyncAction::class,
|
PendingSyncAction::class,
|
||||||
AppActivity::class,
|
AppActivity::class,
|
||||||
Notification::class
|
Notification::class
|
||||||
], version = 18)
|
], version = 19)
|
||||||
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()
|
||||||
|
@ -86,7 +86,8 @@ abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database
|
||||||
DatabaseMigrations.MIGRATE_TO_V15,
|
DatabaseMigrations.MIGRATE_TO_V15,
|
||||||
DatabaseMigrations.MIGRATE_TO_V16,
|
DatabaseMigrations.MIGRATE_TO_V16,
|
||||||
DatabaseMigrations.MIGRATE_TO_V17,
|
DatabaseMigrations.MIGRATE_TO_V17,
|
||||||
DatabaseMigrations.MIGRATE_TO_V18
|
DatabaseMigrations.MIGRATE_TO_V18,
|
||||||
|
DatabaseMigrations.MIGRATE_TO_V19
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,4 +221,7 @@ abstract class ConfigDao {
|
||||||
|
|
||||||
fun getEnableBackgroundSyncAsync(): LiveData<Boolean> = getValueOfKeyAsync(ConfigurationItemType.EnableBackgroundSync).map { (it ?: "0") != "0" }
|
fun getEnableBackgroundSyncAsync(): LiveData<Boolean> = getValueOfKeyAsync(ConfigurationItemType.EnableBackgroundSync).map { (it ?: "0") != "0" }
|
||||||
fun setEnableBackgroundSync(enable: Boolean) = updateValueSync(ConfigurationItemType.EnableBackgroundSync, if (enable) "1" else "0")
|
fun setEnableBackgroundSync(enable: Boolean) = updateValueSync(ConfigurationItemType.EnableBackgroundSync, if (enable) "1" else "0")
|
||||||
|
|
||||||
|
fun getEnableAlternativeDurationSelectionAsync() = getValueOfKeyAsync(ConfigurationItemType.EnableAlternativeDurationSelection).map { it == "1" }
|
||||||
|
fun setEnableAlternativeDurationSelectionSync(enable: Boolean) = updateValueSync(ConfigurationItemType.EnableAlternativeDurationSelection, if (enable) "1" else "0")
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,8 @@ enum class ConfigurationItemType {
|
||||||
ServerMessage,
|
ServerMessage,
|
||||||
CustomServerUrl,
|
CustomServerUrl,
|
||||||
ForegroundAppQueryRange,
|
ForegroundAppQueryRange,
|
||||||
EnableBackgroundSync
|
EnableBackgroundSync,
|
||||||
|
EnableAlternativeDurationSelection
|
||||||
}
|
}
|
||||||
|
|
||||||
object ConfigurationItemTypeUtil {
|
object ConfigurationItemTypeUtil {
|
||||||
|
@ -108,6 +109,7 @@ object ConfigurationItemTypeUtil {
|
||||||
private const val CUSTOM_SERVER_URL = 13
|
private const val CUSTOM_SERVER_URL = 13
|
||||||
private const val FOREGROUND_APP_QUERY_RANGE = 14
|
private const val FOREGROUND_APP_QUERY_RANGE = 14
|
||||||
private const val ENABLE_BACKGROUND_SYNC = 15
|
private const val ENABLE_BACKGROUND_SYNC = 15
|
||||||
|
private const val ENABLE_ALTERNATIVE_DURATION_SELECTION = 16
|
||||||
|
|
||||||
val TYPES = listOf(
|
val TYPES = listOf(
|
||||||
ConfigurationItemType.OwnDeviceId,
|
ConfigurationItemType.OwnDeviceId,
|
||||||
|
@ -123,7 +125,8 @@ object ConfigurationItemTypeUtil {
|
||||||
ConfigurationItemType.ServerMessage,
|
ConfigurationItemType.ServerMessage,
|
||||||
ConfigurationItemType.CustomServerUrl,
|
ConfigurationItemType.CustomServerUrl,
|
||||||
ConfigurationItemType.ForegroundAppQueryRange,
|
ConfigurationItemType.ForegroundAppQueryRange,
|
||||||
ConfigurationItemType.EnableBackgroundSync
|
ConfigurationItemType.EnableBackgroundSync,
|
||||||
|
ConfigurationItemType.EnableAlternativeDurationSelection
|
||||||
)
|
)
|
||||||
|
|
||||||
fun serialize(value: ConfigurationItemType) = when(value) {
|
fun serialize(value: ConfigurationItemType) = when(value) {
|
||||||
|
@ -141,6 +144,7 @@ object ConfigurationItemTypeUtil {
|
||||||
ConfigurationItemType.CustomServerUrl -> CUSTOM_SERVER_URL
|
ConfigurationItemType.CustomServerUrl -> CUSTOM_SERVER_URL
|
||||||
ConfigurationItemType.ForegroundAppQueryRange -> FOREGROUND_APP_QUERY_RANGE
|
ConfigurationItemType.ForegroundAppQueryRange -> FOREGROUND_APP_QUERY_RANGE
|
||||||
ConfigurationItemType.EnableBackgroundSync -> ENABLE_BACKGROUND_SYNC
|
ConfigurationItemType.EnableBackgroundSync -> ENABLE_BACKGROUND_SYNC
|
||||||
|
ConfigurationItemType.EnableAlternativeDurationSelection -> ENABLE_ALTERNATIVE_DURATION_SELECTION
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parse(value: Int) = when(value) {
|
fun parse(value: Int) = when(value) {
|
||||||
|
@ -158,6 +162,7 @@ object ConfigurationItemTypeUtil {
|
||||||
CUSTOM_SERVER_URL -> ConfigurationItemType.CustomServerUrl
|
CUSTOM_SERVER_URL -> ConfigurationItemType.CustomServerUrl
|
||||||
FOREGROUND_APP_QUERY_RANGE -> ConfigurationItemType.ForegroundAppQueryRange
|
FOREGROUND_APP_QUERY_RANGE -> ConfigurationItemType.ForegroundAppQueryRange
|
||||||
ENABLE_BACKGROUND_SYNC -> ConfigurationItemType.EnableBackgroundSync
|
ENABLE_BACKGROUND_SYNC -> ConfigurationItemType.EnableBackgroundSync
|
||||||
|
ENABLE_ALTERNATIVE_DURATION_SELECTION -> ConfigurationItemType.EnableAlternativeDurationSelection
|
||||||
else -> throw IllegalArgumentException()
|
else -> throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import io.timelimit.android.ui.manage.child.advanced.managedisabletimelimits.Man
|
||||||
import io.timelimit.android.ui.manage.child.category.create.CreateCategoryDialogFragment
|
import io.timelimit.android.ui.manage.child.category.create.CreateCategoryDialogFragment
|
||||||
import io.timelimit.android.ui.manage.child.primarydevice.UpdatePrimaryDeviceDialogFragment
|
import io.timelimit.android.ui.manage.child.primarydevice.UpdatePrimaryDeviceDialogFragment
|
||||||
import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment
|
import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment
|
||||||
|
import io.timelimit.android.ui.view.SelectTimeSpanViewListener
|
||||||
|
|
||||||
class LockFragment : Fragment() {
|
class LockFragment : Fragment() {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -224,6 +225,8 @@ class LockFragment : Fragment() {
|
||||||
// bind adding extra time controls
|
// bind adding extra time controls
|
||||||
logic.fullVersion.shouldProvideFullVersionFunctions.observe(this, Observer { hasFullVersion ->
|
logic.fullVersion.shouldProvideFullVersionFunctions.observe(this, Observer { hasFullVersion ->
|
||||||
binding.extraTimeBtnOk.setOnClickListener {
|
binding.extraTimeBtnOk.setOnClickListener {
|
||||||
|
binding.extraTimeSelection.clearNumberPickerFocus()
|
||||||
|
|
||||||
if (hasFullVersion) {
|
if (hasFullVersion) {
|
||||||
if (auth.isParentAuthenticated()) {
|
if (auth.isParentAuthenticated()) {
|
||||||
runAsync {
|
runAsync {
|
||||||
|
@ -255,6 +258,22 @@ class LockFragment : Fragment() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
logic.database.config().getEnableAlternativeDurationSelectionAsync().observe(this, Observer {
|
||||||
|
binding.extraTimeSelection.enablePickerMode(it)
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.extraTimeSelection.listener = object: SelectTimeSpanViewListener {
|
||||||
|
override fun onTimeSpanChanged(newTimeInMillis: Long) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setEnablePickerMode(enable: Boolean) {
|
||||||
|
Threads.database.execute {
|
||||||
|
logic.database.config().setEnableAlternativeDurationSelectionSync(enable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// bind disable time limits
|
// bind disable time limits
|
||||||
mergeLiveData(logic.deviceUserEntry, logic.fullVersion.shouldProvideFullVersionFunctions).observe(this, Observer {
|
mergeLiveData(logic.deviceUserEntry, logic.fullVersion.shouldProvideFullVersionFunctions).observe(this, Observer {
|
||||||
(child, hasFullVersion) ->
|
(child, hasFullVersion) ->
|
||||||
|
|
|
@ -23,6 +23,7 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
|
import io.timelimit.android.async.Threads
|
||||||
import io.timelimit.android.databinding.FragmentCategorySettingsBinding
|
import io.timelimit.android.databinding.FragmentCategorySettingsBinding
|
||||||
import io.timelimit.android.logic.AppLogic
|
import io.timelimit.android.logic.AppLogic
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
|
@ -31,6 +32,7 @@ import io.timelimit.android.ui.main.ActivityViewModel
|
||||||
import io.timelimit.android.ui.main.getActivityViewModel
|
import io.timelimit.android.ui.main.getActivityViewModel
|
||||||
import io.timelimit.android.ui.manage.category.ManageCategoryFragmentArgs
|
import io.timelimit.android.ui.manage.category.ManageCategoryFragmentArgs
|
||||||
import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment
|
import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment
|
||||||
|
import io.timelimit.android.ui.view.SelectTimeSpanViewListener
|
||||||
|
|
||||||
class CategorySettingsFragment : Fragment() {
|
class CategorySettingsFragment : Fragment() {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -99,6 +101,8 @@ class CategorySettingsFragment : Fragment() {
|
||||||
|
|
||||||
appLogic.fullVersion.shouldProvideFullVersionFunctions.observe(this, Observer { hasFullVersion ->
|
appLogic.fullVersion.shouldProvideFullVersionFunctions.observe(this, Observer { hasFullVersion ->
|
||||||
binding.extraTimeBtnOk.setOnClickListener {
|
binding.extraTimeBtnOk.setOnClickListener {
|
||||||
|
binding.extraTimeSelection.clearNumberPickerFocus()
|
||||||
|
|
||||||
if (hasFullVersion) {
|
if (hasFullVersion) {
|
||||||
val newExtraTime = binding.extraTimeSelection.timeInMillis
|
val newExtraTime = binding.extraTimeSelection.timeInMillis
|
||||||
|
|
||||||
|
@ -118,6 +122,22 @@ class CategorySettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
appLogic.database.config().getEnableAlternativeDurationSelectionAsync().observe(this, Observer {
|
||||||
|
binding.extraTimeSelection.enablePickerMode(it)
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.extraTimeSelection.listener = object: SelectTimeSpanViewListener {
|
||||||
|
override fun onTimeSpanChanged(newTimeInMillis: Long) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setEnablePickerMode(enable: Boolean) {
|
||||||
|
Threads.database.execute {
|
||||||
|
appLogic.database.config().setEnableAlternativeDurationSelectionSync(enable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import androidx.lifecycle.Observer
|
||||||
import com.google.android.material.R
|
import com.google.android.material.R
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import io.timelimit.android.async.Threads
|
||||||
import io.timelimit.android.data.IdGenerator
|
import io.timelimit.android.data.IdGenerator
|
||||||
import io.timelimit.android.data.model.TimeLimitRule
|
import io.timelimit.android.data.model.TimeLimitRule
|
||||||
import io.timelimit.android.data.model.UserType
|
import io.timelimit.android.data.model.UserType
|
||||||
|
@ -92,6 +93,7 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment() {
|
||||||
val view = FragmentEditTimeLimitRuleDialogBinding.inflate(layoutInflater, container, false)
|
val view = FragmentEditTimeLimitRuleDialogBinding.inflate(layoutInflater, container, false)
|
||||||
val listener = targetFragment as EditTimeLimitRuleDialogFragmentListener
|
val listener = targetFragment as EditTimeLimitRuleDialogFragmentListener
|
||||||
var newRule: TimeLimitRule
|
var newRule: TimeLimitRule
|
||||||
|
val database = DefaultAppLogic.with(context!!).database
|
||||||
|
|
||||||
auth.authenticatedUser.observe(this, Observer {
|
auth.authenticatedUser.observe(this, Observer {
|
||||||
if (it == null || it.second.type != UserType.Parent) {
|
if (it == null || it.second.type != UserType.Parent) {
|
||||||
|
@ -135,7 +137,7 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment() {
|
||||||
view.timeSpan.timeInMillis = newRule.maximumTimeInMillis.toLong()
|
view.timeSpan.timeInMillis = newRule.maximumTimeInMillis.toLong()
|
||||||
|
|
||||||
val affectedDays = Math.max(0, (0..6).map { (newRule.dayMask.toInt() shr it) and 1 }.sum())
|
val affectedDays = Math.max(0, (0..6).map { (newRule.dayMask.toInt() shr it) and 1 }.sum())
|
||||||
view.timeSpan.maxDays = affectedDays - 1
|
view.timeSpan.maxDays = Math.max(0, affectedDays - 1) // max prevents crash
|
||||||
view.affectsMultipleDays = affectedDays >= 2
|
view.affectsMultipleDays = affectedDays >= 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +162,8 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveRule() {
|
override fun onSaveRule() {
|
||||||
|
view.timeSpan.clearNumberPickerFocus()
|
||||||
|
|
||||||
if (existingRule != null) {
|
if (existingRule != null) {
|
||||||
if (existingRule != newRule) {
|
if (existingRule != newRule) {
|
||||||
if (!auth.tryDispatchParentAction(
|
if (!auth.tryDispatchParentAction(
|
||||||
|
@ -213,10 +217,20 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment() {
|
||||||
bindRule()
|
bindRule()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setEnablePickerMode(enable: Boolean) {
|
||||||
|
Threads.database.execute {
|
||||||
|
database.config().setEnableAlternativeDurationSelectionSync(enable)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
database.config().getEnableAlternativeDurationSelectionAsync().observe(this, Observer {
|
||||||
|
view.timeSpan.enablePickerMode(it)
|
||||||
|
})
|
||||||
|
|
||||||
if (existingRule != null) {
|
if (existingRule != null) {
|
||||||
DefaultAppLogic.with(context!!).database.timeLimitRules()
|
database.timeLimitRules()
|
||||||
.getTimeLimitRuleByIdLive(existingRule!!.id).observe(this, Observer {
|
.getTimeLimitRuleByIdLive(existingRule!!.id).observe(this, Observer {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
// rule was deleted
|
// rule was deleted
|
||||||
|
|
|
@ -18,6 +18,7 @@ package io.timelimit.android.ui.view
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
|
@ -34,14 +35,16 @@ class SelectTimeSpanView(context: Context, attributeSet: AttributeSet): FrameLay
|
||||||
|
|
||||||
var listener: SelectTimeSpanViewListener? = null
|
var listener: SelectTimeSpanViewListener? = null
|
||||||
|
|
||||||
var timeInMillis: Long by Delegates.observable(0L) {
|
var timeInMillis: Long by Delegates.observable(0L) { _, _, _ ->
|
||||||
_, _, _ ->
|
|
||||||
bindTime()
|
bindTime()
|
||||||
listener?.onTimeSpanChanged(timeInMillis)
|
listener?.onTimeSpanChanged(timeInMillis)
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxDays: Int by Delegates.observable(0) {
|
var maxDays: Int by Delegates.observable(0) { _, _, _ ->
|
||||||
_, _, _ -> binding.maxDays = maxDays
|
binding.maxDays = maxDays
|
||||||
|
|
||||||
|
binding.dayPicker.maxValue = maxDays
|
||||||
|
binding.dayPickerContainer.visibility = if (maxDays > 0) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -69,6 +72,10 @@ class SelectTimeSpanView(context: Context, attributeSet: AttributeSet): FrameLay
|
||||||
binding.daysText = TimeTextUtil.days(totalDays, context!!)
|
binding.daysText = TimeTextUtil.days(totalDays, context!!)
|
||||||
binding.minutesText = TimeTextUtil.minutes(minutes, context!!)
|
binding.minutesText = TimeTextUtil.minutes(minutes, context!!)
|
||||||
binding.hoursText = TimeTextUtil.hours(hours, context!!)
|
binding.hoursText = TimeTextUtil.hours(hours, context!!)
|
||||||
|
|
||||||
|
binding.minutePicker.value = binding.minutes ?: 0
|
||||||
|
binding.hourPicker.value = binding.hours ?: 0
|
||||||
|
binding.dayPicker.value = binding.days ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readStatusFromBinding() {
|
private fun readStatusFromBinding() {
|
||||||
|
@ -79,7 +86,43 @@ class SelectTimeSpanView(context: Context, attributeSet: AttributeSet): FrameLay
|
||||||
timeInMillis = (((days * 24) + hours) * 60 + minutes) * 1000 * 60
|
timeInMillis = (((days * 24) + hours) * 60 + minutes) * 1000 * 60
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearNumberPickerFocus() {
|
||||||
|
binding.minutePicker.clearFocus()
|
||||||
|
binding.hourPicker.clearFocus()
|
||||||
|
binding.dayPicker.clearFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enablePickerMode(enable: Boolean) {
|
||||||
|
binding.seekbarContainer.visibility = if (enable) View.GONE else View.VISIBLE
|
||||||
|
binding.pickerContainer.visibility = if (enable) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
binding.minutePicker.minValue = 0
|
||||||
|
binding.minutePicker.maxValue = 59
|
||||||
|
|
||||||
|
binding.hourPicker.minValue = 0
|
||||||
|
binding.hourPicker.maxValue = 23
|
||||||
|
|
||||||
|
binding.dayPicker.minValue = 0
|
||||||
|
binding.dayPicker.maxValue = 1
|
||||||
|
binding.dayPickerContainer.visibility = View.GONE
|
||||||
|
|
||||||
|
binding.minutePicker.setOnValueChangedListener { _, _, newValue ->
|
||||||
|
binding.minutes = newValue
|
||||||
|
readStatusFromBinding()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.hourPicker.setOnValueChangedListener { _, _, newValue ->
|
||||||
|
binding.hours = newValue
|
||||||
|
readStatusFromBinding()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.dayPicker.setOnValueChangedListener { _, _, newValue ->
|
||||||
|
binding.days = newValue
|
||||||
|
readStatusFromBinding()
|
||||||
|
}
|
||||||
|
|
||||||
binding.daysSeek.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
|
binding.daysSeek.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
|
||||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||||
binding.days = progress
|
binding.days = progress
|
||||||
|
@ -124,9 +167,15 @@ class SelectTimeSpanView(context: Context, attributeSet: AttributeSet): FrameLay
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.pickerContainer.visibility = GONE
|
||||||
|
|
||||||
|
binding.switchToPickerButton.setOnClickListener { listener?.setEnablePickerMode(true) }
|
||||||
|
binding.switchToSeekbarButton.setOnClickListener { listener?.setEnablePickerMode(false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SelectTimeSpanViewListener {
|
interface SelectTimeSpanViewListener {
|
||||||
fun onTimeSpanChanged(newTimeInMillis: Long)
|
fun onTimeSpanChanged(newTimeInMillis: Long)
|
||||||
|
fun setEnablePickerMode(enable: Boolean)
|
||||||
}
|
}
|
9
app/src/main/res/drawable/ic_unfold_more_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_unfold_more_black_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zM12,18.17L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"/>
|
||||||
|
</vector>
|
|
@ -53,6 +53,18 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/seekbar_container"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<SeekBar
|
<SeekBar
|
||||||
android:id="@+id/minutes_seek"
|
android:id="@+id/minutes_seek"
|
||||||
android:progress="@{safeUnbox(minutes)}"
|
android:progress="@{safeUnbox(minutes)}"
|
||||||
|
@ -96,4 +108,94 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/switch_to_picker_button"
|
||||||
|
android:src="@drawable/ic_unfold_more_black_24dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/picker_container"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/day_picker_container"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/day_picker"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="@string/select_time_span_view_days"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/hour_picker"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="@string/select_time_span_view_hours"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/minute_picker"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="@string/select_time_span_view_minutes"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/switch_to_seekbar_button"
|
||||||
|
android:rotation="90"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:src="@drawable/ic_unfold_more_black_24dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
|
|
20
app/src/main/res/values-de/strings-select-time-span-view.xml
Normal file
20
app/src/main/res/values-de/strings-select-time-span-view.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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="select_time_span_view_minutes">Minuten</string>
|
||||||
|
<string name="select_time_span_view_hours">Stunden</string>
|
||||||
|
<string name="select_time_span_view_days">Tage</string>
|
||||||
|
</resources>
|
20
app/src/main/res/values/strings-select-time-span-view.xml
Normal file
20
app/src/main/res/values/strings-select-time-span-view.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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="select_time_span_view_minutes">Minutes</string>
|
||||||
|
<string name="select_time_span_view_hours">Hours</string>
|
||||||
|
<string name="select_time_span_view_days">Days</string>
|
||||||
|
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue