Make homescreen redirection delay configurable

This commit is contained in:
Jonas Lochmann 2020-02-17 01:00:00 +01:00
parent 937545dc78
commit 7e564e1522
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
12 changed files with 1106 additions and 28 deletions

View file

@ -0,0 +1,828 @@
{
"formatVersion": 1,
"database": {
"version": 26,
"identityHash": "2a202b43acf918df8278ab09c67b5ddf",
"entities": [
{
"tableName": "user",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `password` TEXT NOT NULL, `second_password_salt` TEXT NOT NULL, `type` TEXT NOT NULL, `timezone` TEXT NOT NULL, `disable_limits_until` INTEGER NOT NULL, `mail` TEXT NOT NULL, `current_device` TEXT NOT NULL, `category_for_not_assigned_apps` TEXT NOT NULL, `relax_primary_device` INTEGER NOT NULL, `mail_notification_flags` INTEGER NOT NULL, `blocked_times` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "secondPasswordSalt",
"columnName": "second_password_salt",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "timeZone",
"columnName": "timezone",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "disableLimitsUntil",
"columnName": "disable_limits_until",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "mail",
"columnName": "mail",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "currentDevice",
"columnName": "current_device",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "categoryForNotAssignedApps",
"columnName": "category_for_not_assigned_apps",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "relaxPrimaryDevice",
"columnName": "relax_primary_device",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "mailNotificationFlags",
"columnName": "mail_notification_flags",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "blockedTimes",
"columnName": "blocked_times",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "device",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `model` TEXT NOT NULL, `added_at` INTEGER NOT NULL, `current_user_id` TEXT NOT NULL, `apps_version` TEXT NOT NULL, `network_time` TEXT NOT NULL, `current_protection_level` TEXT NOT NULL, `highest_permission_level` TEXT NOT NULL, `current_usage_stats_permission` TEXT NOT NULL, `highest_usage_stats_permission` TEXT NOT NULL, `current_notification_access_permission` TEXT NOT NULL, `highest_notification_access_permission` TEXT NOT NULL, `current_app_version` INTEGER NOT NULL, `highest_app_version` INTEGER NOT NULL, `tried_disabling_device_admin` INTEGER NOT NULL, `did_reboot` INTEGER NOT NULL, `had_manipulation` INTEGER NOT NULL, `had_manipulation_flags` INTEGER NOT NULL, `did_report_uninstall` INTEGER NOT NULL, `is_user_kept_signed_in` INTEGER NOT NULL, `show_device_connected` INTEGER NOT NULL, `default_user` TEXT NOT NULL, `default_user_timeout` INTEGER NOT NULL, `consider_reboot_manipulation` INTEGER NOT NULL, `current_overlay_permission` TEXT NOT NULL, `highest_overlay_permission` TEXT NOT NULL, `current_accessibility_service_permission` INTEGER NOT NULL, `was_accessibility_service_permission` INTEGER NOT NULL, `enable_activity_level_blocking` INTEGER NOT NULL, `q_or_later` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "model",
"columnName": "model",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "addedAt",
"columnName": "added_at",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "currentUserId",
"columnName": "current_user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "installedAppsVersion",
"columnName": "apps_version",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "networkTime",
"columnName": "network_time",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "currentProtectionLevel",
"columnName": "current_protection_level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "highestProtectionLevel",
"columnName": "highest_permission_level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "currentUsageStatsPermission",
"columnName": "current_usage_stats_permission",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "highestUsageStatsPermission",
"columnName": "highest_usage_stats_permission",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "currentNotificationAccessPermission",
"columnName": "current_notification_access_permission",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "highestNotificationAccessPermission",
"columnName": "highest_notification_access_permission",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "currentAppVersion",
"columnName": "current_app_version",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "highestAppVersion",
"columnName": "highest_app_version",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "manipulationTriedDisablingDeviceAdmin",
"columnName": "tried_disabling_device_admin",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "manipulationDidReboot",
"columnName": "did_reboot",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "hadManipulation",
"columnName": "had_manipulation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "hadManipulationFlags",
"columnName": "had_manipulation_flags",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "didReportUninstall",
"columnName": "did_report_uninstall",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isUserKeptSignedIn",
"columnName": "is_user_kept_signed_in",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "showDeviceConnected",
"columnName": "show_device_connected",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "defaultUser",
"columnName": "default_user",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "defaultUserTimeout",
"columnName": "default_user_timeout",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "considerRebootManipulation",
"columnName": "consider_reboot_manipulation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "currentOverlayPermission",
"columnName": "current_overlay_permission",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "highestOverlayPermission",
"columnName": "highest_overlay_permission",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessibilityServiceEnabled",
"columnName": "current_accessibility_service_permission",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "wasAccessibilityServiceEnabled",
"columnName": "was_accessibility_service_permission",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enableActivityLevelBlocking",
"columnName": "enable_activity_level_blocking",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "qOrLater",
"columnName": "q_or_later",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "app",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`device_id` TEXT NOT NULL, `package_name` TEXT NOT NULL, `title` TEXT NOT NULL, `launchable` INTEGER NOT NULL, `recommendation` TEXT NOT NULL, PRIMARY KEY(`device_id`, `package_name`))",
"fields": [
{
"fieldPath": "deviceId",
"columnName": "device_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isLaunchable",
"columnName": "launchable",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "recommendation",
"columnName": "recommendation",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"device_id",
"package_name"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_app_device_id",
"unique": false,
"columnNames": [
"device_id"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_app_device_id` ON `${TABLE_NAME}` (`device_id`)"
},
{
"name": "index_app_package_name",
"unique": false,
"columnNames": [
"package_name"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_app_package_name` ON `${TABLE_NAME}` (`package_name`)"
}
],
"foreignKeys": []
},
{
"tableName": "category_app",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`category_id` TEXT NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`category_id`, `package_name`))",
"fields": [
{
"fieldPath": "categoryId",
"columnName": "category_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"category_id",
"package_name"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_category_app_category_id",
"unique": false,
"columnNames": [
"category_id"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_category_app_category_id` ON `${TABLE_NAME}` (`category_id`)"
},
{
"name": "index_category_app_package_name",
"unique": false,
"columnNames": [
"package_name"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_category_app_package_name` ON `${TABLE_NAME}` (`package_name`)"
}
],
"foreignKeys": []
},
{
"tableName": "category",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `child_id` TEXT NOT NULL, `title` TEXT NOT NULL, `blocked_times` TEXT NOT NULL, `extra_time` INTEGER NOT NULL, `temporarily_blocked` INTEGER NOT NULL, `temporarily_blocked_end_time` INTEGER NOT NULL, `base_version` TEXT NOT NULL, `apps_version` TEXT NOT NULL, `rules_version` TEXT NOT NULL, `usedtimes_version` TEXT NOT NULL, `parent_category_id` TEXT NOT NULL, `block_all_notifications` INTEGER NOT NULL, `time_warnings` INTEGER NOT NULL, `min_battery_charging` INTEGER NOT NULL, `min_battery_mobile` INTEGER NOT NULL, `sort` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "childId",
"columnName": "child_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockedMinutesInWeek",
"columnName": "blocked_times",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "extraTimeInMillis",
"columnName": "extra_time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "temporarilyBlocked",
"columnName": "temporarily_blocked",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "temporarilyBlockedEndTime",
"columnName": "temporarily_blocked_end_time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "baseVersion",
"columnName": "base_version",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "assignedAppsVersion",
"columnName": "apps_version",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "timeLimitRulesVersion",
"columnName": "rules_version",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "usedTimesVersion",
"columnName": "usedtimes_version",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "parentCategoryId",
"columnName": "parent_category_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockAllNotifications",
"columnName": "block_all_notifications",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "timeWarnings",
"columnName": "time_warnings",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minBatteryLevelWhileCharging",
"columnName": "min_battery_charging",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minBatteryLevelMobile",
"columnName": "min_battery_mobile",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "used_time",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`day_of_epoch` INTEGER NOT NULL, `used_time` INTEGER NOT NULL, `category_id` TEXT NOT NULL, PRIMARY KEY(`category_id`, `day_of_epoch`))",
"fields": [
{
"fieldPath": "dayOfEpoch",
"columnName": "day_of_epoch",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "usedMillis",
"columnName": "used_time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "categoryId",
"columnName": "category_id",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"category_id",
"day_of_epoch"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "time_limit_rule",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `category_id` TEXT NOT NULL, `apply_to_extra_time_usage` INTEGER NOT NULL, `day_mask` INTEGER NOT NULL, `max_time` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "categoryId",
"columnName": "category_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "applyToExtraTimeUsage",
"columnName": "apply_to_extra_time_usage",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dayMask",
"columnName": "day_mask",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "maximumTimeInMillis",
"columnName": "max_time",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "config",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "key",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "temporarily_allowed_app",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`device_id` TEXT NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`device_id`, `package_name`))",
"fields": [
{
"fieldPath": "deviceId",
"columnName": "device_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"device_id",
"package_name"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "pending_sync_action",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sequence_number` INTEGER NOT NULL, `action` TEXT NOT NULL, `integrity` TEXT NOT NULL, `scheduled_for_upload` INTEGER NOT NULL, `type` TEXT NOT NULL, `user_id` TEXT NOT NULL, PRIMARY KEY(`sequence_number`))",
"fields": [
{
"fieldPath": "sequenceNumber",
"columnName": "sequence_number",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "encodedAction",
"columnName": "action",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "integrity",
"columnName": "integrity",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "scheduledForUpload",
"columnName": "scheduled_for_upload",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"sequence_number"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_pending_sync_action_scheduled_for_upload",
"unique": false,
"columnNames": [
"scheduled_for_upload"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_pending_sync_action_scheduled_for_upload` ON `${TABLE_NAME}` (`scheduled_for_upload`)"
}
],
"foreignKeys": []
},
{
"tableName": "app_activity",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`device_id` TEXT NOT NULL, `app_package_name` TEXT NOT NULL, `activity_class_name` TEXT NOT NULL, `activity_title` TEXT NOT NULL, PRIMARY KEY(`device_id`, `app_package_name`, `activity_class_name`))",
"fields": [
{
"fieldPath": "deviceId",
"columnName": "device_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "appPackageName",
"columnName": "app_package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "activityClassName",
"columnName": "activity_class_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "activity_title",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"device_id",
"app_package_name",
"activity_class_name"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "notification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` INTEGER NOT NULL, `id` TEXT NOT NULL, `first_notify_time` INTEGER NOT NULL, `dismissed` INTEGER NOT NULL, PRIMARY KEY(`type`, `id`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "firstNotifyTime",
"columnName": "first_notify_time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isDismissed",
"columnName": "dismissed",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"type",
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "allowed_contact",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `phone` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "phone",
"columnName": "phone",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2a202b43acf918df8278ab09c67b5ddf')"
]
}
}

