mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Make the categories sortable
This commit is contained in:
parent
412966df26
commit
0d2dd7bed1
16 changed files with 1020 additions and 33 deletions
828
app/schemas/io.timelimit.android.data.RoomDatabase/25.json
Normal file
828
app/schemas/io.timelimit.android.data.RoomDatabase/25.json
Normal file
|
@ -0,0 +1,828 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 25,
|
||||
"identityHash": "2a202b43acf918df8278ab09c67b5ddf",
|
||||
"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, `temporarily_blocked_end_time` 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, `sort` 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": "temporarilyBlockedEndTime",
|
||||
"columnName": "temporarily_blocked_end_time",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"fieldPath": "sort",
|
||||
"columnName": "sort",
|
||||
"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, '2a202b43acf918df8278ab09c67b5ddf')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -172,4 +172,10 @@ object DatabaseMigrations {
|
|||
database.execSQL("ALTER TABLE `category` ADD COLUMN `temporarily_blocked_end_time` INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATE_TO_V25 = object: Migration(24, 25) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE `category` ADD COLUMN `sort` INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import io.timelimit.android.data.model.*
|
|||
AppActivity::class,
|
||||
Notification::class,
|
||||
AllowedContact::class
|
||||
], version = 24)
|
||||
], version = 25)
|
||||
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
||||
companion object {
|
||||
private val lock = Object()
|
||||
|
@ -93,7 +93,8 @@ abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database
|
|||
DatabaseMigrations.MIGRATE_TO_V21,
|
||||
DatabaseMigrations.MIGRATE_TO_V22,
|
||||
DatabaseMigrations.MIGRATE_TO_V23,
|
||||
DatabaseMigrations.MIGRATE_TO_V24
|
||||
DatabaseMigrations.MIGRATE_TO_V24,
|
||||
DatabaseMigrations.MIGRATE_TO_V25
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
|
|
@ -97,6 +97,14 @@ abstract class CategoryDao {
|
|||
|
||||
@Query("SELECT * FROM category")
|
||||
abstract fun getAllCategoriesSync(): List<Category>
|
||||
|
||||
// if there is no category, then the result is null
|
||||
// Room converts null to 0 si it works
|
||||
@Query("SELECT MAX(sort) + 1 FROM category WHERE child_id = :childId")
|
||||
abstract fun getNextCategorySortKeyByChildId(childId: String): Int
|
||||
|
||||
@Query("UPDATE category SET sort = :sort WHERE id = :categoryId")
|
||||
abstract fun updateCategorySorting(categoryId: String, sort: Int)
|
||||
}
|
||||
|
||||
data class CategoryWithVersionNumbers(
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.data.extensions
|
||||
|
||||
import io.timelimit.android.data.model.Category
|
||||
|
||||
fun List<Category>.sorted(): List<Category> {
|
||||
val categoryIds = this.map { it.id }.toSet()
|
||||
|
||||
val sortedCategories = mutableListOf<Category>()
|
||||
val childCategories = this.filter { categoryIds.contains(it.parentCategoryId) }.groupBy { it.parentCategoryId }
|
||||
|
||||
this.filterNot { categoryIds.contains(it.parentCategoryId) }.sortedBy { it.sort }.forEach { category ->
|
||||
sortedCategories.add(category)
|
||||
|
||||
childCategories[category.id]?.sortedBy { it.sort }?.let { items ->
|
||||
sortedCategories.addAll(items)
|
||||
}
|
||||
}
|
||||
|
||||
return sortedCategories.toList()
|
||||
}
|
|
@ -63,7 +63,9 @@ data class Category(
|
|||
@ColumnInfo(name = "min_battery_charging")
|
||||
val minBatteryLevelWhileCharging: Int,
|
||||
@ColumnInfo(name = "min_battery_mobile")
|
||||
val minBatteryLevelMobile: Int
|
||||
val minBatteryLevelMobile: Int,
|
||||
@ColumnInfo(name = "sort")
|
||||
val sort: Int
|
||||
): JsonSerializable {
|
||||
companion object {
|
||||
const val MINUTES_PER_DAY = 60 * 24
|
||||
|
@ -85,6 +87,7 @@ data class Category(
|
|||
private const val TIME_WARNINGS = "tw"
|
||||
private const val MIN_BATTERY_CHARGING = "minBatteryCharging"
|
||||
private const val MIN_BATTERY_MOBILE = "minBatteryMobile"
|
||||
private const val SORT = "sort"
|
||||
|
||||
fun parse(reader: JsonReader): Category {
|
||||
var id: String? = null
|
||||
|
@ -104,6 +107,7 @@ data class Category(
|
|||
var timeWarnings = 0
|
||||
var minBatteryCharging = 0
|
||||
var minBatteryMobile = 0
|
||||
var sort = 0
|
||||
|
||||
reader.beginObject()
|
||||
|
||||
|
@ -125,6 +129,7 @@ data class Category(
|
|||
TIME_WARNINGS -> timeWarnings = reader.nextInt()
|
||||
MIN_BATTERY_CHARGING -> minBatteryCharging = reader.nextInt()
|
||||
MIN_BATTERY_MOBILE -> minBatteryMobile = reader.nextInt()
|
||||
SORT -> sort = reader.nextInt()
|
||||
else -> reader.skipValue()
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +152,8 @@ data class Category(
|
|||
blockAllNotifications = blockAllNotifications,
|
||||
timeWarnings = timeWarnings,
|
||||
minBatteryLevelWhileCharging = minBatteryCharging,
|
||||
minBatteryLevelMobile = minBatteryMobile
|
||||
minBatteryLevelMobile = minBatteryMobile,
|
||||
sort = sort
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -192,6 +198,7 @@ data class Category(
|
|||
writer.name(TIME_WARNINGS).value(timeWarnings)
|
||||
writer.name(MIN_BATTERY_CHARGING).value(minBatteryLevelWhileCharging)
|
||||
writer.name(MIN_BATTERY_MOBILE).value(minBatteryLevelMobile)
|
||||
writer.name(SORT).value(sort)
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
|
|
|
@ -182,7 +182,8 @@ class AppSetupLogic(private val appLogic: AppLogic) {
|
|||
blockAllNotifications = false,
|
||||
timeWarnings = 0,
|
||||
minBatteryLevelWhileCharging = 0,
|
||||
minBatteryLevelMobile = 0
|
||||
minBatteryLevelMobile = 0,
|
||||
sort = 0
|
||||
))
|
||||
|
||||
appLogic.database.category().addCategory(Category(
|
||||
|
@ -201,7 +202,8 @@ class AppSetupLogic(private val appLogic: AppLogic) {
|
|||
blockAllNotifications = false,
|
||||
timeWarnings = 0,
|
||||
minBatteryLevelWhileCharging = 0,
|
||||
minBatteryLevelMobile = 0
|
||||
minBatteryLevelMobile = 0,
|
||||
sort = 1
|
||||
))
|
||||
|
||||
// add default allowed apps
|
||||
|
|
|
@ -312,7 +312,8 @@ object ApplyServerDataStatus {
|
|||
parentCategoryId = newCategory.parentCategoryId,
|
||||
timeWarnings = newCategory.timeWarnings,
|
||||
minBatteryLevelMobile = newCategory.minBatteryLevelMobile,
|
||||
minBatteryLevelWhileCharging = newCategory.minBatteryLevelCharging
|
||||
minBatteryLevelWhileCharging = newCategory.minBatteryLevelCharging,
|
||||
sort = newCategory.sort
|
||||
))
|
||||
} else {
|
||||
val updatedCategory = oldCategory.copy(
|
||||
|
@ -327,7 +328,8 @@ object ApplyServerDataStatus {
|
|||
parentCategoryId = newCategory.parentCategoryId,
|
||||
timeWarnings = newCategory.timeWarnings,
|
||||
minBatteryLevelMobile = newCategory.minBatteryLevelMobile,
|
||||
minBatteryLevelWhileCharging = newCategory.minBatteryLevelCharging
|
||||
minBatteryLevelWhileCharging = newCategory.minBatteryLevelCharging,
|
||||
sort = newCategory.sort
|
||||
)
|
||||
|
||||
if (updatedCategory != oldCategory) {
|
||||
|
|
|
@ -804,6 +804,36 @@ data class UpdateCategoryBatteryLimit(val categoryId: String, val chargingLimit:
|
|||
writer.endObject()
|
||||
}
|
||||
}
|
||||
data class UpdateCategorySortingAction(val categoryIds: List<String>): ParentAction() {
|
||||
companion object {
|
||||
private const val TYPE_VALUE = "UPDATE_CATEGORY_SORTING"
|
||||
private const val CATEGORY_IDS = "categoryIds"
|
||||
}
|
||||
|
||||
init {
|
||||
if (categoryIds.isEmpty()) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
if (categoryIds.distinct().size != categoryIds.size) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
categoryIds.forEach { IdGenerator.assertIdValid(it) }
|
||||
}
|
||||
|
||||
override fun serialize(writer: JsonWriter) {
|
||||
writer.beginObject()
|
||||
|
||||
writer.name(TYPE).value(TYPE_VALUE)
|
||||
|
||||
writer.name(CATEGORY_IDS).beginArray()
|
||||
categoryIds.forEach { writer.value(it) }
|
||||
writer.endArray()
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
}
|
||||
// DeviceDao
|
||||
|
||||
data class UpdateDeviceStatusAction(
|
||||
|
|
|
@ -67,6 +67,7 @@ object ActionParser {
|
|||
// UpdateEnableActivityLevelBlocking
|
||||
// UpdateCategoryTimeWarningsAction
|
||||
// UpdateCategoryBatteryLimit
|
||||
// UpdateCategorySorting
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,8 @@ object LocalDatabaseParentActionDispatcher {
|
|||
DatabaseValidation.assertChildExists(database, action.childId)
|
||||
|
||||
// create the category
|
||||
val sort = database.category().getNextCategorySortKeyByChildId(action.childId)
|
||||
|
||||
database.category().addCategory(Category(
|
||||
id = action.categoryId,
|
||||
childId = action.childId,
|
||||
|
@ -83,7 +85,8 @@ object LocalDatabaseParentActionDispatcher {
|
|||
blockAllNotifications = false,
|
||||
timeWarnings = 0,
|
||||
minBatteryLevelWhileCharging = 0,
|
||||
minBatteryLevelMobile = 0
|
||||
minBatteryLevelMobile = 0,
|
||||
sort = sort
|
||||
))
|
||||
}
|
||||
is DeleteCategoryAction -> {
|
||||
|
@ -557,6 +560,16 @@ object LocalDatabaseParentActionDispatcher {
|
|||
)
|
||||
)
|
||||
}
|
||||
is UpdateCategorySortingAction -> {
|
||||
// no validation here:
|
||||
// - only parents can do it
|
||||
// - using it over categories which don't belong together destroys the sorting for both,
|
||||
// but does not cause any trouble
|
||||
|
||||
action.categoryIds.forEachIndexed { index, categoryId ->
|
||||
database.category().updateCategorySorting(categoryId, index)
|
||||
}
|
||||
}
|
||||
}.let { }
|
||||
|
||||
database.setTransactionSuccessful()
|
||||
|
|
|
@ -342,7 +342,8 @@ data class ServerUpdatedCategoryBaseData(
|
|||
val blockAllNotifications: Boolean,
|
||||
val timeWarnings: Int,
|
||||
val minBatteryLevelCharging: Int,
|
||||
val minBatteryLevelMobile: Int
|
||||
val minBatteryLevelMobile: Int,
|
||||
val sort: Int
|
||||
) {
|
||||
companion object {
|
||||
private const val CATEGORY_ID = "categoryId"
|
||||
|
@ -358,6 +359,7 @@ data class ServerUpdatedCategoryBaseData(
|
|||
private const val TIME_WARNINGS = "timeWarnings"
|
||||
private const val MIN_BATTERY_LEVEL_MOBILE = "mblMobile"
|
||||
private const val MIN_BATTERY_LEVEL_CHARGING = "mblCharging"
|
||||
private const val SORT = "sort"
|
||||
|
||||
fun parse(reader: JsonReader): ServerUpdatedCategoryBaseData {
|
||||
var categoryId: String? = null
|
||||
|
@ -374,6 +376,7 @@ data class ServerUpdatedCategoryBaseData(
|
|||
var timeWarnings = 0
|
||||
var minBatteryLevelCharging = 0
|
||||
var minBatteryLevelMobile = 0
|
||||
var sort = 0
|
||||
|
||||
reader.beginObject()
|
||||
while (reader.hasNext()) {
|
||||
|
@ -391,6 +394,7 @@ data class ServerUpdatedCategoryBaseData(
|
|||
TIME_WARNINGS -> timeWarnings = reader.nextInt()
|
||||
MIN_BATTERY_LEVEL_CHARGING -> minBatteryLevelCharging = reader.nextInt()
|
||||
MIN_BATTERY_LEVEL_MOBILE -> minBatteryLevelMobile = reader.nextInt()
|
||||
SORT -> sort = reader.nextInt()
|
||||
else -> reader.skipValue()
|
||||
}
|
||||
}
|
||||
|
@ -409,7 +413,8 @@ data class ServerUpdatedCategoryBaseData(
|
|||
blockAllNotifications = blockAllNotifications,
|
||||
timeWarnings = timeWarnings,
|
||||
minBatteryLevelCharging = minBatteryLevelCharging,
|
||||
minBatteryLevelMobile = minBatteryLevelMobile
|
||||
minBatteryLevelMobile = minBatteryLevelMobile,
|
||||
sort = sort
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import io.timelimit.android.R
|
|||
import io.timelimit.android.async.Threads
|
||||
import io.timelimit.android.coroutines.executeAndWait
|
||||
import io.timelimit.android.coroutines.runAsync
|
||||
import io.timelimit.android.data.extensions.sorted
|
||||
import io.timelimit.android.data.model.*
|
||||
import io.timelimit.android.databinding.LockFragmentBinding
|
||||
import io.timelimit.android.livedata.*
|
||||
|
@ -186,7 +187,7 @@ class LockFragment : Fragment() {
|
|||
} else {
|
||||
val (user, categoryEntries) = status
|
||||
|
||||
categoryEntries.forEach {
|
||||
categoryEntries.sorted().forEach {
|
||||
category ->
|
||||
|
||||
val button = Button(context)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package io.timelimit.android.ui.manage.child.advanced.manageblocktemporarily
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import io.timelimit.android.data.extensions.sorted
|
||||
import io.timelimit.android.data.model.Category
|
||||
import io.timelimit.android.livedata.*
|
||||
import io.timelimit.android.logic.RealTimeLogic
|
||||
|
@ -35,7 +36,7 @@ object ManageBlockTemporarilyItems {
|
|||
val time = liveDataFromFunction { realTimeLogic.getCurrentTimeInMillis() }
|
||||
|
||||
return categories.map { categories ->
|
||||
categories.map { category ->
|
||||
categories.sorted().map { category ->
|
||||
ManageBlockTemporarilyItem(
|
||||
categoryId = category.id,
|
||||
categoryTitle = category.title,
|
||||
|
|
|
@ -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
|
||||
|
@ -33,6 +33,7 @@ import io.timelimit.android.data.model.HintsToShow
|
|||
import io.timelimit.android.extensions.safeNavigate
|
||||
import io.timelimit.android.logic.AppLogic
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.sync.actions.UpdateCategorySortingAction
|
||||
import io.timelimit.android.ui.main.ActivityViewModel
|
||||
import io.timelimit.android.ui.main.getActivityViewModel
|
||||
import io.timelimit.android.ui.manage.child.ManageChildFragmentArgs
|
||||
|
@ -97,12 +98,74 @@ class ManageChildCategoriesFragment : Fragment() {
|
|||
if (item == CategoriesIntroductionHeader) {
|
||||
return makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE, ItemTouchHelper.END or ItemTouchHelper.START) or
|
||||
makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.END or ItemTouchHelper.START)
|
||||
} else if (item is CategoryItem) {
|
||||
return makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP or ItemTouchHelper.DOWN) or
|
||||
makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.UP or ItemTouchHelper.DOWN)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder) = throw IllegalStateException()
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
val fromIndex = viewHolder.adapterPosition
|
||||
val toIndex = target.adapterPosition
|
||||
val categories = adapter.categories!!
|
||||
|
||||
if (fromIndex == RecyclerView.NO_POSITION || toIndex == RecyclerView.NO_POSITION) {
|
||||
return false
|
||||
}
|
||||
|
||||
val fromItem = categories[fromIndex]
|
||||
val toItem = categories[toIndex]
|
||||
|
||||
if (!(fromItem is CategoryItem)) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
if (!(toItem is CategoryItem)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (fromItem.parentCategoryTitle == null) {
|
||||
if (toItem.parentCategoryTitle != null) {
|
||||
return false
|
||||
}
|
||||
|
||||
val parentCategories = mutableListOf<CategoryItem>()
|
||||
|
||||
categories.forEach { if (it is CategoryItem && it.parentCategoryTitle == null) { parentCategories.add(it) } }
|
||||
|
||||
val targetIndex = parentCategories.indexOf(toItem)
|
||||
val sourceIndex = parentCategories.indexOf(fromItem)
|
||||
|
||||
parentCategories.add(targetIndex, parentCategories.removeAt(sourceIndex))
|
||||
|
||||
return auth.tryDispatchParentAction(
|
||||
UpdateCategorySortingAction(
|
||||
categoryIds = parentCategories.map { it.category.id }
|
||||
)
|
||||
)
|
||||
} else {
|
||||
if (toItem.category.parentCategoryId != fromItem.category.parentCategoryId) {
|
||||
return false
|
||||
}
|
||||
|
||||
val childCategories = mutableListOf<CategoryItem>()
|
||||
|
||||
categories.forEach { if (it is CategoryItem && it.category.parentCategoryId == fromItem.category.parentCategoryId) { childCategories.add(it) } }
|
||||
|
||||
val targetIndex = childCategories.indexOf(toItem)
|
||||
val sourceIndex = childCategories.indexOf(fromItem)
|
||||
|
||||
childCategories.add(targetIndex, childCategories.removeAt(sourceIndex))
|
||||
|
||||
return auth.tryDispatchParentAction(
|
||||
UpdateCategorySortingAction(
|
||||
categoryIds = childCategories.map { it.category.id }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
val database = logic.database
|
||||
|
|
|
@ -20,7 +20,7 @@ import android.util.SparseLongArray
|
|||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.timelimit.android.data.extensions.mapToTimezone
|
||||
import io.timelimit.android.data.model.Category
|
||||
import io.timelimit.android.data.extensions.sorted
|
||||
import io.timelimit.android.data.model.HintsToShow
|
||||
import io.timelimit.android.date.DateInTimezone
|
||||
import io.timelimit.android.date.getMinuteOfWeek
|
||||
|
@ -30,7 +30,6 @@ import io.timelimit.android.livedata.map
|
|||
import io.timelimit.android.livedata.switchMap
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.logic.RemainingTime
|
||||
import java.util.*
|
||||
|
||||
class ManageChildCategoriesModel(application: Application): AndroidViewModel(application) {
|
||||
private val logic = DefaultAppLogic.with(application)
|
||||
|
@ -78,22 +77,7 @@ class ManageChildCategoriesModel(application: Application): AndroidViewModel(app
|
|||
)
|
||||
}
|
||||
|
||||
private val sortedCategories = categories.map { categories ->
|
||||
val categoryById = categories.associateBy { it.id }
|
||||
|
||||
val sortedCategories = mutableListOf<Category>()
|
||||
val childCategories = categories.filter { categoryById.containsKey(it.parentCategoryId) }.groupBy { it.parentCategoryId }
|
||||
|
||||
categories.filterNot { categoryById.containsKey(it.parentCategoryId) }.sortedBy { it.title.toLowerCase(Locale.getDefault()) }.forEach { category ->
|
||||
sortedCategories.add(category)
|
||||
|
||||
childCategories[category.id]?.sortedBy { it.title.toLowerCase(Locale.getDefault()) }?.let { items ->
|
||||
sortedCategories.addAll(items)
|
||||
}
|
||||
}
|
||||
|
||||
sortedCategories.toList()
|
||||
}
|
||||
private val sortedCategories = categories.map { it.sorted() }
|
||||
|
||||
private val categoryItems = categoryForUnassignedAppsLive.switchMap { categoryForUnassignedApps ->
|
||||
sortedCategories.switchMap { categories ->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue