From 86d0592524132c7e472539459e0a082bbc4d9872 Mon Sep 17 00:00:00 2001 From: Jonas Lochmann Date: Mon, 12 Jun 2023 02:00:00 +0200 Subject: [PATCH] Add platform level --- app/build.gradle | 2 +- .../47.json | 1773 +++++++++++++++++ .../io/timelimit/android/data/RoomDatabase.kt | 6 +- .../io/timelimit/android/data/model/Device.kt | 24 +- .../timelimit/android/logic/AppSetupLogic.kt | 4 +- .../android/logic/BackgroundTaskLogic.kt | 11 + .../android/sync/ApplyServerDataStatus.kt | 8 +- .../timelimit/android/sync/actions/Actions.kt | 28 +- .../sync/actions/dispatch/AppLogicAction.kt | 10 +- .../android/sync/network/ServerDataStatus.kt | 14 +- .../timelimit/android/util/AndroidVersion.kt | 7 +- 11 files changed, 1871 insertions(+), 16 deletions(-) create mode 100644 app/schemas/io.timelimit.android.data.RoomDatabase/47.json diff --git a/app/build.gradle b/app/build.gradle index 2c7887f..269768c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,7 +23,7 @@ apply plugin: 'com.squareup.wire' android { namespace 'io.timelimit.android' - compileSdkVersion 33 + compileSdkVersion 34 defaultConfig { applicationId "io.timelimit.android" minSdkVersion 21 diff --git a/app/schemas/io.timelimit.android.data.RoomDatabase/47.json b/app/schemas/io.timelimit.android.data.RoomDatabase/47.json new file mode 100644 index 0000000..76c2b45 --- /dev/null +++ b/app/schemas/io.timelimit.android.data.RoomDatabase/47.json @@ -0,0 +1,1773 @@ +{ + "formatVersion": 1, + "database": { + "version": 47, + "identityHash": "b2fe1fa00b58f93b2b039e5e9ffb329c", + "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, `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 + }, + { + "fieldPath": "obsoleteBlockedTimes", + "columnName": "blocked_times", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "flags", + "columnName": "flags", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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, `manipulation_flags` INTEGER NOT NULL, `platform_type` TEXT, `platform_level` INTEGER NOT NULL DEFAULT 0, 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 + }, + { + "fieldPath": "manipulationFlags", + "columnName": "manipulation_flags", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "platformType", + "columnName": "platform_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "platformLevel", + "columnName": "platform_level", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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": { + "autoGenerate": false, + "columnNames": [ + "device_id", + "package_name" + ] + }, + "indices": [ + { + "name": "index_app_device_id", + "unique": false, + "columnNames": [ + "device_id" + ], + "orders": [], + "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" + ], + "orders": [], + "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": "appSpecifierString", + "columnName": "package_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "category_id", + "package_name" + ] + }, + "indices": [ + { + "name": "index_category_app_category_id", + "unique": false, + "columnNames": [ + "category_id" + ], + "orders": [], + "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" + ], + "orders": [], + "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, `extra_time_day` 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, `tasks_version` TEXT NOT NULL DEFAULT '', `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, `disable_limits_until` INTEGER NOT NULL, `flags` INTEGER NOT NULL DEFAULT 0, `block_notification_delay` INTEGER NOT NULL DEFAULT 0, 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": "extraTimeDay", + "columnName": "extra_time_day", + "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": "tasksVersion", + "columnName": "tasks_version", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "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 + }, + { + "fieldPath": "disableLimitsUntil", + "columnName": "disable_limits_until", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flags", + "columnName": "flags", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "blockNotificationDelay", + "columnName": "block_notification_delay", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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, `start_time_of_day` INTEGER NOT NULL, `end_time_of_day` INTEGER NOT NULL, PRIMARY KEY(`category_id`, `day_of_epoch`, `start_time_of_day`, `end_time_of_day`))", + "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 + }, + { + "fieldPath": "startTimeOfDay", + "columnName": "start_time_of_day", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endTimeOfDay", + "columnName": "end_time_of_day", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "category_id", + "day_of_epoch", + "start_time_of_day", + "end_time_of_day" + ] + }, + "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, `start_minute_of_day` INTEGER NOT NULL, `end_minute_of_day` INTEGER NOT NULL, `session_duration_milliseconds` INTEGER NOT NULL, `session_pause_milliseconds` INTEGER NOT NULL, `per_day` 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 + }, + { + "fieldPath": "startMinuteOfDay", + "columnName": "start_minute_of_day", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endMinuteOfDay", + "columnName": "end_minute_of_day", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sessionDurationMilliseconds", + "columnName": "session_duration_milliseconds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sessionPauseMilliseconds", + "columnName": "session_pause_milliseconds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "perDay", + "columnName": "per_day", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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": { + "autoGenerate": false, + "columnNames": [ + "device_id", + "package_name" + ] + }, + "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": { + "autoGenerate": false, + "columnNames": [ + "sequence_number" + ] + }, + "indices": [ + { + "name": "index_pending_sync_action_scheduled_for_upload", + "unique": false, + "columnNames": [ + "scheduled_for_upload" + ], + "orders": [], + "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": { + "autoGenerate": false, + "columnNames": [ + "device_id", + "app_package_name", + "activity_class_name" + ] + }, + "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": { + "autoGenerate": false, + "columnNames": [ + "type", + "id" + ] + }, + "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": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "user_key", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_id` TEXT NOT NULL, `key` BLOB NOT NULL, `last_use` INTEGER NOT NULL, PRIMARY KEY(`user_id`), FOREIGN KEY(`user_id`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "publicKey", + "columnName": "key", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "lastUse", + "columnName": "last_use", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "user_id" + ] + }, + "indices": [ + { + "name": "index_user_key_key", + "unique": true, + "columnNames": [ + "key" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_key_key` ON `${TABLE_NAME}` (`key`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "user_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "session_duration", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`category_id` TEXT NOT NULL, `max_session_duration` INTEGER NOT NULL, `session_pause_duration` INTEGER NOT NULL, `start_minute_of_day` INTEGER NOT NULL, `end_minute_of_day` INTEGER NOT NULL, `last_usage` INTEGER NOT NULL, `last_session_duration` INTEGER NOT NULL, PRIMARY KEY(`category_id`, `max_session_duration`, `session_pause_duration`, `start_minute_of_day`, `end_minute_of_day`), FOREIGN KEY(`category_id`) REFERENCES `category`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "maxSessionDuration", + "columnName": "max_session_duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sessionPauseDuration", + "columnName": "session_pause_duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "startMinuteOfDay", + "columnName": "start_minute_of_day", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endMinuteOfDay", + "columnName": "end_minute_of_day", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUsage", + "columnName": "last_usage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSessionDuration", + "columnName": "last_session_duration", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "category_id", + "max_session_duration", + "session_pause_duration", + "start_minute_of_day", + "end_minute_of_day" + ] + }, + "indices": [ + { + "name": "session_duration_index_category_id", + "unique": false, + "columnNames": [ + "category_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `session_duration_index_category_id` ON `${TABLE_NAME}` (`category_id`)" + } + ], + "foreignKeys": [ + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "category_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_limit_login_category", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_id` TEXT NOT NULL, `category_id` TEXT NOT NULL, `pre_block_duration` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`user_id`), FOREIGN KEY(`user_id`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`category_id`) REFERENCES `category`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "preBlockDuration", + "columnName": "pre_block_duration", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "user_id" + ] + }, + "indices": [ + { + "name": "user_limit_login_category_index_category_id", + "unique": false, + "columnNames": [ + "category_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `user_limit_login_category_index_category_id` ON `${TABLE_NAME}` (`category_id`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "user_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "category_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "category_network_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`category_id` TEXT NOT NULL, `network_item_id` TEXT NOT NULL, `hashed_network_id` TEXT NOT NULL, PRIMARY KEY(`category_id`, `network_item_id`), FOREIGN KEY(`category_id`) REFERENCES `category`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "networkItemId", + "columnName": "network_item_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hashedNetworkId", + "columnName": "hashed_network_id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "category_id", + "network_item_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "category_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "child_task", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`task_id` TEXT NOT NULL, `category_id` TEXT NOT NULL, `task_title` TEXT NOT NULL, `extra_time_duration` INTEGER NOT NULL, `pending_request` INTEGER NOT NULL, `last_grant_timestamp` INTEGER NOT NULL, PRIMARY KEY(`task_id`), FOREIGN KEY(`category_id`) REFERENCES `category`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "taskId", + "columnName": "task_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "taskTitle", + "columnName": "task_title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extraTimeDuration", + "columnName": "extra_time_duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pendingRequest", + "columnName": "pending_request", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastGrantTimestamp", + "columnName": "last_grant_timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "task_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "category_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "category_time_warning", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`category_id` TEXT NOT NULL, `minutes` INTEGER NOT NULL, PRIMARY KEY(`category_id`, `minutes`), FOREIGN KEY(`category_id`) REFERENCES `category`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "minutes", + "columnName": "minutes", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "category_id", + "minutes" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "category_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "crypt_container_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crypt_container_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `device_id` TEXT, `category_id` TEXT, `type` INTEGER NOT NULL, `server_version` TEXT NOT NULL, `current_generation` INTEGER NOT NULL, `current_generation_first_timestamp` INTEGER NOT NULL, `next_counter` INTEGER NOT NULL, `current_generation_key` BLOB, `status` INTEGER NOT NULL, FOREIGN KEY(`device_id`) REFERENCES `device`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`category_id`) REFERENCES `category`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "cryptContainerId", + "columnName": "crypt_container_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serverVersion", + "columnName": "server_version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currentGeneration", + "columnName": "current_generation", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "currentGenerationFirstTimestamp", + "columnName": "current_generation_first_timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nextCounter", + "columnName": "next_counter", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "currentGenerationKey", + "columnName": "current_generation_key", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "crypt_container_id" + ] + }, + "indices": [ + { + "name": "index_crypt_container_metadata_device_id", + "unique": false, + "columnNames": [ + "device_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_crypt_container_metadata_device_id` ON `${TABLE_NAME}` (`device_id`)" + }, + { + "name": "index_crypt_container_metadata_category_id", + "unique": false, + "columnNames": [ + "category_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_crypt_container_metadata_category_id` ON `${TABLE_NAME}` (`category_id`)" + } + ], + "foreignKeys": [ + { + "table": "device", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "device_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "category_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "crypt_container_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crypt_container_id` INTEGER NOT NULL, `encrypted_data` BLOB NOT NULL, PRIMARY KEY(`crypt_container_id`), FOREIGN KEY(`crypt_container_id`) REFERENCES `crypt_container_metadata`(`crypt_container_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "cryptContainerId", + "columnName": "crypt_container_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "encryptedData", + "columnName": "encrypted_data", + "affinity": "BLOB", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "crypt_container_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "crypt_container_metadata", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "crypt_container_id" + ], + "referencedColumns": [ + "crypt_container_id" + ] + } + ] + }, + { + "tableName": "crypt_container_pending_key_request", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crypt_container_id` INTEGER NOT NULL, `request_time_crypt_container_generation` INTEGER NOT NULL, `request_sequence_id` INTEGER NOT NULL, `request_key` BLOB NOT NULL, PRIMARY KEY(`crypt_container_id`), FOREIGN KEY(`crypt_container_id`) REFERENCES `crypt_container_metadata`(`crypt_container_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "cryptContainerId", + "columnName": "crypt_container_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "requestTimeCryptContainerGeneration", + "columnName": "request_time_crypt_container_generation", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "requestSequenceId", + "columnName": "request_sequence_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "requestKey", + "columnName": "request_key", + "affinity": "BLOB", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "crypt_container_id" + ] + }, + "indices": [ + { + "name": "index_crypt_container_pending_key_request_request_sequence_id", + "unique": true, + "columnNames": [ + "request_sequence_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_crypt_container_pending_key_request_request_sequence_id` ON `${TABLE_NAME}` (`request_sequence_id`)" + } + ], + "foreignKeys": [ + { + "table": "crypt_container_metadata", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "crypt_container_id" + ], + "referencedColumns": [ + "crypt_container_id" + ] + } + ] + }, + { + "tableName": "crypt_container_key_result", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`request_sequence_id` INTEGER NOT NULL, `device_id` TEXT NOT NULL, `status` INTEGER NOT NULL, PRIMARY KEY(`request_sequence_id`, `device_id`), FOREIGN KEY(`request_sequence_id`) REFERENCES `crypt_container_pending_key_request`(`request_sequence_id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`device_id`) REFERENCES `device`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "requestSequenceId", + "columnName": "request_sequence_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "request_sequence_id", + "device_id" + ] + }, + "indices": [ + { + "name": "index_crypt_container_key_result_request_sequence_id", + "unique": false, + "columnNames": [ + "request_sequence_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_crypt_container_key_result_request_sequence_id` ON `${TABLE_NAME}` (`request_sequence_id`)" + }, + { + "name": "index_crypt_container_key_result_device_id", + "unique": false, + "columnNames": [ + "device_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_crypt_container_key_result_device_id` ON `${TABLE_NAME}` (`device_id`)" + } + ], + "foreignKeys": [ + { + "table": "crypt_container_pending_key_request", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "request_sequence_id" + ], + "referencedColumns": [ + "request_sequence_id" + ] + }, + { + "table": "device", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "device_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "device_public_key", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`device_id` TEXT NOT NULL, `public_key` BLOB NOT NULL, `next_sequence_number` INTEGER NOT NULL, PRIMARY KEY(`device_id`), FOREIGN KEY(`device_id`) REFERENCES `device`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "publicKey", + "columnName": "public_key", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "nextSequenceNumber", + "columnName": "next_sequence_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "device_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "device", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "device_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_u2f_key", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `user_id` TEXT NOT NULL, `added_at` INTEGER NOT NULL, `key_handle` BLOB NOT NULL, `public_key` BLOB NOT NULL, `next_counter` INTEGER NOT NULL, FOREIGN KEY(`user_id`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "keyId", + "columnName": "key_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "addedAt", + "columnName": "added_at", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keyHandle", + "columnName": "key_handle", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "publicKey", + "columnName": "public_key", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "nextCounter", + "columnName": "next_counter", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "key_id" + ] + }, + "indices": [ + { + "name": "index_user_u2f_key_key_handle_public_key", + "unique": true, + "columnNames": [ + "key_handle", + "public_key" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_u2f_key_key_handle_public_key` ON `${TABLE_NAME}` (`key_handle`, `public_key`)" + }, + { + "name": "index_user_u2f_key_user_id", + "unique": false, + "columnNames": [ + "user_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_u2f_key_user_id` ON `${TABLE_NAME}` (`user_id`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "user_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "widget_category", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`widget_id` INTEGER NOT NULL, `category_id` TEXT NOT NULL, PRIMARY KEY(`widget_id`, `category_id`), FOREIGN KEY(`category_id`) REFERENCES `category`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "widgetId", + "columnName": "widget_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "widget_id", + "category_id" + ] + }, + "indices": [ + { + "name": "index_widget_category_category_id", + "unique": false, + "columnNames": [ + "category_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_widget_category_category_id` ON `${TABLE_NAME}` (`category_id`)" + } + ], + "foreignKeys": [ + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "category_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "widget_config", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`widget_id` INTEGER NOT NULL, `translucent` INTEGER NOT NULL, PRIMARY KEY(`widget_id`))", + "fields": [ + { + "fieldPath": "widgetId", + "columnName": "widget_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "translucent", + "columnName": "translucent", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "widget_id" + ] + }, + "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, 'b2fe1fa00b58f93b2b039e5e9ffb329c')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/data/RoomDatabase.kt b/app/src/main/java/io/timelimit/android/data/RoomDatabase.kt index 0f35346..e8a11f4 100644 --- a/app/src/main/java/io/timelimit/android/data/RoomDatabase.kt +++ b/app/src/main/java/io/timelimit/android/data/RoomDatabase.kt @@ -17,11 +17,11 @@ package io.timelimit.android.data import android.annotation.SuppressLint import android.content.Context +import androidx.room.AutoMigration import androidx.room.Database import androidx.room.InvalidationTracker import androidx.room.Room import androidx.room.RoomDatabase -import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase import io.timelimit.android.async.Threads import io.timelimit.android.data.dao.DerivedDataDao @@ -61,7 +61,9 @@ import java.util.concurrent.TimeUnit UserU2FKey::class, WidgetCategory::class, WidgetConfig::class -], version = 46) +], version = 47, autoMigrations = [ + AutoMigration(from = 46, to = 47) +]) abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database { companion object { private val lock = Object() diff --git a/app/src/main/java/io/timelimit/android/data/model/Device.kt b/app/src/main/java/io/timelimit/android/data/model/Device.kt index ed135db..2fcc822 100644 --- a/app/src/main/java/io/timelimit/android/data/model/Device.kt +++ b/app/src/main/java/io/timelimit/android/data/model/Device.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2022 Jonas Lochmann + * TimeLimit Copyright 2019 - 2023 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 @@ -94,7 +94,11 @@ data class Device( @ColumnInfo(name = "q_or_later") val qOrLater: Boolean, @ColumnInfo(name = "manipulation_flags") - val manipulationFlags: Long + val manipulationFlags: Long, + @ColumnInfo(name = "platform_type") + val platformType: String?, + @ColumnInfo(name = "platform_level", defaultValue = "0") + val platformLevel: Int ): JsonSerializable { companion object { private const val ID = "id" @@ -129,6 +133,8 @@ data class Device( private const val ENABLE_ACTIVITY_LEVEL_BLOCKING = "ealb" private const val Q_OR_LATER = "qol" private const val MANIPULATION_FLAGS = "mf" + private const val PLATFORM_TYPE = "pt" + private const val PLATFORM_LEVEL = "pl" fun parse(reader: JsonReader): Device { var id: String? = null @@ -163,6 +169,8 @@ data class Device( var enableActivityLevelBlocking = false var qOrLater = false var manipulationFlags = 0L + var platformType: String? = null + var platformLevel = 0 reader.beginObject() @@ -200,6 +208,8 @@ data class Device( ENABLE_ACTIVITY_LEVEL_BLOCKING -> enableActivityLevelBlocking = reader.nextBoolean() Q_OR_LATER -> qOrLater = reader.nextBoolean() MANIPULATION_FLAGS -> manipulationFlags = reader.nextLong() + PLATFORM_TYPE -> platformType = reader.nextString() + PLATFORM_LEVEL -> platformLevel = reader.nextInt() else -> reader.skipValue() } } @@ -238,7 +248,9 @@ data class Device( wasAccessibilityServiceEnabled = wasAccessibilityServiceEnabled, enableActivityLevelBlocking = enableActivityLevelBlocking, qOrLater = qOrLater, - manipulationFlags = manipulationFlags + manipulationFlags = manipulationFlags, + platformType = platformType, + platformLevel = platformLevel ) } } @@ -310,6 +322,8 @@ data class Device( writer.name(ENABLE_ACTIVITY_LEVEL_BLOCKING).value(enableActivityLevelBlocking) writer.name(Q_OR_LATER).value(qOrLater) writer.name(MANIPULATION_FLAGS).value(manipulationFlags) + platformType?.let { writer.name(PLATFORM_TYPE).value(it) } + writer.name(PLATFORM_LEVEL).value(platformLevel) writer.endObject() } @@ -392,4 +406,8 @@ object HadManipulationFlag { object ManipulationFlag { const val USED_FGS_KILLER = 1L shl 0 +} + +object DevicePlatform { + const val ANDROID = "android" } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/logic/AppSetupLogic.kt b/app/src/main/java/io/timelimit/android/logic/AppSetupLogic.kt index 34dfed9..89e02b2 100644 --- a/app/src/main/java/io/timelimit/android/logic/AppSetupLogic.kt +++ b/app/src/main/java/io/timelimit/android/logic/AppSetupLogic.kt @@ -110,7 +110,9 @@ class AppSetupLogic(private val appLogic: AppLogic) { wasAccessibilityServiceEnabled = false, enableActivityLevelBlocking = false, qOrLater = AndroidVersion.qOrLater, - manipulationFlags = 0 + manipulationFlags = 0, + platformType = DevicePlatform.ANDROID, + platformLevel = AndroidVersion.platformLevel ) appLogic.database.device().addDeviceSync(device) diff --git a/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt b/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt index dec9a56..5c3238c 100644 --- a/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt +++ b/app/src/main/java/io/timelimit/android/logic/BackgroundTaskLogic.kt @@ -26,6 +26,7 @@ import io.timelimit.android.coroutines.executeAndWait import io.timelimit.android.coroutines.runAsync import io.timelimit.android.coroutines.runAsyncExpectForever import io.timelimit.android.data.backup.DatabaseBackup +import io.timelimit.android.data.model.DevicePlatform import io.timelimit.android.data.model.ExperimentalFlags import io.timelimit.android.data.model.ManipulationFlag import io.timelimit.android.data.model.UserType @@ -982,6 +983,8 @@ class BackgroundTaskLogic(val appLogic: AppLogic) { val overlayPermission = appLogic.platformIntegration.getDrawOverOtherAppsPermissionStatus(useStrictChecking) val accessibilityService = appLogic.platformIntegration.isAccessibilityServiceEnabled() val qOrLater = AndroidVersion.qOrLater + val platformType = DevicePlatform.ANDROID + val platformLevel = AndroidVersion.platformLevel if (protectionLevel != deviceEntry.currentProtectionLevel) { changes = changes.copy( @@ -1020,6 +1023,14 @@ class BackgroundTaskLogic(val appLogic: AppLogic) { if (qOrLater && !deviceEntry.qOrLater) { changes = changes.copy(isQOrLaterNow = true) } + + if (deviceEntry.platformType != platformType) { + changes = changes.copy(newPlatformType = platformType) + } + + if (deviceEntry.platformLevel != platformLevel) { + changes = changes.copy(newPlatformLevel = platformLevel) + } } return changes diff --git a/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt b/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt index d986495..f7e6104 100644 --- a/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt +++ b/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt @@ -194,7 +194,9 @@ object ApplyServerDataStatus { wasAccessibilityServiceEnabled = newDevice.wasAccessibilityServiceEnabled, enableActivityLevelBlocking = newDevice.enableActivityLevelBlocking, qOrLater = newDevice.qOrLater, - manipulationFlags = newDevice.manipulationFlags + manipulationFlags = newDevice.manipulationFlags, + platformType = newDevice.platformType, + platformLevel = newDevice.platformLevel ?: 0 )) newDeviceTitles.add(newDevice.name) @@ -231,7 +233,9 @@ object ApplyServerDataStatus { wasAccessibilityServiceEnabled = newDevice.wasAccessibilityServiceEnabled, enableActivityLevelBlocking = newDevice.enableActivityLevelBlocking, qOrLater = newDevice.qOrLater, - manipulationFlags = newDevice.manipulationFlags + manipulationFlags = newDevice.manipulationFlags, + platformType = newDevice.platformType ?: oldDeviceEntry.platformType, + platformLevel = newDevice.platformLevel ?: oldDeviceEntry.platformLevel ) if (updatedDeviceEntry != oldDeviceEntry) { diff --git a/app/src/main/java/io/timelimit/android/sync/actions/Actions.kt b/app/src/main/java/io/timelimit/android/sync/actions/Actions.kt index 0fcc031..6d3c092 100644 --- a/app/src/main/java/io/timelimit/android/sync/actions/Actions.kt +++ b/app/src/main/java/io/timelimit/android/sync/actions/Actions.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2022 Jonas Lochmann + * TimeLimit Copyright 2019 - 2023 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 @@ -1220,7 +1220,9 @@ data class UpdateDeviceStatusAction( val newAppVersion: Int?, val didReboot: Boolean, val isQOrLaterNow: Boolean, - val addedManipulationFlags: Long + val addedManipulationFlags: Long, + val newPlatformType: String?, + val newPlatformLevel: Int? ): AppLogicAction() { companion object { const val TYPE_VALUE = "UPDATE_DEVICE_STATUS" @@ -1233,6 +1235,8 @@ data class UpdateDeviceStatusAction( private const val DID_REBOOT = "didReboot" private const val IS_Q_OR_LATER_NOW = "isQOrLaterNow" private const val ADDED_MANIPULATION_FLAGS = "addedManipulationFlags" + private const val PLATFORM_TYPE = "platformType" + private const val PLATFORM_LEVEL = "platformLevel" val empty = UpdateDeviceStatusAction( newProtectionLevel = null, @@ -1243,7 +1247,9 @@ data class UpdateDeviceStatusAction( newAppVersion = null, didReboot = false, isQOrLaterNow = false, - addedManipulationFlags = 0L + addedManipulationFlags = 0L, + newPlatformType = null, + newPlatformLevel = null ) } @@ -1251,6 +1257,14 @@ data class UpdateDeviceStatusAction( if (newAppVersion != null && newAppVersion < 0) { throw IllegalArgumentException() } + + if (newPlatformType != null && newPlatformType.isEmpty()) { + throw IllegalArgumentException() + } + + if (newPlatformLevel != null && newPlatformLevel < 0) { + throw IllegalArgumentException() + } } override fun serialize(writer: JsonWriter) { @@ -1304,6 +1318,14 @@ data class UpdateDeviceStatusAction( writer.name(ADDED_MANIPULATION_FLAGS).value(addedManipulationFlags) } + if (newPlatformType != null) { + writer.name(PLATFORM_TYPE).value(newPlatformType) + } + + if (newPlatformLevel != null) { + writer.name(PLATFORM_LEVEL).value(newPlatformLevel) + } + writer.endObject() } } diff --git a/app/src/main/java/io/timelimit/android/sync/actions/dispatch/AppLogicAction.kt b/app/src/main/java/io/timelimit/android/sync/actions/dispatch/AppLogicAction.kt index e0bcf10..5658e04 100644 --- a/app/src/main/java/io/timelimit/android/sync/actions/dispatch/AppLogicAction.kt +++ b/app/src/main/java/io/timelimit/android/sync/actions/dispatch/AppLogicAction.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2022 Jonas Lochmann + * TimeLimit Copyright 2019 - 2023 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 @@ -305,6 +305,14 @@ object LocalDatabaseAppLogicActionDispatcher { device = device.copy(qOrLater = true) } + if (action.newPlatformType != null) { + device = device.copy(platformType = action.newPlatformType) + } + + if (action.newPlatformLevel != null) { + device = device.copy(platformLevel = action.newPlatformLevel) + } + if (action.addedManipulationFlags != 0L) { device = device.copy(manipulationFlags = device.manipulationFlags or action.addedManipulationFlags) } diff --git a/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt b/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt index 0401c3d..1fa0c39 100644 --- a/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt +++ b/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt @@ -323,7 +323,9 @@ data class ServerDeviceData( val enableActivityLevelBlocking: Boolean, val qOrLater: Boolean, val manipulationFlags: Long, - val publicKey: ByteArray? + val publicKey: ByteArray?, + val platformType: String?, + val platformLevel: Int? ) { companion object { private const val DEVICE_ID = "deviceId" @@ -358,6 +360,8 @@ data class ServerDeviceData( private const val Q_OR_LATER = "qOrLater" private const val MANIPULATION_FLAGS = "mFlags" private const val PUBLIC_KEY = "pk" + private const val PLATFORM_TYPE = "pType" + private const val PLATFORM_LEVEL = "pLevel" fun parse(reader: JsonReader): ServerDeviceData { var deviceId: String? = null @@ -392,6 +396,8 @@ data class ServerDeviceData( var qOrLater = false var manipulationFlags = 0L var publicKey: ByteArray? = null + var platformType: String? = null + var platformLevel: Int? = null reader.beginObject() while (reader.hasNext()) { @@ -428,6 +434,8 @@ data class ServerDeviceData( Q_OR_LATER -> qOrLater = reader.nextBoolean() MANIPULATION_FLAGS -> manipulationFlags = reader.nextLong() PUBLIC_KEY -> publicKey = reader.nextString().parseBase64() + PLATFORM_TYPE -> platformType = reader.nextString() + PLATFORM_LEVEL -> platformLevel = reader.nextInt() else -> reader.skipValue() } } @@ -465,7 +473,9 @@ data class ServerDeviceData( enableActivityLevelBlocking = enableActivityLevelBlocking, qOrLater = qOrLater, manipulationFlags = manipulationFlags, - publicKey = publicKey + publicKey = publicKey, + platformType = platformType, + platformLevel = platformLevel ) } diff --git a/app/src/main/java/io/timelimit/android/util/AndroidVersion.kt b/app/src/main/java/io/timelimit/android/util/AndroidVersion.kt index c858e7b..0f5c51c 100644 --- a/app/src/main/java/io/timelimit/android/util/AndroidVersion.kt +++ b/app/src/main/java/io/timelimit/android/util/AndroidVersion.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 Jonas Lochmann + * TimeLimit Copyright 2019 - 2023 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 @@ -19,4 +19,9 @@ import android.os.Build object AndroidVersion { val qOrLater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + + val platformLevel = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 2 + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) 1 + else 0 } \ No newline at end of file