View file

@ -178,4 +178,12 @@ object DatabaseMigrations {
database.execSQL("ALTER TABLE `category` ADD COLUMN `sort` INTEGER NOT NULL DEFAULT 0")
}
}
val MIGRATE_TO_V26 = object: Migration(25, 26) {
override fun migrate(database: SupportSQLiteDatabase) {
// this is empty
//
// a new possible enum value was added, the version upgrade enables the downgrade mechanism
}
}
}

View file

@ -35,7 +35,7 @@ import io.timelimit.android.data.model.*
AppActivity::class,
Notification::class,
AllowedContact::class
], version = 25)
], version = 26)
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
companion object {
private val lock = Object()
@ -94,7 +94,8 @@ abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database
DatabaseMigrations.MIGRATE_TO_V22,
DatabaseMigrations.MIGRATE_TO_V23,
DatabaseMigrations.MIGRATE_TO_V24,
DatabaseMigrations.MIGRATE_TO_V25
DatabaseMigrations.MIGRATE_TO_V25,
DatabaseMigrations.MIGRATE_TO_V26
)
.build()
}

View file

@ -268,4 +268,7 @@ abstract class ConfigDao {
}
fun setDefaultHomescreenSync(componentName: ComponentName?) = updateValueSync(ConfigurationItemType.DefaultHomescreen, componentName?.flattenToString())
fun getHomescreenDelaySync(): Int = getValueOfKeySync(ConfigurationItemType.HomescreenDelay)?.toIntOrNull(radix = 10) ?: 5
fun setHomescreenDelaySync(delay: Int) = updateValueSync(ConfigurationItemType.HomescreenDelay, delay.toString(radix = 10))
}

