mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-04 02:09:19 +02:00
Add option for daily rules
This commit is contained in:
parent
4ddd79bc94
commit
8fb9f496aa
19 changed files with 1375 additions and 149 deletions
1196
app/schemas/io.timelimit.android.data.RoomDatabase/35.json
Normal file
1196
app/schemas/io.timelimit.android.data.RoomDatabase/35.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -250,4 +250,10 @@ object DatabaseMigrations {
|
||||||
database.execSQL("ALTER TABLE `category` ADD COLUMN `tasks_version` TEXT NOT NULL DEFAULT ''")
|
database.execSQL("ALTER TABLE `category` ADD COLUMN `tasks_version` TEXT NOT NULL DEFAULT ''")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val MIGRATE_TO_V35 = object: Migration(34, 35) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE `time_limit_rule` ADD COLUMN `per_day` INTEGER NOT NULL DEFAULT 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ import java.util.concurrent.TimeUnit
|
||||||
UserLimitLoginCategory::class,
|
UserLimitLoginCategory::class,
|
||||||
CategoryNetworkId::class,
|
CategoryNetworkId::class,
|
||||||
ChildTask::class
|
ChildTask::class
|
||||||
], version = 34)
|
], version = 35)
|
||||||
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
||||||
companion object {
|
companion object {
|
||||||
private val lock = Object()
|
private val lock = Object()
|
||||||
|
@ -118,7 +118,8 @@ abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database
|
||||||
DatabaseMigrations.MIGRATE_TO_V31,
|
DatabaseMigrations.MIGRATE_TO_V31,
|
||||||
DatabaseMigrations.MIGRATE_TO_V32,
|
DatabaseMigrations.MIGRATE_TO_V32,
|
||||||
DatabaseMigrations.MIGRATE_TO_V33,
|
DatabaseMigrations.MIGRATE_TO_V33,
|
||||||
DatabaseMigrations.MIGRATE_TO_V34
|
DatabaseMigrations.MIGRATE_TO_V34,
|
||||||
|
DatabaseMigrations.MIGRATE_TO_V35
|
||||||
)
|
)
|
||||||
.setQueryExecutor(Threads.database)
|
.setQueryExecutor(Threads.database)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -211,7 +211,7 @@ object HintsToShow {
|
||||||
const val CATEGORIES_INTRODUCTION = 4L
|
const val CATEGORIES_INTRODUCTION = 4L
|
||||||
const val TIME_LIMIT_RULE_INTRODUCTION = 8L
|
const val TIME_LIMIT_RULE_INTRODUCTION = 8L
|
||||||
const val CONTACTS_INTRO = 16L
|
const val CONTACTS_INTRO = 16L
|
||||||
const val TIMELIMIT_RULE_MUSTREAD = 32L
|
private const val OBSOLETE_TIMELIMIT_RULE_MUSTREAD = 32L
|
||||||
const val BLOCKED_TIME_AREAS_OBSOLETE = 64L
|
const val BLOCKED_TIME_AREAS_OBSOLETE = 64L
|
||||||
const val TASKS_INTRODUCTION = 128L
|
const val TASKS_INTRODUCTION = 128L
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,9 @@ data class TimeLimitRule(
|
||||||
@ColumnInfo(name = "session_duration_milliseconds")
|
@ColumnInfo(name = "session_duration_milliseconds")
|
||||||
val sessionDurationMilliseconds: Int,
|
val sessionDurationMilliseconds: Int,
|
||||||
@ColumnInfo(name = "session_pause_milliseconds")
|
@ColumnInfo(name = "session_pause_milliseconds")
|
||||||
val sessionPauseMilliseconds: Int
|
val sessionPauseMilliseconds: Int,
|
||||||
|
@ColumnInfo(name = "per_day")
|
||||||
|
val perDay: Boolean
|
||||||
): Parcelable, JsonSerializable {
|
): Parcelable, JsonSerializable {
|
||||||
companion object {
|
companion object {
|
||||||
private const val RULE_ID = "ruleId"
|
private const val RULE_ID = "ruleId"
|
||||||
|
@ -65,6 +67,7 @@ data class TimeLimitRule(
|
||||||
private const val END_MINUTE_OF_DAY = "end"
|
private const val END_MINUTE_OF_DAY = "end"
|
||||||
private const val SESSION_DURATION_MILLISECONDS = "dur"
|
private const val SESSION_DURATION_MILLISECONDS = "dur"
|
||||||
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
||||||
|
private const val PER_DAY = "perDay"
|
||||||
|
|
||||||
const val MIN_START_MINUTE = MinuteOfDay.MIN
|
const val MIN_START_MINUTE = MinuteOfDay.MIN
|
||||||
const val MAX_END_MINUTE = MinuteOfDay.MAX
|
const val MAX_END_MINUTE = MinuteOfDay.MAX
|
||||||
|
@ -79,6 +82,7 @@ data class TimeLimitRule(
|
||||||
var endMinuteOfDay = MAX_END_MINUTE
|
var endMinuteOfDay = MAX_END_MINUTE
|
||||||
var sessionDurationMilliseconds = 0
|
var sessionDurationMilliseconds = 0
|
||||||
var sessionPauseMilliseconds = 0
|
var sessionPauseMilliseconds = 0
|
||||||
|
var perDay = false
|
||||||
|
|
||||||
reader.beginObject()
|
reader.beginObject()
|
||||||
|
|
||||||
|
@ -93,6 +97,7 @@ data class TimeLimitRule(
|
||||||
END_MINUTE_OF_DAY -> endMinuteOfDay = reader.nextInt()
|
END_MINUTE_OF_DAY -> endMinuteOfDay = reader.nextInt()
|
||||||
SESSION_DURATION_MILLISECONDS -> sessionDurationMilliseconds = reader.nextInt()
|
SESSION_DURATION_MILLISECONDS -> sessionDurationMilliseconds = reader.nextInt()
|
||||||
SESSION_PAUSE_MILLISECONDS -> sessionPauseMilliseconds = reader.nextInt()
|
SESSION_PAUSE_MILLISECONDS -> sessionPauseMilliseconds = reader.nextInt()
|
||||||
|
PER_DAY -> perDay = reader.nextBoolean()
|
||||||
else -> reader.skipValue()
|
else -> reader.skipValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +113,8 @@ data class TimeLimitRule(
|
||||||
startMinuteOfDay = startMinuteOfDay,
|
startMinuteOfDay = startMinuteOfDay,
|
||||||
endMinuteOfDay = endMinuteOfDay,
|
endMinuteOfDay = endMinuteOfDay,
|
||||||
sessionDurationMilliseconds = sessionDurationMilliseconds,
|
sessionDurationMilliseconds = sessionDurationMilliseconds,
|
||||||
sessionPauseMilliseconds = sessionPauseMilliseconds
|
sessionPauseMilliseconds = sessionPauseMilliseconds,
|
||||||
|
perDay = perDay
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,6 +165,8 @@ data class TimeLimitRule(
|
||||||
writer.name(SESSION_PAUSE_MILLISECONDS).value(sessionPauseMilliseconds)
|
writer.name(SESSION_PAUSE_MILLISECONDS).value(sessionPauseMilliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (perDay) writer.name(PER_DAY).value(true)
|
||||||
|
|
||||||
writer.endObject()
|
writer.endObject()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import org.threeten.bp.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
data class DateInTimezone(val dayOfWeek: Int, val dayOfEpoch: Int, val localDate: LocalDate) {
|
data class DateInTimezone(val dayOfWeek: Int, val dayOfEpoch: Int, val localDate: LocalDate) {
|
||||||
|
val firstDayOfWeekAsEpochDay get() = dayOfEpoch - dayOfWeek
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun convertDayOfWeek(dayOfWeek: Int) = when(dayOfWeek) {
|
fun convertDayOfWeek(dayOfWeek: Int) = when(dayOfWeek) {
|
||||||
Calendar.MONDAY -> 0
|
Calendar.MONDAY -> 0
|
||||||
|
|
|
@ -57,8 +57,8 @@ data class RemainingTime(val includingExtraTime: Long, val default: Long) {
|
||||||
}
|
}
|
||||||
|
|
||||||
val relatedRules = getRulesRelatedToDay(dayOfWeek, minuteOfDay, rules)
|
val relatedRules = getRulesRelatedToDay(dayOfWeek, minuteOfDay, rules)
|
||||||
val withoutExtraTime = getRemainingTime(usedTimes, relatedRules, false, firstDayOfWeekAsEpochDay)
|
val withoutExtraTime = getRemainingTime(usedTimes, relatedRules, false, firstDayOfWeekAsEpochDay, dayOfWeek)
|
||||||
val withExtraTime = getRemainingTime(usedTimes, relatedRules, true, firstDayOfWeekAsEpochDay)
|
val withExtraTime = getRemainingTime(usedTimes, relatedRules, true, firstDayOfWeekAsEpochDay, dayOfWeek)
|
||||||
|
|
||||||
if (withoutExtraTime == null && withExtraTime == null) {
|
if (withoutExtraTime == null && withExtraTime == null) {
|
||||||
// no rules
|
// no rules
|
||||||
|
@ -86,15 +86,21 @@ data class RemainingTime(val includingExtraTime: Long, val default: Long) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRemainingTime(usedTimes: List<UsedTimeItem>, relatedRules: List<TimeLimitRule>, assumeMaximalExtraTime: Boolean, firstDayOfWeekAsEpochDay: Int): Long? {
|
private fun getRemainingTime(usedTimes: List<UsedTimeItem>, relatedRules: List<TimeLimitRule>, assumeMaximalExtraTime: Boolean, firstDayOfWeekAsEpochDay: Int, dayOfWeek: Int): Long? {
|
||||||
return relatedRules.filter { (!assumeMaximalExtraTime) || it.applyToExtraTimeUsage }.map { rule ->
|
return relatedRules.filter { (!assumeMaximalExtraTime) || it.applyToExtraTimeUsage }.map { rule ->
|
||||||
var usedTime = 0L
|
var usedTime = 0L
|
||||||
|
|
||||||
usedTimes.forEach { usedTimeItem ->
|
usedTimes.forEach { usedTimeItem ->
|
||||||
if (usedTimeItem.dayOfEpoch >= firstDayOfWeekAsEpochDay && usedTimeItem.dayOfEpoch <= firstDayOfWeekAsEpochDay + 6) {
|
val doesWeekMatch = usedTimeItem.dayOfEpoch >= firstDayOfWeekAsEpochDay && usedTimeItem.dayOfEpoch <= firstDayOfWeekAsEpochDay + 6
|
||||||
val usedTimeItemDayOfWeek = usedTimeItem.dayOfEpoch - firstDayOfWeekAsEpochDay
|
|
||||||
|
|
||||||
if ((rule.dayMask.toInt() and (1 shl usedTimeItemDayOfWeek)) != 0) {
|
if (doesWeekMatch) {
|
||||||
|
val usedTimeItemDayOfWeek = usedTimeItem.dayOfEpoch - firstDayOfWeekAsEpochDay
|
||||||
|
val doesDayMaskMatch = (rule.dayMask.toInt() and (1 shl usedTimeItemDayOfWeek)) != 0
|
||||||
|
val doesCurrentDayMatch = dayOfWeek == usedTimeItemDayOfWeek
|
||||||
|
|
||||||
|
val usedTimeItemMatching = if (rule.perDay) doesCurrentDayMatch else doesDayMaskMatch
|
||||||
|
|
||||||
|
if (usedTimeItemMatching) {
|
||||||
if (rule.startMinuteOfDay == usedTimeItem.startTimeOfDay && rule.endMinuteOfDay == usedTimeItem.endTimeOfDay) {
|
if (rule.startMinuteOfDay == usedTimeItem.startTimeOfDay && rule.endMinuteOfDay == usedTimeItem.endTimeOfDay) {
|
||||||
usedTime += usedTimeItem.usedMillis
|
usedTime += usedTimeItem.usedMillis
|
||||||
}
|
}
|
||||||
|
@ -106,7 +112,7 @@ data class RemainingTime(val includingExtraTime: Long, val default: Long) {
|
||||||
val remaining = Math.max(0, maxTime - usedTime)
|
val remaining = Math.max(0, maxTime - usedTime)
|
||||||
|
|
||||||
remaining
|
remaining
|
||||||
}.min()
|
}.minOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1442,7 +1442,8 @@ data class CreateTimeLimitRuleAction(val rule: TimeLimitRule): ParentAction() {
|
||||||
|
|
||||||
data class UpdateTimeLimitRuleAction(
|
data class UpdateTimeLimitRuleAction(
|
||||||
val ruleId: String, val dayMask: Byte, val maximumTimeInMillis: Int, val applyToExtraTimeUsage: Boolean,
|
val ruleId: String, val dayMask: Byte, val maximumTimeInMillis: Int, val applyToExtraTimeUsage: Boolean,
|
||||||
val start: Int, val end: Int, val sessionDurationMilliseconds: Int, val sessionPauseMilliseconds: Int
|
val start: Int, val end: Int, val sessionDurationMilliseconds: Int, val sessionPauseMilliseconds: Int,
|
||||||
|
val perDay: Boolean
|
||||||
): ParentAction() {
|
): ParentAction() {
|
||||||
companion object {
|
companion object {
|
||||||
const val TYPE_VALUE = "UPDATE_TIMELIMIT_RULE"
|
const val TYPE_VALUE = "UPDATE_TIMELIMIT_RULE"
|
||||||
|
@ -1454,6 +1455,7 @@ data class UpdateTimeLimitRuleAction(
|
||||||
private const val END = "end"
|
private const val END = "end"
|
||||||
private const val SESSION_DURATION_MILLISECONDS = "dur"
|
private const val SESSION_DURATION_MILLISECONDS = "dur"
|
||||||
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
||||||
|
private const val PER_DAY = "perDay"
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -1492,6 +1494,8 @@ data class UpdateTimeLimitRuleAction(
|
||||||
writer.name(SESSION_PAUSE_MILLISECONDS).value(sessionPauseMilliseconds)
|
writer.name(SESSION_PAUSE_MILLISECONDS).value(sessionPauseMilliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (perDay) writer.name(PER_DAY).value(true)
|
||||||
|
|
||||||
writer.endObject()
|
writer.endObject()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,7 +315,8 @@ object LocalDatabaseParentActionDispatcher {
|
||||||
startMinuteOfDay = action.start,
|
startMinuteOfDay = action.start,
|
||||||
endMinuteOfDay = action.end,
|
endMinuteOfDay = action.end,
|
||||||
sessionDurationMilliseconds = action.sessionDurationMilliseconds,
|
sessionDurationMilliseconds = action.sessionDurationMilliseconds,
|
||||||
sessionPauseMilliseconds = action.sessionPauseMilliseconds
|
sessionPauseMilliseconds = action.sessionPauseMilliseconds,
|
||||||
|
perDay = action.perDay
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
is SetDeviceUserAction -> {
|
is SetDeviceUserAction -> {
|
||||||
|
|
|
@ -821,7 +821,8 @@ data class ServerTimeLimitRule(
|
||||||
val startMinuteOfDay: Int,
|
val startMinuteOfDay: Int,
|
||||||
val endMinuteOfDay: Int,
|
val endMinuteOfDay: Int,
|
||||||
val sessionDurationMilliseconds: Int,
|
val sessionDurationMilliseconds: Int,
|
||||||
val sessionPauseMilliseconds: Int
|
val sessionPauseMilliseconds: Int,
|
||||||
|
val perDay: Boolean
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val ID = "id"
|
private const val ID = "id"
|
||||||
|
@ -832,6 +833,7 @@ data class ServerTimeLimitRule(
|
||||||
private const val END_MINUTE_OF_DAY = "end"
|
private const val END_MINUTE_OF_DAY = "end"
|
||||||
private const val SESSION_DURATION_MILLISECONDS = "session"
|
private const val SESSION_DURATION_MILLISECONDS = "session"
|
||||||
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
||||||
|
private const val PER_DAY = "perDay"
|
||||||
|
|
||||||
fun parse(reader: JsonReader): ServerTimeLimitRule {
|
fun parse(reader: JsonReader): ServerTimeLimitRule {
|
||||||
var id: String? = null
|
var id: String? = null
|
||||||
|
@ -842,6 +844,7 @@ data class ServerTimeLimitRule(
|
||||||
var endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE
|
var endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE
|
||||||
var sessionDurationMilliseconds: Int = 0
|
var sessionDurationMilliseconds: Int = 0
|
||||||
var sessionPauseMilliseconds: Int = 0
|
var sessionPauseMilliseconds: Int = 0
|
||||||
|
var perDay = false
|
||||||
|
|
||||||
reader.beginObject()
|
reader.beginObject()
|
||||||
while (reader.hasNext()) {
|
while (reader.hasNext()) {
|
||||||
|
@ -854,6 +857,7 @@ data class ServerTimeLimitRule(
|
||||||
END_MINUTE_OF_DAY -> endMinuteOfDay = reader.nextInt()
|
END_MINUTE_OF_DAY -> endMinuteOfDay = reader.nextInt()
|
||||||
SESSION_DURATION_MILLISECONDS -> sessionDurationMilliseconds = reader.nextInt()
|
SESSION_DURATION_MILLISECONDS -> sessionDurationMilliseconds = reader.nextInt()
|
||||||
SESSION_PAUSE_MILLISECONDS -> sessionPauseMilliseconds = reader.nextInt()
|
SESSION_PAUSE_MILLISECONDS -> sessionPauseMilliseconds = reader.nextInt()
|
||||||
|
PER_DAY -> perDay = reader.nextBoolean()
|
||||||
else -> reader.skipValue()
|
else -> reader.skipValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -867,7 +871,8 @@ data class ServerTimeLimitRule(
|
||||||
startMinuteOfDay = startMinuteOfDay,
|
startMinuteOfDay = startMinuteOfDay,
|
||||||
endMinuteOfDay = endMinuteOfDay,
|
endMinuteOfDay = endMinuteOfDay,
|
||||||
sessionDurationMilliseconds = sessionDurationMilliseconds,
|
sessionDurationMilliseconds = sessionDurationMilliseconds,
|
||||||
sessionPauseMilliseconds = sessionPauseMilliseconds
|
sessionPauseMilliseconds = sessionPauseMilliseconds,
|
||||||
|
perDay = perDay
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,7 +898,8 @@ data class ServerTimeLimitRule(
|
||||||
startMinuteOfDay = startMinuteOfDay,
|
startMinuteOfDay = startMinuteOfDay,
|
||||||
endMinuteOfDay = endMinuteOfDay,
|
endMinuteOfDay = endMinuteOfDay,
|
||||||
sessionDurationMilliseconds = sessionDurationMilliseconds,
|
sessionDurationMilliseconds = sessionDurationMilliseconds,
|
||||||
sessionPauseMilliseconds = sessionPauseMilliseconds
|
sessionPauseMilliseconds = sessionPauseMilliseconds,
|
||||||
|
perDay = perDay
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
import io.timelimit.android.data.model.UsedTimeItem
|
import io.timelimit.android.data.model.UsedTimeItem
|
||||||
import io.timelimit.android.databinding.*
|
import io.timelimit.android.databinding.*
|
||||||
|
import io.timelimit.android.date.DateInTimezone
|
||||||
import io.timelimit.android.extensions.MinuteOfDay
|
import io.timelimit.android.extensions.MinuteOfDay
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
import io.timelimit.android.logic.DummyApps
|
import io.timelimit.android.logic.DummyApps
|
||||||
|
@ -46,7 +47,7 @@ class AppAndRuleAdapter: RecyclerView.Adapter<AppAndRuleAdapter.Holder>() {
|
||||||
|
|
||||||
var items: List<AppAndRuleItem> by Delegates.observable(emptyList()) { _, _, _ -> notifyDataSetChanged() }
|
var items: List<AppAndRuleItem> by Delegates.observable(emptyList()) { _, _, _ -> notifyDataSetChanged() }
|
||||||
var usedTimes: List<UsedTimeItem> by Delegates.observable(emptyList()) { _, _, _ -> notifyDataSetChanged() }
|
var usedTimes: List<UsedTimeItem> by Delegates.observable(emptyList()) { _, _, _ -> notifyDataSetChanged() }
|
||||||
var epochDayOfStartOfWeek: Int by Delegates.observable(0) { _, _, _ -> notifyDataSetChanged() }
|
var date: DateInTimezone? by Delegates.observable(null as DateInTimezone?) { _, _, _ -> notifyDataSetChanged() }
|
||||||
var handlers: Handlers? = null
|
var handlers: Handlers? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -153,13 +154,29 @@ class AppAndRuleAdapter: RecyclerView.Adapter<AppAndRuleAdapter.Holder>() {
|
||||||
val binding = holder.itemView.tag as FragmentCategoryTimeLimitRuleItemBinding
|
val binding = holder.itemView.tag as FragmentCategoryTimeLimitRuleItemBinding
|
||||||
val context = binding.root.context
|
val context = binding.root.context
|
||||||
val dayNames = binding.root.resources.getStringArray(R.array.days_of_week_array)
|
val dayNames = binding.root.resources.getStringArray(R.array.days_of_week_array)
|
||||||
val usedTime = usedTimes.filter { usedTime ->
|
val usedTime = date?.let { date ->
|
||||||
val dayOfWeek = usedTime.dayOfEpoch - epochDayOfStartOfWeek
|
usedTimes.filter { usedTime ->
|
||||||
usedTime.startTimeOfDay == rule.startMinuteOfDay && usedTime.endTimeOfDay == rule.endMinuteOfDay &&
|
val dayOfWeek = usedTime.dayOfEpoch - date.firstDayOfWeekAsEpochDay
|
||||||
(rule.dayMask.toInt() and (1 shl dayOfWeek) != 0)
|
val matchingSlot = usedTime.startTimeOfDay == rule.startMinuteOfDay && usedTime.endTimeOfDay == rule.endMinuteOfDay
|
||||||
}.map { it.usedMillis }.sum().toInt()
|
val matchingMask = (rule.dayMask.toInt() and (1 shl dayOfWeek) != 0)
|
||||||
|
val matchingDay = dayOfWeek == date.dayOfWeek
|
||||||
|
|
||||||
binding.maxTimeString = TimeTextUtil.time(rule.maximumTimeInMillis, context)
|
matchingSlot && (if (rule.perDay) matchingDay else matchingMask)
|
||||||
|
}.map { it.usedMillis }.sum().toInt()
|
||||||
|
} ?: 0
|
||||||
|
|
||||||
|
binding.maxTimeString = rule.maximumTimeInMillis.let { time ->
|
||||||
|
val timeString = TimeTextUtil.time(time, context)
|
||||||
|
val weeklyDailyString = context.getString(
|
||||||
|
if (rule.perDay) R.string.category_time_limit_rules_per_day
|
||||||
|
else R.string.category_time_limit_rules_per_week
|
||||||
|
)
|
||||||
|
val zeroTime = time == 0
|
||||||
|
val onlySingleDay = rule.dayMask.countOneBits() <= 1
|
||||||
|
val hideDailyWeekly = zeroTime || onlySingleDay
|
||||||
|
|
||||||
|
if (hideDailyWeekly) timeString else "$weeklyDailyString $timeString"
|
||||||
|
}
|
||||||
binding.usageAsText = if (rule.maximumTimeInMillis > 0) TimeTextUtil.used(usedTime, context) else null
|
binding.usageAsText = if (rule.maximumTimeInMillis > 0) TimeTextUtil.used(usedTime, context) else null
|
||||||
binding.usageProgressInPercent = if (rule.maximumTimeInMillis > 0)
|
binding.usageProgressInPercent = if (rule.maximumTimeInMillis > 0)
|
||||||
(usedTime * 100 / rule.maximumTimeInMillis)
|
(usedTime * 100 / rule.maximumTimeInMillis)
|
||||||
|
|
|
@ -44,16 +44,14 @@ class AppsAndRulesModel(application: Application): AndroidViewModel(application)
|
||||||
|
|
||||||
private val userDayOfWeek = userDate.map { it.dayOfWeek }
|
private val userDayOfWeek = userDate.map { it.dayOfWeek }
|
||||||
|
|
||||||
val firstDayOfWeekAndUsedTimes = userDate.switchMap { date ->
|
val dateAndUsedTimes = userDate.switchMap { date ->
|
||||||
val firstDayOfWeekAsEpochDay = date.dayOfEpoch - date.dayOfWeek
|
|
||||||
|
|
||||||
categoryIdLive.switchMap { categoryId ->
|
categoryIdLive.switchMap { categoryId ->
|
||||||
database.usedTimes().getUsedTimesOfWeek(
|
database.usedTimes().getUsedTimesOfWeek(
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
firstDayOfWeekAsEpochDay = firstDayOfWeekAsEpochDay
|
firstDayOfWeekAsEpochDay = date.firstDayOfWeekAsEpochDay
|
||||||
)
|
)
|
||||||
}.map { usedTimes ->
|
}.map { usedTimes ->
|
||||||
firstDayOfWeekAsEpochDay to usedTimes
|
date to usedTimes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,8 +63,8 @@ abstract class CategoryAppsAndRulesFragment: Fragment(), Handlers, EditTimeLimit
|
||||||
recycler.layoutManager = LinearLayoutManager(requireContext())
|
recycler.layoutManager = LinearLayoutManager(requireContext())
|
||||||
recycler.adapter = adapter
|
recycler.adapter = adapter
|
||||||
|
|
||||||
model.firstDayOfWeekAndUsedTimes.observe(viewLifecycleOwner) { (firstDayOfWeek, usedTimes) ->
|
model.dateAndUsedTimes.observe(viewLifecycleOwner) { (date, usedTimes) ->
|
||||||
adapter.epochDayOfStartOfWeek = firstDayOfWeek
|
adapter.date = date
|
||||||
adapter.usedTimes = usedTimes
|
adapter.usedTimes = usedTimes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,8 @@ abstract class CategoryAppsAndRulesFragment: Fragment(), Handlers, EditTimeLimit
|
||||||
start = oldRule.startMinuteOfDay,
|
start = oldRule.startMinuteOfDay,
|
||||||
end = oldRule.endMinuteOfDay,
|
end = oldRule.endMinuteOfDay,
|
||||||
sessionDurationMilliseconds = oldRule.sessionDurationMilliseconds,
|
sessionDurationMilliseconds = oldRule.sessionDurationMilliseconds,
|
||||||
sessionPauseMilliseconds = oldRule.sessionPauseMilliseconds
|
sessionPauseMilliseconds = oldRule.sessionPauseMilliseconds,
|
||||||
|
perDay = oldRule.perDay
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,22 +28,18 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
import io.timelimit.android.async.Threads
|
import io.timelimit.android.async.Threads
|
||||||
import io.timelimit.android.coroutines.runAsync
|
|
||||||
import io.timelimit.android.data.IdGenerator
|
import io.timelimit.android.data.IdGenerator
|
||||||
import io.timelimit.android.data.model.HintsToShow
|
|
||||||
import io.timelimit.android.data.model.TimeLimitRule
|
import io.timelimit.android.data.model.TimeLimitRule
|
||||||
import io.timelimit.android.data.model.UserType
|
import io.timelimit.android.data.model.UserType
|
||||||
import io.timelimit.android.databinding.FragmentEditTimeLimitRuleDialogBinding
|
import io.timelimit.android.databinding.FragmentEditTimeLimitRuleDialogBinding
|
||||||
import io.timelimit.android.extensions.MinuteOfDay
|
import io.timelimit.android.extensions.MinuteOfDay
|
||||||
import io.timelimit.android.extensions.showSafe
|
import io.timelimit.android.extensions.showSafe
|
||||||
import io.timelimit.android.livedata.waitForNonNullValue
|
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
import io.timelimit.android.sync.actions.CreateTimeLimitRuleAction
|
import io.timelimit.android.sync.actions.CreateTimeLimitRuleAction
|
||||||
import io.timelimit.android.sync.actions.DeleteTimeLimitRuleAction
|
import io.timelimit.android.sync.actions.DeleteTimeLimitRuleAction
|
||||||
import io.timelimit.android.sync.actions.UpdateTimeLimitRuleAction
|
import io.timelimit.android.sync.actions.UpdateTimeLimitRuleAction
|
||||||
import io.timelimit.android.ui.main.ActivityViewModel
|
import io.timelimit.android.ui.main.ActivityViewModel
|
||||||
import io.timelimit.android.ui.main.getActivityViewModel
|
import io.timelimit.android.ui.main.getActivityViewModel
|
||||||
import io.timelimit.android.ui.mustread.MustReadFragment
|
|
||||||
import io.timelimit.android.ui.view.SelectDayViewHandlers
|
import io.timelimit.android.ui.view.SelectDayViewHandlers
|
||||||
import io.timelimit.android.ui.view.SelectTimeSpanViewListener
|
import io.timelimit.android.ui.view.SelectTimeSpanViewListener
|
||||||
import io.timelimit.android.util.TimeTextUtil
|
import io.timelimit.android.util.TimeTextUtil
|
||||||
|
@ -86,30 +82,14 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
||||||
if (existingRule != null) {
|
if (existingRule != null) {
|
||||||
existingRule!!.categoryId
|
existingRule!!.categoryId
|
||||||
} else {
|
} else {
|
||||||
arguments!!.getString(PARAM_CATEGORY_ID)!!
|
requireArguments().getString(PARAM_CATEGORY_ID)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private val auth: ActivityViewModel by lazy { getActivityViewModel(activity!!) }
|
private val auth: ActivityViewModel by lazy { getActivityViewModel(requireActivity()) }
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
val database = DefaultAppLogic.with(context!!).database
|
|
||||||
|
|
||||||
runAsync {
|
|
||||||
val wasShown = database.config().wereHintsShown(HintsToShow.TIMELIMIT_RULE_MUSTREAD).waitForNonNullValue()
|
|
||||||
|
|
||||||
if (!wasShown) {
|
|
||||||
MustReadFragment.newInstance(io.timelimit.android.R.string.must_read_timelimit_rules).show(fragmentManager!!)
|
|
||||||
|
|
||||||
Threads.database.execute {
|
|
||||||
database.config().setHintsShownSync(HintsToShow.TIMELIMIT_RULE_MUSTREAD)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
existingRule = savedInstanceState?.getParcelable(PARAM_EXISTING_RULE)
|
existingRule = savedInstanceState?.getParcelable(PARAM_EXISTING_RULE)
|
||||||
?: arguments?.getParcelable<TimeLimitRule?>(PARAM_EXISTING_RULE)
|
?: arguments?.getParcelable<TimeLimitRule?>(PARAM_EXISTING_RULE)
|
||||||
}
|
}
|
||||||
|
@ -124,23 +104,28 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
||||||
)
|
)
|
||||||
view.applyToExtraTime = newRule.applyToExtraTimeUsage
|
view.applyToExtraTime = newRule.applyToExtraTimeUsage
|
||||||
|
|
||||||
val affectedDays = Math.max(0, (0..6).map { (newRule.dayMask.toInt() shr it) and 1 }.sum())
|
val affectedDays = (0..6).map { (newRule.dayMask.toInt() shr it) and 1 }.sum()
|
||||||
view.timeSpan.maxDays = Math.max(0, affectedDays - 1) // max prevents crash
|
val maxDayDuration = if (newRule.perDay) 1 else affectedDays
|
||||||
|
|
||||||
|
view.timeSpan.maxDays = 0.coerceAtLeast(maxDayDuration - 1)
|
||||||
view.timeSpan.timeInMillis = newRule.maximumTimeInMillis.toLong()
|
view.timeSpan.timeInMillis = newRule.maximumTimeInMillis.toLong()
|
||||||
view.affectsMultipleDays = affectedDays >= 2
|
view.showWeeklyDailyOption = affectedDays >= 2 && newRule.maximumTimeInMillis > 0
|
||||||
|
|
||||||
view.applyToWholeDay = newRule.appliesToWholeDay
|
view.applyToWholeDay = newRule.appliesToWholeDay
|
||||||
view.startTime = MinuteOfDay.format(newRule.startMinuteOfDay)
|
view.startTime = MinuteOfDay.format(newRule.startMinuteOfDay)
|
||||||
view.endTime = MinuteOfDay.format(newRule.endMinuteOfDay)
|
view.endTime = MinuteOfDay.format(newRule.endMinuteOfDay)
|
||||||
|
|
||||||
view.enableSessionDurationLimit = newRule.sessionDurationLimitEnabled
|
view.enableSessionDurationLimit = newRule.sessionDurationLimitEnabled
|
||||||
view.sessionBreakText = TimeTextUtil.minutes(newRule.sessionPauseMilliseconds / (1000 * 60), context!!)
|
view.sessionBreakText = TimeTextUtil.minutes(newRule.sessionPauseMilliseconds / (1000 * 60), requireContext())
|
||||||
view.sessionLengthText = TimeTextUtil.minutes(newRule.sessionDurationMilliseconds / (1000 * 60), context!!)
|
view.sessionLengthText = TimeTextUtil.minutes(newRule.sessionDurationMilliseconds / (1000 * 60), requireContext())
|
||||||
|
|
||||||
|
view.typePerDay.isChecked = newRule.perDay
|
||||||
|
view.typePerWeek.isChecked = !newRule.perDay
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
val listener = targetFragment as EditTimeLimitRuleDialogFragmentListener
|
val listener = targetFragment as EditTimeLimitRuleDialogFragmentListener
|
||||||
val database = DefaultAppLogic.with(context!!).database
|
val database = DefaultAppLogic.with(requireContext()).database
|
||||||
|
|
||||||
view = FragmentEditTimeLimitRuleDialogBinding.inflate(layoutInflater, container, false)
|
view = FragmentEditTimeLimitRuleDialogBinding.inflate(layoutInflater, container, false)
|
||||||
|
|
||||||
|
@ -162,7 +147,8 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
||||||
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
||||||
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
||||||
sessionPauseMilliseconds = 0,
|
sessionPauseMilliseconds = 0,
|
||||||
sessionDurationMilliseconds = 0
|
sessionDurationMilliseconds = 0,
|
||||||
|
perDay = true
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
view.isNewRule = false
|
view.isNewRule = false
|
||||||
|
@ -278,7 +264,8 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
||||||
start = newRule.startMinuteOfDay,
|
start = newRule.startMinuteOfDay,
|
||||||
end = newRule.endMinuteOfDay,
|
end = newRule.endMinuteOfDay,
|
||||||
sessionDurationMilliseconds = newRule.sessionDurationMilliseconds,
|
sessionDurationMilliseconds = newRule.sessionDurationMilliseconds,
|
||||||
sessionPauseMilliseconds = newRule.sessionPauseMilliseconds
|
sessionPauseMilliseconds = newRule.sessionPauseMilliseconds,
|
||||||
|
perDay = newRule.perDay
|
||||||
)
|
)
|
||||||
)) {
|
)) {
|
||||||
return
|
return
|
||||||
|
@ -333,6 +320,9 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
view.typePerDay.setOnClickListener { newRule = newRule.copy(perDay = true); bindRule() }
|
||||||
|
view.typePerWeek.setOnClickListener { newRule = newRule.copy(perDay = false); bindRule() }
|
||||||
|
|
||||||
database.config().getEnableAlternativeDurationSelectionAsync().observe(viewLifecycleOwner, Observer {
|
database.config().getEnableAlternativeDurationSelectionAsync().observe(viewLifecycleOwner, Observer {
|
||||||
view.timeSpan.enablePickerMode(it)
|
view.timeSpan.enablePickerMode(it)
|
||||||
})
|
})
|
||||||
|
|
|
@ -42,44 +42,38 @@ class DefaultCategories private constructor(private val context: Context) {
|
||||||
val rules = mutableListOf<TimeLimitRule>()
|
val rules = mutableListOf<TimeLimitRule>()
|
||||||
|
|
||||||
// maximum time for each workday
|
// maximum time for each workday
|
||||||
for (day in 0..4) {
|
rules.add(
|
||||||
rules.add(
|
TimeLimitRule(
|
||||||
TimeLimitRule(
|
id = IdGenerator.generateId(),
|
||||||
id = IdGenerator.generateId(),
|
categoryId = categoryId,
|
||||||
categoryId = categoryId,
|
applyToExtraTimeUsage = false,
|
||||||
applyToExtraTimeUsage = false,
|
dayMask = (1 or 2 or 4 or 8 or 16).toByte(),
|
||||||
dayMask = (1 shl day).toByte(),
|
maximumTimeInMillis = 1000 * 60 * 30, // 30 minutes
|
||||||
maximumTimeInMillis = 1000 * 60 * 30, // 30 minutes
|
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
||||||
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
||||||
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
sessionPauseMilliseconds = 0,
|
||||||
sessionPauseMilliseconds = 0,
|
sessionDurationMilliseconds = 0,
|
||||||
sessionDurationMilliseconds = 0
|
perDay = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// maximum time for each weekend day
|
// maximum time for each weekend day
|
||||||
for (day in 5..6) {
|
rules.add(
|
||||||
rules.add(
|
TimeLimitRule(
|
||||||
TimeLimitRule(
|
id = IdGenerator.generateId(),
|
||||||
id = IdGenerator.generateId(),
|
categoryId = categoryId,
|
||||||
categoryId = categoryId,
|
applyToExtraTimeUsage = false,
|
||||||
applyToExtraTimeUsage = false,
|
dayMask = (32 or 64).toByte(),
|
||||||
dayMask = (1 shl day).toByte(),
|
maximumTimeInMillis = 1000 * 60 * 60 * 3, // 3 hours
|
||||||
maximumTimeInMillis = 1000 * 60 * 60 * 3, // 3 hours
|
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
||||||
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
||||||
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
sessionPauseMilliseconds = 0,
|
||||||
sessionPauseMilliseconds = 0,
|
sessionDurationMilliseconds = 0,
|
||||||
sessionDurationMilliseconds = 0
|
perDay = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// maximum time per total week
|
// maximum time per total week
|
||||||
val dayMask = BitSet()
|
|
||||||
|
|
||||||
dayMask.set(0, 7)
|
|
||||||
|
|
||||||
rules.add(
|
rules.add(
|
||||||
TimeLimitRule(
|
TimeLimitRule(
|
||||||
id = IdGenerator.generateId(),
|
id = IdGenerator.generateId(),
|
||||||
|
@ -90,7 +84,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
||||||
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
||||||
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
||||||
sessionPauseMilliseconds = 0,
|
sessionPauseMilliseconds = 0,
|
||||||
sessionDurationMilliseconds = 0
|
sessionDurationMilliseconds = 0,
|
||||||
|
perDay = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -105,7 +100,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
||||||
startMinuteOfDay = 0,
|
startMinuteOfDay = 0,
|
||||||
endMinuteOfDay = 6 * 60 - 1,
|
endMinuteOfDay = 6 * 60 - 1,
|
||||||
sessionPauseMilliseconds = 0,
|
sessionPauseMilliseconds = 0,
|
||||||
sessionDurationMilliseconds = 0
|
sessionDurationMilliseconds = 0,
|
||||||
|
perDay = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -119,7 +115,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
||||||
startMinuteOfDay = 18 * 60,
|
startMinuteOfDay = 18 * 60,
|
||||||
endMinuteOfDay = MinuteOfDay.MAX,
|
endMinuteOfDay = MinuteOfDay.MAX,
|
||||||
sessionPauseMilliseconds = 0,
|
sessionPauseMilliseconds = 0,
|
||||||
sessionDurationMilliseconds = 0
|
sessionDurationMilliseconds = 0,
|
||||||
|
perDay = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -134,7 +131,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
||||||
startMinuteOfDay = 0,
|
startMinuteOfDay = 0,
|
||||||
endMinuteOfDay = 9 * 60 - 1,
|
endMinuteOfDay = 9 * 60 - 1,
|
||||||
sessionPauseMilliseconds = 0,
|
sessionPauseMilliseconds = 0,
|
||||||
sessionDurationMilliseconds = 0
|
sessionDurationMilliseconds = 0,
|
||||||
|
perDay = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -148,7 +146,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
||||||
startMinuteOfDay = 20 * 60,
|
startMinuteOfDay = 20 * 60,
|
||||||
endMinuteOfDay = MinuteOfDay.MAX,
|
endMinuteOfDay = MinuteOfDay.MAX,
|
||||||
sessionPauseMilliseconds = 0,
|
sessionPauseMilliseconds = 0,
|
||||||
sessionDurationMilliseconds = 0
|
sessionDurationMilliseconds = 0,
|
||||||
|
perDay = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,10 @@
|
||||||
name="affectsMultipleDays"
|
name="affectsMultipleDays"
|
||||||
type="boolean" />
|
type="boolean" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="showWeeklyDailyOption"
|
||||||
|
type="boolean" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="startTime"
|
name="startTime"
|
||||||
type="String" />
|
type="String" />
|
||||||
|
@ -80,6 +84,27 @@
|
||||||
|
|
||||||
<include layout="@layout/view_select_days" android:id="@+id/day_selection" />
|
<include layout="@layout/view_select_days" android:id="@+id/day_selection" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||||
|
android:visibility="@{showWeeklyDailyOption ? View.VISIBLE : View.GONE}"
|
||||||
|
android:id="@+id/per_day_group"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/type_per_day"
|
||||||
|
style="?materialButtonOutlinedStyle"
|
||||||
|
android:text="@string/category_time_limit_rules_per_day"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/type_per_week"
|
||||||
|
style="?materialButtonOutlinedStyle"
|
||||||
|
android:text="@string/category_time_limit_rules_per_week"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||||
|
|
||||||
<io.timelimit.android.ui.view.SelectTimeSpanView
|
<io.timelimit.android.ui.view.SelectTimeSpanView
|
||||||
android:id="@+id/time_span"
|
android:id="@+id/time_span"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -107,8 +132,9 @@
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
android:textAppearance="?android:textAppearanceSmall"
|
||||||
android:padding="8dp"
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
android:text="@string/category_time_limit_rules_apply_to_part_day_1"
|
android:text="@string/category_time_limit_rules_apply_to_part_day_1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
@ -122,7 +148,7 @@
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
android:textAppearance="?android:textAppearanceSmall"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:text="@string/category_time_limit_rules_apply_to_part_day_2"
|
android:text="@string/category_time_limit_rules_apply_to_part_day_2"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -161,10 +187,10 @@
|
||||||
app:layout_gravity="fill_vertical|fill_horizontal"
|
app:layout_gravity="fill_vertical|fill_horizontal"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingStart="8dp"
|
android:paddingStart="0dp"
|
||||||
app:layout_row="1"
|
app:layout_row="1"
|
||||||
app:layout_column="1"
|
app:layout_column="1"
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
android:textAppearance="?android:textAppearanceSmall"
|
||||||
android:text="@string/category_time_limit_rules_session_limit_duration"
|
android:text="@string/category_time_limit_rules_session_limit_duration"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
@ -187,8 +213,8 @@
|
||||||
app:layout_gravity="fill_vertical|fill_horizontal"
|
app:layout_gravity="fill_vertical|fill_horizontal"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingStart="8dp"
|
android:paddingStart="0dp"
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
android:textAppearance="?android:textAppearanceSmall"
|
||||||
app:layout_row="2"
|
app:layout_row="2"
|
||||||
app:layout_column="1"
|
app:layout_column="1"
|
||||||
android:text="@string/category_time_limit_rules_session_limit_pause"
|
android:text="@string/category_time_limit_rules_session_limit_pause"
|
||||||
|
@ -217,13 +243,6 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:visibility="@{safeUnbox(affectsMultipleDays) ? View.VISIBLE : View.GONE}"
|
|
||||||
android:textAppearance="?android:textAppearanceSmall"
|
|
||||||
android:text="@string/category_time_limit_rules_warning_multiple_days"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -327,11 +327,9 @@
|
||||||
<string name="category_time_limit_rules_snackbar_created">Regel wurde erstellt</string>
|
<string name="category_time_limit_rules_snackbar_created">Regel wurde erstellt</string>
|
||||||
<string name="category_time_limit_rules_snackbar_updated">Regel wurde geändert</string>
|
<string name="category_time_limit_rules_snackbar_updated">Regel wurde geändert</string>
|
||||||
<string name="category_time_limit_rules_snackbar_deleted">Regel wurde gelöscht</string>
|
<string name="category_time_limit_rules_snackbar_deleted">Regel wurde gelöscht</string>
|
||||||
|
<string name="category_time_limit_rules_per_day">täglich</string>
|
||||||
|
<string name="category_time_limit_rules_per_week">wöchentlich</string>
|
||||||
|
|
||||||
<string name="category_time_limit_rules_warning_multiple_days">Diese Regel
|
|
||||||
wird die Gesamtnutzungsdauer in einer Woche an den gewählten Tagen einschränken.
|
|
||||||
Wenn Sei die Begrenzung je Tag wollen, dann erstellen Sie eine Regel je Tag.
|
|
||||||
</string>
|
|
||||||
<string name="category_time_limit_rules_warning_day_part">Diese Regel begrenzt nur
|
<string name="category_time_limit_rules_warning_day_part">Diese Regel begrenzt nur
|
||||||
die Nutzung im angegebenen Intervall. Ohne Regeln für andere Intervalle
|
die Nutzung im angegebenen Intervall. Ohne Regeln für andere Intervalle
|
||||||
oder Sperrzeiten wird es keine Begrenzung außerhalb dieses Intervalls
|
oder Sperrzeiten wird es keine Begrenzung außerhalb dieses Intervalls
|
||||||
|
@ -1034,14 +1032,6 @@
|
||||||
\n2. Wenn \"es nicht funktioniert\" liegt es oft an Energiesparfunktionen die deaktiviert werden müssen.
|
\n2. Wenn \"es nicht funktioniert\" liegt es oft an Energiesparfunktionen die deaktiviert werden müssen.
|
||||||
Diese sind oft automatisch aktiviert, ohne das es Ihnen als Nutzer bewusst ist.
|
Diese sind oft automatisch aktiviert, ohne das es Ihnen als Nutzer bewusst ist.
|
||||||
</string>
|
</string>
|
||||||
<string name="must_read_timelimit_rules">
|
|
||||||
Da es manchmal falsch bedient wird:
|
|
||||||
Eine Regel begrenzt die Gesamtnutzungsdauer der Kategorie in einer Woche an den gewählten Tagen.
|
|
||||||
Das bedeutet, dass wenn eine Regel für Montag bis Freitag mit 3 Stunden eingestellt wird,
|
|
||||||
die Kategorie in jeder Woche von Montag bis Freitag insgesamt nur 3 Stunden benutzt werden kann.
|
|
||||||
Wenn je eine Regel für die Tage von Montag bis Freitag mit jeweils 3 Stunden gewählt wird,
|
|
||||||
kann die Kategorie an jedem dieser Tage für 3 Stunden genutzt werden.
|
|
||||||
</string>
|
|
||||||
<string name="must_read_add_already_assigned_apps">
|
<string name="must_read_add_already_assigned_apps">
|
||||||
Apps mit Kategorie werden beim Hinzufügen aus der vorherigen Kategorie entfernt - für mehrere Kategorien gleichzeitig können Sie Ober- und Unter-Kategorien verwenden
|
Apps mit Kategorie werden beim Hinzufügen aus der vorherigen Kategorie entfernt - für mehrere Kategorien gleichzeitig können Sie Ober- und Unter-Kategorien verwenden
|
||||||
</string>
|
</string>
|
||||||
|
|
|
@ -962,14 +962,6 @@ Esta %s só é permitida em algumas redes, mas nenhuma conexão com tal rede foi
|
||||||
\n2nd: Se \"não funciona\" então isto é muito provavelmente causado por recursos de economia da bateria.
|
\n2nd: Se \"não funciona\" então isto é muito provavelmente causado por recursos de economia da bateria.
|
||||||
Estos recursos são freqüentemente ativadas mesmo que você não o tenha feito.
|
Estos recursos são freqüentemente ativadas mesmo que você não o tenha feito.
|
||||||
</string>
|
</string>
|
||||||
<string name="must_read_timelimit_rules">
|
|
||||||
Isso acontece às vezes porque é configurado de forma errada :
|
|
||||||
Uma regra limita a duração total de uso de uma categoria nos dias selecionados da semana.
|
|
||||||
Se houver uma regra de segunda-feira a sexta-feira com 3 horas,
|
|
||||||
então há um limite total de 3 horas por semana, de segunda a sexta.
|
|
||||||
Se houver uma regra por dia com 3 horas,
|
|
||||||
então há um limite de 3 horas por dia.
|
|
||||||
</string>
|
|
||||||
<string name="must_read_add_already_assigned_apps">
|
<string name="must_read_add_already_assigned_apps">
|
||||||
Os aplicativos com categoria são excluídos da categoria anterior quando são adicionados a uma nova categoria -
|
Os aplicativos com categoria são excluídos da categoria anterior quando são adicionados a uma nova categoria -
|
||||||
usar categorias de Pais e filhos para \"adicionar\" um aplicativo a múltiplas categorias
|
usar categorias de Pais e filhos para \"adicionar\" um aplicativo a múltiplas categorias
|
||||||
|
|
|
@ -367,15 +367,13 @@
|
||||||
<string name="category_time_limit_rules_enable_session_limit">Limit session duration</string>
|
<string name="category_time_limit_rules_enable_session_limit">Limit session duration</string>
|
||||||
<string name="category_time_limit_rules_session_limit_duration">Maximum session duration</string>
|
<string name="category_time_limit_rules_session_limit_duration">Maximum session duration</string>
|
||||||
<string name="category_time_limit_rules_session_limit_pause">Break duration after session</string>
|
<string name="category_time_limit_rules_session_limit_pause">Break duration after session</string>
|
||||||
|
<string name="category_time_limit_rules_per_day">daily</string>
|
||||||
|
<string name="category_time_limit_rules_per_week">weekly</string>
|
||||||
|
|
||||||
<string name="category_time_limit_rules_snackbar_created">Rule was created</string>
|
<string name="category_time_limit_rules_snackbar_created">Rule was created</string>
|
||||||
<string name="category_time_limit_rules_snackbar_updated">Rule was modified</string>
|
<string name="category_time_limit_rules_snackbar_updated">Rule was modified</string>
|
||||||
<string name="category_time_limit_rules_snackbar_deleted">Rule was deleted</string>
|
<string name="category_time_limit_rules_snackbar_deleted">Rule was deleted</string>
|
||||||
|
|
||||||
<string name="category_time_limit_rules_warning_multiple_days">This rule
|
|
||||||
will limit the total usage duration during one week at the selected days.
|
|
||||||
If you want it per day, create one rule per day.
|
|
||||||
</string>
|
|
||||||
<string name="category_time_limit_rules_warning_day_part">This rule
|
<string name="category_time_limit_rules_warning_day_part">This rule
|
||||||
will limit the usage duration only in the specified interval.
|
will limit the usage duration only in the specified interval.
|
||||||
Without rules for other times or blocked time areas, there will
|
Without rules for other times or blocked time areas, there will
|
||||||
|
@ -1077,14 +1075,6 @@
|
||||||
\n2nd: If \"it does not work\" then this is most likely caused by power saving features.
|
\n2nd: If \"it does not work\" then this is most likely caused by power saving features.
|
||||||
This features are often enabled even if you did not enable it actively.
|
This features are often enabled even if you did not enable it actively.
|
||||||
</string>
|
</string>
|
||||||
<string name="must_read_timelimit_rules">
|
|
||||||
Because it happens sometimes that it is configured wrongly:
|
|
||||||
A rule limits the total usage duration of a category at the selected days of the week.
|
|
||||||
If there is a rule from monday to friday with 3 hours,
|
|
||||||
then there is a limit of total 3 hours per week from monday to friday.
|
|
||||||
If there is one rule per day with 3 hours,
|
|
||||||
then there is a limit of 3 hours per day.
|
|
||||||
</string>
|
|
||||||
<string name="must_read_add_already_assigned_apps">
|
<string name="must_read_add_already_assigned_apps">
|
||||||
Apps with category are removed from the previous category when they are added to a new category -
|
Apps with category are removed from the previous category when they are added to a new category -
|
||||||
use parent and child categories to \"add\" an App to multiple categories
|
use parent and child categories to \"add\" an App to multiple categories
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue