From 46550588f5130e030d77cb216d88e0f3a787f9ba Mon Sep 17 00:00:00 2001 From: Jonas Lochmann Date: Mon, 15 Aug 2022 02:00:00 +0200 Subject: [PATCH] Improve the widget --- .../45.json | 1734 +++++++++++++++++ app/src/main/AndroidManifest.xml | 8 + .../io/timelimit/android/data/Database.kt | 1 + .../io/timelimit/android/data/Migrations.kt | 10 +- .../io/timelimit/android/data/RoomDatabase.kt | 5 +- .../android/data/dao/WidgetCategoryDao.kt | 47 + .../android/data/model/WidgetCategory.kt | 40 + .../android/BackgroundActionService.kt | 11 +- .../platform/android/BackgroundService.kt | 10 +- .../io/timelimit/android/logic/AppLogic.kt | 2 + .../android/ui/widget/TimesWidgetConfig.kt | 24 + .../android/ui/widget/TimesWidgetContent.kt | 32 + .../ui/widget/TimesWidgetContentLoader.kt | 135 ++ .../android/ui/widget/TimesWidgetItem.kt | 12 +- .../android/ui/widget/TimesWidgetItems.kt | 112 +- .../android/ui/widget/TimesWidgetProvider.kt | 69 +- .../android/ui/widget/TimesWidgetService.kt | 149 +- .../config/UnconfiguredDialogFragment.kt | 51 + .../ui/widget/config/WidgetConfigActivity.kt | 78 + .../WidgetConfigFilterDialogFragment.kt | 88 + .../config/WidgetConfigModeDialogFragment.kt | 77 + .../ui/widget/config/WidgetConfigModel.kt | 165 ++ .../main/res/drawable-de/widget_preview.png | Bin 36644 -> 24966 bytes app/src/main/res/drawable/widget_preview.png | Bin 37093 -> 23610 bytes app/src/main/res/layout/widget_times.xml | 36 +- .../main/res/layout/widget_times_button.xml | 33 + ...tem.xml => widget_times_category_item.xml} | 15 +- .../main/res/layout/widget_times_preview.xml | 58 + app/src/main/res/values-de/strings.xml | 12 +- app/src/main/res/values-night/colors.xml | 5 +- app/src/main/res/values/colors.xml | 5 +- app/src/main/res/values/strings.xml | 12 +- app/src/main/res/xml/times_widget_info.xml | 24 +- 33 files changed, 2843 insertions(+), 217 deletions(-) create mode 100644 app/schemas/io.timelimit.android.data.RoomDatabase/45.json create mode 100644 app/src/main/java/io/timelimit/android/data/dao/WidgetCategoryDao.kt create mode 100644 app/src/main/java/io/timelimit/android/data/model/WidgetCategory.kt create mode 100644 app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetConfig.kt create mode 100644 app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetContent.kt create mode 100644 app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetContentLoader.kt create mode 100644 app/src/main/java/io/timelimit/android/ui/widget/config/UnconfiguredDialogFragment.kt create mode 100644 app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigActivity.kt create mode 100644 app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigFilterDialogFragment.kt create mode 100644 app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigModeDialogFragment.kt create mode 100644 app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigModel.kt create mode 100644 app/src/main/res/layout/widget_times_button.xml rename app/src/main/res/layout/{widget_times_item.xml => widget_times_category_item.xml} (87%) create mode 100644 app/src/main/res/layout/widget_times_preview.xml diff --git a/app/schemas/io.timelimit.android.data.RoomDatabase/45.json b/app/schemas/io.timelimit.android.data.RoomDatabase/45.json new file mode 100644 index 0000000..fb0185c --- /dev/null +++ b/app/schemas/io.timelimit.android.data.RoomDatabase/45.json @@ -0,0 +1,1734 @@ +{ + "formatVersion": 1, + "database": { + "version": 45, + "identityHash": "cd8c9703a2467f95e13b2e79b1117bde", + "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": { + "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, `manipulation_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": "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 + } + ], + "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" + ], + "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": { + "columnNames": [ + "category_id", + "package_name" + ], + "autoGenerate": false + }, + "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": { + "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, `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": { + "columnNames": [ + "category_id", + "day_of_epoch", + "start_time_of_day", + "end_time_of_day" + ], + "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, `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": { + "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" + ], + "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": { + "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": [] + }, + { + "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": { + "columnNames": [ + "user_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "category_id", + "max_session_duration", + "session_pause_duration", + "start_minute_of_day", + "end_minute_of_day" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "user_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "category_id", + "network_item_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "task_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "category_id", + "minutes" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "crypt_container_id" + ], + "autoGenerate": true + }, + "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": { + "columnNames": [ + "crypt_container_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "crypt_container_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "request_sequence_id", + "device_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "device_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "key_id" + ], + "autoGenerate": true + }, + "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": { + "columnNames": [ + "widget_id", + "category_id" + ], + "autoGenerate": false + }, + "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" + ] + } + ] + } + ], + "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, 'cd8c9703a2467f95e13b2e79b1117bde')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5f31d7c..cae3804 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -112,6 +112,14 @@ android:taskAffinity=":update" android:launchMode="singleTop" /> + + + + + + diff --git a/app/src/main/java/io/timelimit/android/data/Database.kt b/app/src/main/java/io/timelimit/android/data/Database.kt index e7544c9..c786f51 100644 --- a/app/src/main/java/io/timelimit/android/data/Database.kt +++ b/app/src/main/java/io/timelimit/android/data/Database.kt @@ -46,6 +46,7 @@ interface Database { fun cryptContainerKeyResult(): CryptContainerKeyResultDao fun deviceKey(): DeviceKeyDao fun u2f(): U2FDao + fun widgetCategory(): WidgetCategoryDao fun runInTransaction(block: () -> T): T fun runInUnobservedTransaction(block: () -> T): T diff --git a/app/src/main/java/io/timelimit/android/data/Migrations.kt b/app/src/main/java/io/timelimit/android/data/Migrations.kt index 695c618..4419806 100644 --- a/app/src/main/java/io/timelimit/android/data/Migrations.kt +++ b/app/src/main/java/io/timelimit/android/data/Migrations.kt @@ -326,6 +326,13 @@ object DatabaseMigrations { } } + val MIGRATE_TO_V45 = object: Migration(44, 45) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE IF NOT EXISTS `widget_category` (`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 )") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_widget_category_category_id` ON `widget_category` (`category_id`)") + } + } + val ALL = arrayOf( MIGRATE_TO_V2, MIGRATE_TO_V3, @@ -369,6 +376,7 @@ object DatabaseMigrations { MIGRATE_TO_V41, MIGRATE_TO_V42, MIGRATE_TP_V43, - MIGRATE_TO_V44 + MIGRATE_TO_V44, + MIGRATE_TO_V45 ) } 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 99b2acc..0e24449 100644 --- a/app/src/main/java/io/timelimit/android/data/RoomDatabase.kt +++ b/app/src/main/java/io/timelimit/android/data/RoomDatabase.kt @@ -58,8 +58,9 @@ import java.util.concurrent.TimeUnit CryptContainerPendingKeyRequest::class, CryptContainerKeyResult::class, DevicePublicKey::class, - UserU2FKey::class -], version = 44) + UserU2FKey::class, + WidgetCategory::class +], version = 45) 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/dao/WidgetCategoryDao.kt b/app/src/main/java/io/timelimit/android/data/dao/WidgetCategoryDao.kt new file mode 100644 index 0000000..b54f0e3 --- /dev/null +++ b/app/src/main/java/io/timelimit/android/data/dao/WidgetCategoryDao.kt @@ -0,0 +1,47 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.data.dao + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import io.timelimit.android.data.model.WidgetCategory + +@Dao +interface WidgetCategoryDao { + @Query("DELETE FROM widget_category WHERE widget_id = :widgetId") + fun deleteByWidgetId(widgetId: Int) + + @Query("DELETE FROM widget_category WHERE widget_id IN (:widgetIds)") + fun deleteByWidgetIds(widgetIds: IntArray) + + @Query("DELETE FROM widget_category WHERE widget_id = :widgetId AND category_id IN (:categoryIds)") + fun deleteByWidgetIdAndCategoryIds(widgetId: Int, categoryIds: List) + + @Query("DELETE FROM widget_category") + fun deleteAll() + + @Query("SELECT * FROM widget_category") + fun queryLive(): LiveData> + + @Query("SELECT category_id FROM widget_category WHERE widget_id = :widgetId") + fun queryByWidgetIdSync(widgetId: Int): List + + @Insert(onConflict = OnConflictStrategy.IGNORE) + fun insert(items: List) +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/data/model/WidgetCategory.kt b/app/src/main/java/io/timelimit/android/data/model/WidgetCategory.kt new file mode 100644 index 0000000..58bd3f5 --- /dev/null +++ b/app/src/main/java/io/timelimit/android/data/model/WidgetCategory.kt @@ -0,0 +1,40 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.data.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey + +@Entity( + tableName = "widget_category", + primaryKeys = ["widget_id", "category_id"], + foreignKeys = [ + ForeignKey( + entity = Category::class, + parentColumns = ["id"], + childColumns = ["category_id"], + onUpdate = ForeignKey.CASCADE, + onDelete = ForeignKey.CASCADE + ) + ] +) +data class WidgetCategory ( + @ColumnInfo(name = "widget_id") + val widgetId: Int, + @ColumnInfo(name = "category_id", index = true) + val categoryId: String +) \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundActionService.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundActionService.kt index d458f22..df56620 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundActionService.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundActionService.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 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 @@ -44,8 +44,13 @@ class BackgroundActionService: Service() { fun prepareRevokeTemporarilyAllowed(context: Context) = Intent(context, BackgroundActionService::class.java) .putExtra(ACTION, ACTION_REVOKE_TEMPORARILY_ALLOWED_APPS) - fun prepareSwitchToDefaultUser(context: Context) = Intent(context, BackgroundActionService::class.java) - .putExtra(ACTION, ACTION_SWITCH_TO_DEFAULT_USER) + fun getSwitchToDefaultUserIntent(context: Context) = PendingIntent.getService( + context, + PendingIntentIds.SWITCH_TO_DEFAULT_USER, + Intent(context, BackgroundActionService::class.java) + .putExtra(ACTION, ACTION_SWITCH_TO_DEFAULT_USER), + PendingIntentIds.PENDING_INTENT_FLAGS + ) fun prepareDismissNotification(context: Context, type: Int, id: String) = Intent(context, BackgroundActionService::class.java) .putExtra(ACTION, ACTION_DISMISS_NOTIFICATION) diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt index 80cbd52..63b6d8d 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2020 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,6 @@ package io.timelimit.android.integration.platform.android import android.app.ActivityManager import android.app.NotificationManager -import android.app.PendingIntent import android.app.Service import android.content.Context import android.content.Intent @@ -89,12 +88,7 @@ class BackgroundService: Service() { NotificationCompat.Action.Builder( R.drawable.ic_account_circle_black_24dp, context.getString(R.string.manage_device_default_user_switch_btn), - PendingIntent.getService( - context, - PendingIntentIds.SWITCH_TO_DEFAULT_USER, - BackgroundActionService.prepareSwitchToDefaultUser(context), - PendingIntentIds.PENDING_INTENT_FLAGS - ) + BackgroundActionService.getSwitchToDefaultUserIntent(context) ).build() ) } diff --git a/app/src/main/java/io/timelimit/android/logic/AppLogic.kt b/app/src/main/java/io/timelimit/android/logic/AppLogic.kt index c4f76d4..e98c472 100644 --- a/app/src/main/java/io/timelimit/android/logic/AppLogic.kt +++ b/app/src/main/java/io/timelimit/android/logic/AppLogic.kt @@ -30,6 +30,7 @@ import io.timelimit.android.sync.SyncUtil import io.timelimit.android.sync.network.api.ServerApi import io.timelimit.android.sync.websocket.NetworkStatusInterface import io.timelimit.android.sync.websocket.WebsocketClientCreator +import io.timelimit.android.ui.widget.TimesWidgetProvider class AppLogic( val platformIntegration: PlatformIntegration, @@ -98,6 +99,7 @@ class AppLogic( init { WatchdogLogic(this) + TimesWidgetProvider.triggerUpdates(context) } val suspendAppsLogic = SuspendAppsLogic(this) diff --git a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetConfig.kt b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetConfig.kt new file mode 100644 index 0000000..02685e4 --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetConfig.kt @@ -0,0 +1,24 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.ui.widget + +import io.timelimit.android.data.model.WidgetCategory + +data class TimesWidgetConfig (val categories: List) { + val widgetCategoriesByWidgetId by lazy { + categories.groupBy { it.widgetId }.mapValues { it.value.map { it.categoryId }.toSet() } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetContent.kt b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetContent.kt new file mode 100644 index 0000000..3e64f3b --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetContent.kt @@ -0,0 +1,32 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.ui.widget + +sealed class TimesWidgetContent { + object UnconfiguredDevice: TimesWidgetContent() + data class NoChildUser(val canSwitchToDefaultUser: Boolean): TimesWidgetContent() + data class Categories( + val categories: List, + val canSwitchToDefaultUser: Boolean + ): TimesWidgetContent() { + data class Item( + val categoryId: String, + val categoryName: String, + val level: Int, + val remainingTimeToday: Long? + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetContentLoader.kt b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetContentLoader.kt new file mode 100644 index 0000000..dd27153 --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetContentLoader.kt @@ -0,0 +1,135 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.ui.widget + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import io.timelimit.android.async.Threads +import io.timelimit.android.data.extensions.sortedCategories +import io.timelimit.android.data.model.UserType +import io.timelimit.android.livedata.ignoreUnchanged +import io.timelimit.android.logic.AppLogic +import io.timelimit.android.logic.RealTime +import io.timelimit.android.logic.blockingreason.CategoryHandlingCache + +object TimesWidgetContentLoader { + fun with(logic: AppLogic): LiveData { + val database = logic.database + val realTimeLogic = logic.realTimeLogic + val realTime = RealTime.newInstance() + val categoryHandlingCache = CategoryHandlingCache() + val handler = Threads.mainThreadHandler + + val deviceAndUserRelatedDataLive = database.derivedDataDao().getUserAndDeviceRelatedDataLive() + var deviceAndUserRelatedDataLiveLoaded = false + + val batteryStatusLive = logic.platformIntegration.getBatteryStatusLive() + + lateinit var timeModificationListener: () -> Unit + lateinit var updateByClockRunnable: Runnable + var isActive = false + + val newResult = object: MediatorLiveData() { + override fun onActive() { + super.onActive() + + isActive = true + + realTimeLogic.registerTimeModificationListener(timeModificationListener) + + // ensure that the next update gets scheduled + updateByClockRunnable.run() + } + + override fun onInactive() { + super.onInactive() + + isActive = true + + realTimeLogic.unregisterTimeModificationListener(timeModificationListener) + handler.removeCallbacks(updateByClockRunnable) + } + } + + fun update() { + handler.removeCallbacks(updateByClockRunnable) + + if (!deviceAndUserRelatedDataLiveLoaded) { return } + + val deviceAndUserRelatedData = deviceAndUserRelatedDataLive.value + val userRelatedData = deviceAndUserRelatedData?.userRelatedData + val canSwitchToDefaultUser = deviceAndUserRelatedData?.deviceRelatedData?.canSwitchToDefaultUser ?: false + + if (deviceAndUserRelatedData == null) { + newResult.value = TimesWidgetContent.UnconfiguredDevice + + return + } else if (userRelatedData?.user?.type != UserType.Child) { + newResult.value = TimesWidgetContent.NoChildUser( + canSwitchToDefaultUser = canSwitchToDefaultUser + ) + + return + } + + realTimeLogic.getRealTime(realTime) + + categoryHandlingCache.reportStatus( + user = userRelatedData, + timeInMillis = realTime.timeInMillis, + batteryStatus = logic.platformIntegration.getBatteryStatus(), + shouldTrustTimeTemporarily = realTime.shouldTrustTimeTemporarily, + assumeCurrentDevice = true, + currentNetworkId = null, // not relevant here + hasPremiumOrLocalMode = false // not relevant here + ) + + var maxTime = Long.MAX_VALUE + + val categories = userRelatedData.sortedCategories().map { (level, category) -> + val handling = categoryHandlingCache.get(categoryId = category.category.id) + + maxTime = maxTime.coerceAtMost(handling.dependsOnMaxTime) + + TimesWidgetContent.Categories.Item( + categoryId = category.category.id, + categoryName = category.category.title, + level = level, + remainingTimeToday = handling.remainingTime?.includingExtraTime + ) + } + + newResult.value = TimesWidgetContent.Categories( + categories = categories, + canSwitchToDefaultUser = canSwitchToDefaultUser + ) + + if (isActive && maxTime != Long.MAX_VALUE) { + val delay = maxTime - realTime.timeInMillis + + handler.postDelayed(updateByClockRunnable, delay) + } + } + + timeModificationListener = { update() } + updateByClockRunnable = Runnable { update() } + + newResult.addSource(deviceAndUserRelatedDataLive) { deviceAndUserRelatedDataLiveLoaded = true; update() } + newResult.addSource(batteryStatusLive) { update() } + + return newResult.ignoreUnchanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetItem.kt b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetItem.kt index 47ce81b..985c5a4 100644 --- a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetItem.kt +++ b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetItem.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2020 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 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 @@ -15,8 +15,8 @@ */ package io.timelimit.android.ui.widget -data class TimesWidgetItem( - val title: String, - val level: Int, - val remainingTimeToday: Long? -) \ No newline at end of file +sealed class TimesWidgetItem { + data class TextMessage(val textRessourceId: Int): TimesWidgetItem() + data class Category(val category: TimesWidgetContent.Categories.Item): TimesWidgetItem() + object DefaultUserButton: TimesWidgetItem() +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetItems.kt b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetItems.kt index dc78ac8..4ff43bd 100644 --- a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetItems.kt +++ b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetItems.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2020 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 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 @@ -15,107 +15,31 @@ */ package io.timelimit.android.ui.widget -import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData -import io.timelimit.android.async.Threads -import io.timelimit.android.data.extensions.sortedCategories -import io.timelimit.android.livedata.ignoreUnchanged -import io.timelimit.android.logic.AppLogic -import io.timelimit.android.logic.RealTime -import io.timelimit.android.logic.blockingreason.CategoryHandlingCache +import io.timelimit.android.R object TimesWidgetItems { - fun with(logic: AppLogic): LiveData> { - val database = logic.database - val realTimeLogic = logic.realTimeLogic - val categoryHandlingCache = CategoryHandlingCache() - val realTime = RealTime.newInstance() - val handler = Threads.mainThreadHandler + fun with(content: TimesWidgetContent, config: TimesWidgetConfig, appWidgetId: Int): List = when (content) { + is TimesWidgetContent.UnconfiguredDevice -> listOf(TimesWidgetItem.TextMessage(R.string.widget_msg_unconfigured)) + is TimesWidgetContent.NoChildUser -> { + val base = TimesWidgetItem.TextMessage(R.string.widget_msg_no_child) - val deviceAndUserRelatedDataLive = database.derivedDataDao().getUserAndDeviceRelatedDataLive() - var deviceAndUserRelatedDataLiveLoaded = false - - val batteryStatusLive = logic.platformIntegration.getBatteryStatusLive() - - lateinit var timeModificationListener: () -> Unit - lateinit var updateByClockRunnable: Runnable - var isActive = false - - val newResult = object: MediatorLiveData>() { - override fun onActive() { - super.onActive() - - isActive = true - - realTimeLogic.registerTimeModificationListener(timeModificationListener) - - // ensure that the next update gets scheduled - updateByClockRunnable.run() - } - - override fun onInactive() { - super.onInactive() - - isActive = true - - realTimeLogic.unregisterTimeModificationListener(timeModificationListener) - handler.removeCallbacks(updateByClockRunnable) - } + if (content.canSwitchToDefaultUser) listOf(base, TimesWidgetItem.DefaultUserButton) + else listOf(base) } + is TimesWidgetContent.Categories -> { + val categoryFilter = config.widgetCategoriesByWidgetId[appWidgetId] ?: emptySet() - fun update() { - handler.removeCallbacks(updateByClockRunnable) + val categoryItems = if (content.categories.isEmpty()) listOf(TimesWidgetItem.TextMessage(R.string.widget_msg_no_category)) + else if (categoryFilter.isEmpty()) content.categories.map { TimesWidgetItem.Category(it) } + else { + val filteredCategories = content.categories.filter { categoryFilter.contains(it.categoryId) } - if (!deviceAndUserRelatedDataLiveLoaded) { return } - - val deviceAndUserRelatedData = deviceAndUserRelatedDataLive.value - val userRelatedData = deviceAndUserRelatedData?.userRelatedData - - if (userRelatedData == null) { - newResult.value = emptyList(); return + if (filteredCategories.isEmpty()) listOf(TimesWidgetItem.TextMessage(R.string.widget_msg_no_filtered_category)) + else filteredCategories.map { TimesWidgetItem.Category(it) } } - realTimeLogic.getRealTime(realTime) - - categoryHandlingCache.reportStatus( - user = userRelatedData, - timeInMillis = realTime.timeInMillis, - batteryStatus = logic.platformIntegration.getBatteryStatus(), - shouldTrustTimeTemporarily = realTime.shouldTrustTimeTemporarily, - assumeCurrentDevice = true, - currentNetworkId = null, // not relevant here - hasPremiumOrLocalMode = false // not relevant here - ) - - var maxTime = Long.MAX_VALUE - - val list = userRelatedData.sortedCategories().map { (level, category) -> - val handling = categoryHandlingCache.get(categoryId = category.category.id) - - maxTime = maxTime.coerceAtMost(handling.dependsOnMaxTime) - - TimesWidgetItem( - title = category.category.title, - level = level, - remainingTimeToday = handling.remainingTime?.includingExtraTime - ) - } - - newResult.value = list - - if (isActive && maxTime != Long.MAX_VALUE) { - val delay = maxTime - realTime.timeInMillis - - handler.postDelayed(updateByClockRunnable, delay) - } + if (content.canSwitchToDefaultUser) categoryItems + TimesWidgetItem.DefaultUserButton + else categoryItems } - - timeModificationListener = { update() } - updateByClockRunnable = Runnable { update() } - - newResult.addSource(deviceAndUserRelatedDataLive) { deviceAndUserRelatedDataLiveLoaded = true; update() } - newResult.addSource(batteryStatusLive) { update() } - - return newResult.ignoreUnchanged() } } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetProvider.kt b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetProvider.kt index 53f1811..a1656a6 100644 --- a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetProvider.kt +++ b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetProvider.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2020 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,13 +17,45 @@ package io.timelimit.android.ui.widget import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProvider +import android.content.ComponentName import android.content.Context import android.content.Intent +import android.util.Log import android.widget.RemoteViews +import androidx.core.content.getSystemService +import io.timelimit.android.BuildConfig import io.timelimit.android.R +import io.timelimit.android.async.Threads +import io.timelimit.android.integration.platform.android.BackgroundActionService import io.timelimit.android.logic.DefaultAppLogic class TimesWidgetProvider: AppWidgetProvider() { + companion object { + private const val LOG_TAG = "TimesWidgetProvider" + + private fun handleUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { + for (appWidgetId in appWidgetIds) { + val views = RemoteViews(context.packageName, R.layout.widget_times) + + views.setRemoteAdapter(android.R.id.list, TimesWidgetService.intent(context, appWidgetId)) + views.setPendingIntentTemplate(android.R.id.list, BackgroundActionService.getSwitchToDefaultUserIntent(context)) + views.setEmptyView(android.R.id.list, android.R.id.empty) + + appWidgetManager.updateAppWidget(appWidgetId, views) + } + + TimesWidgetService.notifyContentChanges(context) + } + + fun triggerUpdates(context: Context) { + context.getSystemService()?.also { appWidgetManager -> + val appWidgetIds = appWidgetManager.getAppWidgetIds(ComponentName(context, TimesWidgetProvider::class.java)) + + handleUpdate(context, appWidgetManager, appWidgetIds) + } + } + } + override fun onReceive(context: Context, intent: Intent?) { super.onReceive(context, intent) @@ -34,13 +66,38 @@ class TimesWidgetProvider: AppWidgetProvider() { override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { super.onUpdate(context, appWidgetManager, appWidgetIds) - for (appWidgetId in appWidgetIds) { - val views = RemoteViews(context.packageName, R.layout.widget_times) + handleUpdate(context, appWidgetManager, appWidgetIds) + } - views.setRemoteAdapter(android.R.id.list, Intent(context, TimesWidgetService::class.java)) - views.setEmptyView(android.R.id.list, android.R.id.empty) + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + super.onDeleted(context, appWidgetIds) - appWidgetManager.updateAppWidget(appWidgetId, views) + val database = DefaultAppLogic.with(context).database + + Threads.database.execute { + try { + database.widgetCategory().deleteByWidgetIds(appWidgetIds) + } catch (ex: Exception) { + if (BuildConfig.DEBUG) { + Log.d(LOG_TAG, "onDisabled", ex) + } + } + } + } + + override fun onDisabled(context: Context) { + super.onDisabled(context) + + val database = DefaultAppLogic.with(context).database + + Threads.database.execute { + try { + database.widgetCategory().deleteAll() + } catch (ex: Exception) { + if (BuildConfig.DEBUG) { + Log.d(LOG_TAG, "onDisabled", ex) + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetService.kt b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetService.kt index 4b619f3..1d255b4 100644 --- a/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetService.kt +++ b/app/src/main/java/io/timelimit/android/ui/widget/TimesWidgetService.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2021 Jonas Lochmann + * TimeLimit Copyright 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,101 +17,150 @@ package io.timelimit.android.ui.widget import android.appwidget.AppWidgetManager import android.content.ComponentName +import android.content.Context import android.content.Intent +import android.net.Uri import android.view.View import android.widget.RemoteViews import android.widget.RemoteViewsService +import androidx.core.content.getSystemService import androidx.lifecycle.LiveData import androidx.lifecycle.Observer +import androidx.lifecycle.map import io.timelimit.android.R import io.timelimit.android.async.Threads +import io.timelimit.android.livedata.mergeLiveDataWaitForValues import io.timelimit.android.logic.DefaultAppLogic import io.timelimit.android.ui.manage.child.category.CategoryItemLeftPadding -import io.timelimit.android.util.TimeTextUtil class TimesWidgetService: RemoteViewsService() { - private val appWidgetManager: AppWidgetManager by lazy { AppWidgetManager.getInstance(this) } + companion object { + private const val EXTRA_APP_WIDGET_ID = "appWidgetId" - private val categoriesLive: LiveData> by lazy { - TimesWidgetItems.with(DefaultAppLogic.with(this)) + fun intent(context: Context, appWidgetId: Int) = Intent(context, TimesWidgetService::class.java) + .setData(Uri.parse("widget:$appWidgetId")) + .putExtra(EXTRA_APP_WIDGET_ID, appWidgetId) + + fun notifyContentChanges(context: Context) { + context.getSystemService()?.also { appWidgetManager -> + val widgetIds = appWidgetManager.getAppWidgetIds(ComponentName(context, TimesWidgetProvider::class.java)) + + appWidgetManager.notifyAppWidgetViewDataChanged(widgetIds, android.R.id.list) + } + } } - private var categoriesInput: List = emptyList() - private var categoriesCurrent: List = categoriesInput + private val content: LiveData> by lazy { + val logic = DefaultAppLogic.with(this) - private val categoriesObserver = Observer> { - categoriesInput = it + val content = TimesWidgetContentLoader.with(logic) + val config = logic.database.widgetCategory().queryLive().map { TimesWidgetConfig(it) } - val widgetIds = appWidgetManager.getAppWidgetIds(ComponentName(this, TimesWidgetProvider::class.java)) - - appWidgetManager.notifyAppWidgetViewDataChanged(widgetIds, android.R.id.list) + mergeLiveDataWaitForValues(content, config) } - private val factory = object : RemoteViewsFactory { + private var observerCounter = 0 + private var contentInput: Pair? = null + + private val contentObserver = Observer> { + contentInput = it + + notifyContentChanges(this) + } + + private fun createFactory(appWidgetId: Int) = object : RemoteViewsFactory { + private var currentItems: List = emptyList() + + init { onDataSetChanged() } + override fun onCreate() { - Threads.mainThreadHandler.post { categoriesLive.observeForever(categoriesObserver) } + Threads.mainThreadHandler.post { + if (observerCounter < 0) throw IllegalStateException() + else if (observerCounter == 0) content.observeForever(contentObserver) + + observerCounter++ + } } override fun onDestroy() { - Threads.mainThreadHandler.post { categoriesLive.removeObserver(categoriesObserver) } + Threads.mainThreadHandler.post { + if (observerCounter <= 0) throw IllegalStateException() + else if (observerCounter == 1) content.removeObserver(contentObserver) + + observerCounter-- + } } override fun onDataSetChanged() { - categoriesCurrent = categoriesInput + currentItems = contentInput?.let { TimesWidgetItems.with(it.first, it.second, appWidgetId) } ?: emptyList() } - override fun getCount(): Int = categoriesCurrent.size + override fun getCount(): Int = currentItems.size override fun getViewAt(position: Int): RemoteViews { - if (position >= categoriesCurrent.size) { - return RemoteViews(packageName, R.layout.widget_times_item) + if (position >= currentItems.size) { + return RemoteViews(packageName, R.layout.widget_times_category_item) } - val category = categoriesCurrent[position] - val result = RemoteViews(packageName, R.layout.widget_times_item) + fun createCategoryItem(title: String?, subtitle: String, paddingLeft: Int) = RemoteViews(packageName, R.layout.widget_times_category_item).also { result -> + result.setTextViewText(R.id.title, title ?: "") + result.setTextViewText(R.id.subtitle, subtitle) - result.setTextViewText(R.id.title, category.title) - result.setTextViewText( - R.id.subtitle, - if (category.remainingTimeToday == null) - getString(R.string.manage_child_category_no_time_limits) - else - TimeTextUtil.remaining(category.remainingTimeToday.toInt(), this@TimesWidgetService) - ) + result.setViewPadding(R.id.widgetInnerContainer, paddingLeft, 0, 0, 0) + result.setViewVisibility(R.id.title, if (title != null) View.VISIBLE else View.GONE) + result.setViewVisibility(R.id.topPadding, if (position == 0) View.VISIBLE else View.GONE) + result.setViewVisibility(R.id.bottomPadding, if (position == count - 1) View.VISIBLE else View.GONE) + } - result.setViewPadding( - R.id.widgetInnerContainer, - // not much space here => / 2 - CategoryItemLeftPadding.calculate(category.level, this@TimesWidgetService) / 2, - 0, 0, 0 - ) + val item = currentItems[position] - result.setViewVisibility(R.id.topPadding, if (position == 0) View.VISIBLE else View.GONE) - result.setViewVisibility(R.id.bottomPadding, if (position == count - 1) View.VISIBLE else View.GONE) + return when (item) { + is TimesWidgetItem.Category -> item.category.let { category -> + createCategoryItem( + title = if (category.remainingTimeToday == null) + getString(R.string.manage_child_category_no_time_limits_short) + else { + val remainingTimeToday = category.remainingTimeToday.coerceAtLeast(0) / (1000 * 60) + val minutes = remainingTimeToday % 60 + val hours = remainingTimeToday / 60 - return result + if (hours == 0L) "$minutes m" + else "$hours h $minutes m" + }, + subtitle = category.categoryName, + // not much space here => / 2 + paddingLeft = CategoryItemLeftPadding.calculate(category.level, this@TimesWidgetService) / 2 + ) + } + is TimesWidgetItem.TextMessage -> createCategoryItem(null, getString(item.textRessourceId), 0) + is TimesWidgetItem.DefaultUserButton -> RemoteViews(packageName, R.layout.widget_times_button).also { result -> + result.setTextViewText(R.id.button, getString(R.string.manage_device_default_user_switch_btn)) + result.setOnClickFillInIntent(R.id.button, Intent()) + } + } } - override fun getLoadingView(): RemoteViews? { - return null - } + override fun getLoadingView(): RemoteViews? = null - override fun getViewTypeCount(): Int { - return 1 - } + override fun getViewTypeCount(): Int = 2 override fun getItemId(position: Int): Long { - if (position >= categoriesCurrent.size) { + if (position >= currentItems.size) { return -(position.toLong()) } - return categoriesCurrent[position].hashCode().toLong() + val item = currentItems[position] + + return when (item) { + is TimesWidgetItem.Category -> item.category.categoryId.hashCode() + else -> item.hashCode() + }.toLong() } - override fun hasStableIds(): Boolean { - return true - } + override fun hasStableIds(): Boolean = true } - override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = factory + override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = createFactory( + intent.getIntExtra(EXTRA_APP_WIDGET_ID, 0) + ) } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/config/UnconfiguredDialogFragment.kt b/app/src/main/java/io/timelimit/android/ui/widget/config/UnconfiguredDialogFragment.kt new file mode 100644 index 0000000..c698680 --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/widget/config/UnconfiguredDialogFragment.kt @@ -0,0 +1,51 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.ui.widget.config + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import io.timelimit.android.R + +class UnconfiguredDialogFragment: DialogFragment() { + companion object { + const val DIALOG_TAG = "UnconfiguredDialogFragment" + } + + private val model: WidgetConfigModel by activityViewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + model.state.observe(this) { + if (!(it is WidgetConfigModel.State.Unconfigured)) dismissAllowingStateLoss() + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = AlertDialog.Builder(requireContext(), theme) + .setMessage(R.string.widget_msg_unconfigured) + .setPositiveButton(R.string.generic_ok) { _, _ -> model.userCancel() } + .create() + + override fun onCancel(dialog: DialogInterface) { + super.onCancel(dialog) + + model.userCancel() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigActivity.kt b/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigActivity.kt new file mode 100644 index 0000000..b1c09e3 --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigActivity.kt @@ -0,0 +1,78 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.ui.widget.config + +import android.appwidget.AppWidgetManager +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.activity.viewModels +import androidx.fragment.app.FragmentActivity +import io.timelimit.android.R +import io.timelimit.android.extensions.showSafe + +class WidgetConfigActivity: FragmentActivity() { + private val model: WidgetConfigModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (model.state.value == WidgetConfigModel.State.WaitingForInit) { + model.init( + intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) + ?: AppWidgetManager.INVALID_APPWIDGET_ID + ) + } + + model.state.observe(this) { state -> + when (state) { + is WidgetConfigModel.State.WaitingForInit -> {/* ignore */} + is WidgetConfigModel.State.Working -> {/* ignore */} + is WidgetConfigModel.State.Unconfigured -> { + if (supportFragmentManager.findFragmentByTag(UnconfiguredDialogFragment.DIALOG_TAG) == null) { + UnconfiguredDialogFragment().showSafe(supportFragmentManager, UnconfiguredDialogFragment.DIALOG_TAG) + } + } + is WidgetConfigModel.State.ShowModeSelection -> { + if (supportFragmentManager.findFragmentByTag(WidgetConfigModeDialogFragment.DIALOG_TAG) == null) { + WidgetConfigModeDialogFragment().showSafe(supportFragmentManager, WidgetConfigModeDialogFragment.DIALOG_TAG) + } + } + is WidgetConfigModel.State.ShowCategorySelection -> { + if (supportFragmentManager.findFragmentByTag(WidgetConfigFilterDialogFragment.DIALOG_TAG) == null) { + WidgetConfigFilterDialogFragment().showSafe(supportFragmentManager, WidgetConfigFilterDialogFragment.DIALOG_TAG) + } + } + is WidgetConfigModel.State.Done -> { + setResult( + RESULT_OK, + Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, state.appWidgetId) + ) + + finish() + } + is WidgetConfigModel.State.ErrorCancel -> { + Toast.makeText(this, R.string.error_general, Toast.LENGTH_SHORT).show() + + finish() + } + is WidgetConfigModel.State.UserCancel -> finish() + } + } + + setResult(RESULT_CANCELED) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigFilterDialogFragment.kt b/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigFilterDialogFragment.kt new file mode 100644 index 0000000..5925efa --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigFilterDialogFragment.kt @@ -0,0 +1,88 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.ui.widget.config + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import io.timelimit.android.R + +class WidgetConfigFilterDialogFragment: DialogFragment() { + companion object { + private const val STATE_CATEGORY_IDS = "categoryIds" + + const val DIALOG_TAG = "WidgetConfigFilterDialogFragment" + } + + private val model: WidgetConfigModel by activityViewModels() + private val selectedCategoryIds = mutableSetOf() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + model.state.value?.also { state -> + if (state is WidgetConfigModel.State.ShowCategorySelection) { + selectedCategoryIds.clear() + selectedCategoryIds.addAll(state.selectedFilterCategories) + } + } + + savedInstanceState?.also { + selectedCategoryIds.clear() + selectedCategoryIds.addAll(it.getStringArray(STATE_CATEGORY_IDS) ?: emptyArray()) + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + + outState.putStringArray(STATE_CATEGORY_IDS, selectedCategoryIds.toTypedArray()) + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val state = model.state.value + + if (!(state is WidgetConfigModel.State.ShowCategorySelection)) return super.onCreateDialog(savedInstanceState) + + return AlertDialog.Builder(requireContext(), theme) + .setMultiChoiceItems( + state.categories.map { it.title }.toTypedArray(), + state.categories.map { selectedCategoryIds.contains(it.id) }.toBooleanArray() + ) { _, index, checked -> + val categoryId = state.categories[index].id + + if (checked) selectedCategoryIds.add(categoryId) else selectedCategoryIds.remove(categoryId) + } + .setPositiveButton(R.string.wiazrd_next) { _, _ -> + if (selectedCategoryIds.isEmpty()) { + Toast.makeText(requireContext(), R.string.widget_config_error_filter_empty, Toast.LENGTH_SHORT).show() + + model.userCancel() + } else model.selectFilterItems(selectedCategoryIds) + } + .create() + } + + override fun onCancel(dialog: DialogInterface) { + super.onCancel(dialog) + + model.userCancel() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigModeDialogFragment.kt b/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigModeDialogFragment.kt new file mode 100644 index 0000000..ffe97cd --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigModeDialogFragment.kt @@ -0,0 +1,77 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.ui.widget.config + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import io.timelimit.android.R + +class WidgetConfigModeDialogFragment: DialogFragment() { + companion object { + private const val STATE_SELECTION = "selection" + + const val DIALOG_TAG = "WidgetConfigModeDialogFragment" + } + + private val model: WidgetConfigModel by activityViewModels() + private var selection = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + model.state.value?.also { + if (it is WidgetConfigModel.State.ShowModeSelection) { + selection = if (it.selectedFilterCategories.isEmpty()) 0 else 1 + } + } + + savedInstanceState?.also { selection = it.getInt(STATE_SELECTION) } + + model.state.observe(this) { + if (!(it is WidgetConfigModel.State.ShowModeSelection)) dismissAllowingStateLoss() + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + + outState.putInt(STATE_SELECTION, selection) + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = AlertDialog.Builder(requireContext(), theme) + .setSingleChoiceItems( + arrayOf( + getString(R.string.widget_config_mode_all), + getString(R.string.widget_config_mode_filter) + ), + selection + ) { _, selectedItemIndex -> selection = selectedItemIndex } + .setPositiveButton(R.string.wiazrd_next) { _, _ -> + if (selection == 0) model.selectModeAll() + else model.selectModeFilter() + } + .create() + + override fun onCancel(dialog: DialogInterface) { + super.onCancel(dialog) + + model.userCancel() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigModel.kt b/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigModel.kt new file mode 100644 index 0000000..98d5858 --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/widget/config/WidgetConfigModel.kt @@ -0,0 +1,165 @@ +/* + * TimeLimit Copyright 2019 - 2022 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 . + */ +package io.timelimit.android.ui.widget.config + +import android.app.Application +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import io.timelimit.android.BuildConfig +import io.timelimit.android.async.Threads +import io.timelimit.android.coroutines.executeAndWait +import io.timelimit.android.data.extensions.sortedCategories +import io.timelimit.android.data.model.Category +import io.timelimit.android.data.model.UserType +import io.timelimit.android.data.model.WidgetCategory +import io.timelimit.android.livedata.castDown +import io.timelimit.android.logic.DefaultAppLogic +import kotlinx.coroutines.launch + +class WidgetConfigModel(application: Application): AndroidViewModel(application) { + companion object { + private const val LOG_TAG = "WidgetConfigModel" + } + + private val stateInternal = MutableLiveData().apply { value = State.WaitingForInit } + private val database = DefaultAppLogic.with(application).database + + val state = stateInternal.castDown() + + fun init(appWidgetId: Int) { + if (state.value != State.WaitingForInit) return + stateInternal.value = State.Working + + viewModelScope.launch { + try { + val (deviceAndUserRelatedData, selectedFilterCategories) = Threads.database.executeAndWait { + database.runInTransaction { + val deviceAndUserRelatedData = database.derivedDataDao().getUserAndDeviceRelatedDataSync() + val selectedFilterCategories = database.widgetCategory().queryByWidgetIdSync(appWidgetId).toSet() + + Pair(deviceAndUserRelatedData, selectedFilterCategories) + } + } + + val isNoChildUser = deviceAndUserRelatedData?.userRelatedData?.user?.type != UserType.Child + val currentUserCategories = deviceAndUserRelatedData?.userRelatedData?.sortedCategories()?.map { it.second.category } + + if (currentUserCategories == null || isNoChildUser) { + stateInternal.value = State.Unconfigured + + return@launch + } + + stateInternal.value = State.ShowModeSelection(appWidgetId, selectedFilterCategories, currentUserCategories) + } catch (ex: Exception) { + if (BuildConfig.DEBUG) { + Log.d(LOG_TAG, "selectModeAll", ex) + } + + stateInternal.value = State.ErrorCancel + } + } + } + + fun selectModeAll() { + val oldState = state.value + if (!(oldState is State.ShowModeSelection)) return + stateInternal.value = State.Working + + viewModelScope.launch { + try { + Threads.database.executeAndWait { + database.widgetCategory().deleteByWidgetId(oldState.appWidgetId) + } + + stateInternal.value = State.Done(appWidgetId = oldState.appWidgetId) + } catch (ex: Exception) { + if (BuildConfig.DEBUG) { + Log.d(LOG_TAG, "selectModeAll", ex) + } + + stateInternal.value = State.ErrorCancel + } + } + } + + fun selectModeFilter() { + val oldState = state.value + if (!(oldState is State.ShowModeSelection)) return + stateInternal.value = State.Working + + stateInternal.value = State.ShowCategorySelection( + appWidgetId = oldState.appWidgetId, + selectedFilterCategories = oldState.selectedFilterCategories, + categories = oldState.categories + ) + } + + fun selectFilterItems(selectedCategoryIds: Set) { + val oldState = state.value + if (!(oldState is State.ShowCategorySelection)) return + stateInternal.value = State.Working + + viewModelScope.launch { + try { + Threads.database.executeAndWait { + val userAndDeviceRelatedData = database.derivedDataDao().getUserAndDeviceRelatedDataSync() + val currentCategoryIds = userAndDeviceRelatedData!!.userRelatedData!!.categoryById.keys + + val categoriesToRemove = currentCategoryIds - selectedCategoryIds + val categoriesToAdd = selectedCategoryIds.filter { currentCategoryIds.contains(it) } + + if (categoriesToRemove.isNotEmpty()) { + database.widgetCategory().deleteByWidgetIdAndCategoryIds( + oldState.appWidgetId, categoriesToRemove.toList() + ) + } + + if (categoriesToAdd.isNotEmpty()) { + database.widgetCategory().insert( + categoriesToAdd.toList().map { WidgetCategory(oldState.appWidgetId, it) } + ) + } + } + + stateInternal.value = State.Done(appWidgetId = oldState.appWidgetId) + } catch (ex: Exception) { + if (BuildConfig.DEBUG) { + Log.d(LOG_TAG, "selectModeAll", ex) + } + + stateInternal.value = State.ErrorCancel + } + } + } + + fun userCancel() { + stateInternal.value = State.UserCancel + } + + sealed class State { + object WaitingForInit: State() + object Working: State() + data class ShowModeSelection(val appWidgetId: Int, val selectedFilterCategories: Set, val categories: List): State() + data class ShowCategorySelection(val appWidgetId: Int, val selectedFilterCategories: Set, val categories: List): State() + data class Done(val appWidgetId: Int): State() + object Unconfigured: State() + object UserCancel: State() + object ErrorCancel: State() + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-de/widget_preview.png b/app/src/main/res/drawable-de/widget_preview.png index b5d2c5573bd9fb03bb2856d48e5a07b6e86e3998..125126bbe250b9acd74612e008bab04f2b801c69 100644 GIT binary patch literal 24966 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%0Ab4Q2%`xEo$3(%#q40MmHE*cxh$ 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 F{~M(jIlce@ diff --git a/app/src/main/res/drawable/widget_preview.png b/app/src/main/res/drawable/widget_preview.png index 0cb60f2c6e63f312ea1b3bd791d6cc204a3ee45e..fbba680da997bb745f2475f7eb0cf3f488821aaa 100644 GIT binary patch literal 23610 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 literal 37093 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 + android:background="@color/widgetBackground"> - + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="?android:attr/progressBarStyleLarge" + android:layout_gravity="center" /> \ No newline at end of file diff --git a/app/src/main/res/layout/widget_times_button.xml b/app/src/main/res/layout/widget_times_button.xml new file mode 100644 index 0000000..1dcc003 --- /dev/null +++ b/app/src/main/res/layout/widget_times_button.xml @@ -0,0 +1,33 @@ + + + + +