View file

@ -93,7 +93,8 @@ enum class ConfigurationItemType {
EnableBackgroundSync,
EnableAlternativeDurationSelection,
ExperimentalFlags,
DefaultHomescreen
DefaultHomescreen,
HomescreenDelay
}
object ConfigurationItemTypeUtil {
@ -114,6 +115,7 @@ object ConfigurationItemTypeUtil {
private const val ENABLE_ALTERNATIVE_DURATION_SELECTION = 16
private const val EXPERIMENTAL_FLAGS = 17
private const val DEFAULT_HOMESCREEN = 18
private const val HOMESCREEN_DELAY = 19
val TYPES = listOf(
ConfigurationItemType.OwnDeviceId,
@ -132,7 +134,8 @@ object ConfigurationItemTypeUtil {
ConfigurationItemType.EnableBackgroundSync,
ConfigurationItemType.EnableAlternativeDurationSelection,
ConfigurationItemType.ExperimentalFlags,
ConfigurationItemType.DefaultHomescreen
ConfigurationItemType.DefaultHomescreen,
ConfigurationItemType.HomescreenDelay
)
fun serialize(value: ConfigurationItemType) = when(value) {
@ -153,6 +156,7 @@ object ConfigurationItemTypeUtil {
ConfigurationItemType.EnableAlternativeDurationSelection -> ENABLE_ALTERNATIVE_DURATION_SELECTION
ConfigurationItemType.ExperimentalFlags -> EXPERIMENTAL_FLAGS
ConfigurationItemType.DefaultHomescreen -> DEFAULT_HOMESCREEN
ConfigurationItemType.HomescreenDelay -> HOMESCREEN_DELAY
}
fun parse(value: Int) = when(value) {
@ -173,6 +177,7 @@ object ConfigurationItemTypeUtil {
ENABLE_ALTERNATIVE_DURATION_SELECTION -> ConfigurationItemType.EnableAlternativeDurationSelection
EXPERIMENTAL_FLAGS -> ConfigurationItemType.ExperimentalFlags
DEFAULT_HOMESCREEN -> ConfigurationItemType.DefaultHomescreen
HOMESCREEN_DELAY -> ConfigurationItemType.HomescreenDelay
else -> throw IllegalArgumentException()
}
}

View file

@ -21,6 +21,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import io.timelimit.android.BuildConfig
@ -29,8 +30,10 @@ import io.timelimit.android.async.Threads
import io.timelimit.android.data.Database
import io.timelimit.android.data.model.ExperimentalFlags
import io.timelimit.android.databinding.DiagnoseExperimentalFlagFragmentBinding
import io.timelimit.android.databinding.DiagnoseExperimentalFlagItemBinding
import io.timelimit.android.livedata.liveDataFromValue
import io.timelimit.android.logic.DefaultAppLogic
import io.timelimit.android.ui.homescreen.ConfigureHomescreenDelayDialogFragment
import io.timelimit.android.ui.main.ActivityViewModelHolder
import io.timelimit.android.ui.main.AuthenticationFab
import io.timelimit.android.ui.main.FragmentWithCustomTitle
@ -54,23 +57,29 @@ class DiagnoseExperimentalFlagFragment : Fragment(), FragmentWithCustomTitle {
binding.fab.setOnClickListener { activity.showAuthenticationScreen() }
val flags = DiagnoseExperimentalFlagItem.items
val checkboxes = flags.map {
CheckBox(context).apply {
setText(it.label)
val checkboxes = flags.map { flag ->
DiagnoseExperimentalFlagItemBinding.inflate(LayoutInflater.from(context), binding.container, true).apply {
label = getString(flag.label)
configButton.setOnClickListener {
if (auth.requestAuthenticationOrReturnTrue()) {
flag.configHook?.invoke(parentFragmentManager)
}
}
}
}
checkboxes.forEach { binding.container.addView(it) }
database.config().experimentalFlags.observe(this, Observer { setFlags ->
database.config().experimentalFlags.observe(viewLifecycleOwner, Observer { setFlags ->
flags.forEachIndexed { index, flag ->
val checkbox = checkboxes[index]
val isFlagSet = (setFlags and flag.enableFlags) == flag.enableFlags
checkbox.setOnCheckedChangeListener { _, _ -> }
checkbox.isChecked = isFlagSet
checkbox.isEnabled = flag.enable(setFlags)
checkbox.setOnCheckedChangeListener { _, didCheck ->
checkbox.enabled = flag.enable(setFlags)
checkbox.showConfigButton = isFlagSet && flag.configHook != null
checkbox.checkbox.setOnCheckedChangeListener { _, _ -> }
checkbox.checkbox.isChecked = isFlagSet
checkbox.checkbox.setOnCheckedChangeListener { _, didCheck ->
if (didCheck != isFlagSet) {
if (auth.requestAuthenticationOrReturnTrue()) {
Threads.database.execute {
@ -83,7 +92,7 @@ class DiagnoseExperimentalFlagFragment : Fragment(), FragmentWithCustomTitle {
}
}
} else {
checkbox.isChecked = isFlagSet
checkbox.checkbox.isChecked = isFlagSet
}
}
}
@ -101,7 +110,8 @@ data class DiagnoseExperimentalFlagItem(
val enableFlags: Long,
val disableFlags: Long,
val enable: (flags: Long) -> Boolean,
val postEnableHook: ((Database) -> Unit)? = null
val postEnableHook: ((Database) -> Unit)? = null,
val configHook: ((FragmentManager) -> Unit)? = null
) {
companion object {
val items = listOf(
@ -142,13 +152,14 @@ data class DiagnoseExperimentalFlagItem(
enableFlags = ExperimentalFlags.CUSTOM_HOME_SCREEN,
disableFlags = ExperimentalFlags.CUSTOM_HOME_SCREEN or ExperimentalFlags.CUSTOM_HOMESCREEN_DELAY,
enable = { true },
postEnableHook = { it.config().setDefaultHomescreenSync(null) }
postEnableHook = { database -> database.config().setDefaultHomescreenSync(null) }
),
DiagnoseExperimentalFlagItem(
label = R.string.diagnose_exf_chd,
enableFlags = ExperimentalFlags.CUSTOM_HOMESCREEN_DELAY,
disableFlags = ExperimentalFlags.CUSTOM_HOMESCREEN_DELAY,
enable = { flags -> flags and ExperimentalFlags.CUSTOM_HOME_SCREEN == ExperimentalFlags.CUSTOM_HOME_SCREEN }
enable = { flags -> flags and ExperimentalFlags.CUSTOM_HOME_SCREEN == ExperimentalFlags.CUSTOM_HOME_SCREEN },
configHook = { fragmentManager -> ConfigureHomescreenDelayDialogFragment().show(fragmentManager) }
)
)
}

View file

@ -0,0 +1,100 @@
/*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.timelimit.android.ui.homescreen
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import io.timelimit.android.async.Threads
import io.timelimit.android.coroutines.executeAndWait
import io.timelimit.android.coroutines.runAsync
import io.timelimit.android.data.model.UserType
import io.timelimit.android.databinding.ConfigureHomescreenDelayDialogBinding
import io.timelimit.android.extensions.showSafe
import io.timelimit.android.ui.main.ActivityViewModelHolder
class ConfigureHomescreenDelayDialogFragment: BottomSheetDialogFragment() {
companion object {
private const val DIALOG_TAG = "ConfigureHomescreenDelayDialogFragment"
private const val STATUS_DID_LOAD_VALUE = "didLoadValue"
private const val STATUS_CURRENT_VALUE = "currentValue"
}
private lateinit var binding: ConfigureHomescreenDelayDialogBinding
private var didLoadValue = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState != null) {
didLoadValue = savedInstanceState.getBoolean(STATUS_DID_LOAD_VALUE, false)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(STATUS_DID_LOAD_VALUE, didLoadValue)
outState.putInt(STATUS_CURRENT_VALUE, binding.numberpicker.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = ConfigureHomescreenDelayDialogBinding.inflate(inflater, container, false)
val auth = (activity as ActivityViewModelHolder).getActivityViewModel()
auth.authenticatedUser.observe(viewLifecycleOwner, Observer {
if (it?.second?.type != UserType.Parent) {
dismissAllowingStateLoss()
}
})
binding.numberpicker.apply {
minValue = 3
maxValue = 300
}
if (savedInstanceState != null) {
binding.numberpicker.value = savedInstanceState.getInt(STATUS_CURRENT_VALUE)
}
if (!didLoadValue) {
runAsync {
val value = Threads.database.executeAndWait { auth.logic.database.config().getHomescreenDelaySync() }
binding.numberpicker.value = value
didLoadValue = true
}
}
binding.saveButton.setOnClickListener {
if (auth.isParentAuthenticated()) {
Threads.database.execute {
auth.database.config().setHomescreenDelaySync(binding.numberpicker.value)
}
dismiss()
}
}
return binding.root
}
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
}

View file

@ -29,8 +29,6 @@ import kotlinx.coroutines.delay
class HomescreenModel(application: Application): AndroidViewModel(application) {
companion object {
private const val DELAY = 5000L // 5 seconds
private const val COUNTER_DURATION = 1000 * 60L // 1 minute
private const val COUNTER_MIN = 20 // 20 times
@ -64,18 +62,22 @@ class HomescreenModel(application: Application): AndroidViewModel(application) {
isHandlingLaunch = true
runAsync {
val enableDelay = Threads.database.executeAndWait {
logic.database.config().isExperimentalFlagsSetSync(ExperimentalFlags.CUSTOM_HOMESCREEN_DELAY)
val delay = Threads.database.executeAndWait {
if (logic.database.config().isExperimentalFlagsSetSync(ExperimentalFlags.CUSTOM_HOMESCREEN_DELAY)) {
logic.database.config().getHomescreenDelaySync() * 1000
} else {
0
}
}
if (enableDelay && (!hadFirstRunAfterLaunch)) {
if (delay > 0 && (!hadFirstRunAfterLaunch)) {
val timeApi = logic.timeApi
var start = timeApi.getCurrentUptimeInMillis()
var end = start + DELAY
var end = start + delay
while (true) {
val now = timeApi.getCurrentUptimeInMillis()
val progress = (now - start) * 100L / DELAY
val progress = (now - start) * 100L / delay
if (now >= end) {
break
@ -94,10 +96,10 @@ class HomescreenModel(application: Application): AndroidViewModel(application) {
}
val afterPause = timeApi.getCurrentUptimeInMillis()
val delay = afterPause - beforePause
val timeToAdd = afterPause - beforePause
start += delay
end += delay
start += timeToAdd
end += timeToAdd
}
}
}

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:textAppearanceLarge"
android:text="@string/homescreen_delay_config_dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:text="@string/homescreen_delay_config_dialog_text"
android:textAppearance="?android:textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<NumberPicker
android:id="@+id/numberpicker"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/save_button"
android:layout_marginEnd="4dp"
android:layout_gravity="end"
android:text="@string/generic_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>

View file

@ -0,0 +1,62 @@
<!--
TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="enabled"
type="boolean" />
<variable
name="label"
type="String" />
<variable
name="showConfigButton"
type="boolean" />
<import type="android.view.View" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:layout_gravity="center_vertical"
android:enabled="@{enabled}"
android:text="@{label}"
tools:text="@string/diagnose_exf_nas"
android:id="@+id/checkbox"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<ImageButton
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="?selectableItemBackground"
android:id="@+id/config_button"
android:visibility="@{showConfigButton ? View.VISIBLE : View.GONE}"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_settings_black_24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>

View file

@ -17,4 +17,7 @@
<string name="homescreen_selection_title">Startbildschirmauswahl</string>
<string name="homescreen_selection_intro">TimeLimit ist kein vollständiger Startbildschirm, aber Sie können auswählen, was angezeigt werden soll</string>
<string name="homescreen_selection_change_info">Dieses Menü kann jederzeit wieder aufgerufen werden durch das wiederholte Tippen auf den Home-Button</string>
<string name="homescreen_delay_config_dialog_title">Startbildschirmweiterleitungverzögerung</string>
<string name="homescreen_delay_config_dialog_text">Hier können Sie die Verzögerung für die Startbildschirmweiterleitung in Sekunden einstellen</string>
</resources>

View file

@ -17,4 +17,7 @@
<string name="homescreen_selection_title">Homescreen selection</string>
<string name="homescreen_selection_intro">TimeLimit itself is no complete homescreen, so you can chose what should be shown</string>
<string name="homescreen_selection_change_info">You can open this menu at any time by repeating tapping the home button</string>
<string name="homescreen_delay_config_dialog_title">Homescreen redirection delay</string>
<string name="homescreen_delay_config_dialog_text">Here you can configure the delay for the redirection in seconds</string>
</resources>