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 ''")
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
CategoryNetworkId::class,
|
||||
ChildTask::class
|
||||
], version = 34)
|
||||
], version = 35)
|
||||
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
|
||||
companion 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_V32,
|
||||
DatabaseMigrations.MIGRATE_TO_V33,
|
||||
DatabaseMigrations.MIGRATE_TO_V34
|
||||
DatabaseMigrations.MIGRATE_TO_V34,
|
||||
DatabaseMigrations.MIGRATE_TO_V35
|
||||
)
|
||||
.setQueryExecutor(Threads.database)
|
||||
.build()
|
||||
|
|
|
@ -211,7 +211,7 @@ object HintsToShow {
|
|||
const val CATEGORIES_INTRODUCTION = 4L
|
||||
const val TIME_LIMIT_RULE_INTRODUCTION = 8L
|
||||
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 TASKS_INTRODUCTION = 128L
|
||||
}
|
||||
|
|
|
@ -53,7 +53,9 @@ data class TimeLimitRule(
|
|||
@ColumnInfo(name = "session_duration_milliseconds")
|
||||
val sessionDurationMilliseconds: Int,
|
||||
@ColumnInfo(name = "session_pause_milliseconds")
|
||||
val sessionPauseMilliseconds: Int
|
||||
val sessionPauseMilliseconds: Int,
|
||||
@ColumnInfo(name = "per_day")
|
||||
val perDay: Boolean
|
||||
): Parcelable, JsonSerializable {
|
||||
companion object {
|
||||
private const val RULE_ID = "ruleId"
|
||||
|
@ -65,6 +67,7 @@ data class TimeLimitRule(
|
|||
private const val END_MINUTE_OF_DAY = "end"
|
||||
private const val SESSION_DURATION_MILLISECONDS = "dur"
|
||||
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
||||
private const val PER_DAY = "perDay"
|
||||
|
||||
const val MIN_START_MINUTE = MinuteOfDay.MIN
|
||||
const val MAX_END_MINUTE = MinuteOfDay.MAX
|
||||
|
@ -79,6 +82,7 @@ data class TimeLimitRule(
|
|||
var endMinuteOfDay = MAX_END_MINUTE
|
||||
var sessionDurationMilliseconds = 0
|
||||
var sessionPauseMilliseconds = 0
|
||||
var perDay = false
|
||||
|
||||
reader.beginObject()
|
||||
|
||||
|
@ -93,6 +97,7 @@ data class TimeLimitRule(
|
|||
END_MINUTE_OF_DAY -> endMinuteOfDay = reader.nextInt()
|
||||
SESSION_DURATION_MILLISECONDS -> sessionDurationMilliseconds = reader.nextInt()
|
||||
SESSION_PAUSE_MILLISECONDS -> sessionPauseMilliseconds = reader.nextInt()
|
||||
PER_DAY -> perDay = reader.nextBoolean()
|
||||
else -> reader.skipValue()
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +113,8 @@ data class TimeLimitRule(
|
|||
startMinuteOfDay = startMinuteOfDay,
|
||||
endMinuteOfDay = endMinuteOfDay,
|
||||
sessionDurationMilliseconds = sessionDurationMilliseconds,
|
||||
sessionPauseMilliseconds = sessionPauseMilliseconds
|
||||
sessionPauseMilliseconds = sessionPauseMilliseconds,
|
||||
perDay = perDay
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +165,8 @@ data class TimeLimitRule(
|
|||
writer.name(SESSION_PAUSE_MILLISECONDS).value(sessionPauseMilliseconds)
|
||||
}
|
||||
|
||||
if (perDay) writer.name(PER_DAY).value(true)
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import org.threeten.bp.LocalDate
|
|||
import java.util.*
|
||||
|
||||
data class DateInTimezone(val dayOfWeek: Int, val dayOfEpoch: Int, val localDate: LocalDate) {
|
||||
val firstDayOfWeekAsEpochDay get() = dayOfEpoch - dayOfWeek
|
||||
|
||||
companion object {
|
||||
fun convertDayOfWeek(dayOfWeek: Int) = when(dayOfWeek) {
|
||||
Calendar.MONDAY -> 0
|
||||
|
|
|
@ -57,8 +57,8 @@ data class RemainingTime(val includingExtraTime: Long, val default: Long) {
|
|||
}
|
||||
|
||||
val relatedRules = getRulesRelatedToDay(dayOfWeek, minuteOfDay, rules)
|
||||
val withoutExtraTime = getRemainingTime(usedTimes, relatedRules, false, firstDayOfWeekAsEpochDay)
|
||||
val withExtraTime = getRemainingTime(usedTimes, relatedRules, true, firstDayOfWeekAsEpochDay)
|
||||
val withoutExtraTime = getRemainingTime(usedTimes, relatedRules, false, firstDayOfWeekAsEpochDay, dayOfWeek)
|
||||
val withExtraTime = getRemainingTime(usedTimes, relatedRules, true, firstDayOfWeekAsEpochDay, dayOfWeek)
|
||||
|
||||
if (withoutExtraTime == null && withExtraTime == null) {
|
||||
// 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 ->
|
||||
var usedTime = 0L
|
||||
|
||||
usedTimes.forEach { usedTimeItem ->
|
||||
if (usedTimeItem.dayOfEpoch >= firstDayOfWeekAsEpochDay && usedTimeItem.dayOfEpoch <= firstDayOfWeekAsEpochDay + 6) {
|
||||
val usedTimeItemDayOfWeek = usedTimeItem.dayOfEpoch - firstDayOfWeekAsEpochDay
|
||||
val doesWeekMatch = usedTimeItem.dayOfEpoch >= firstDayOfWeekAsEpochDay && usedTimeItem.dayOfEpoch <= firstDayOfWeekAsEpochDay + 6
|
||||
|
||||
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) {
|
||||
usedTime += usedTimeItem.usedMillis
|
||||
}
|
||||
|
@ -106,7 +112,7 @@ data class RemainingTime(val includingExtraTime: Long, val default: Long) {
|
|||
val remaining = Math.max(0, maxTime - usedTime)
|
||||
|
||||
remaining
|
||||
}.min()
|
||||
}.minOrNull()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1442,7 +1442,8 @@ data class CreateTimeLimitRuleAction(val rule: TimeLimitRule): ParentAction() {
|
|||
|
||||
data class UpdateTimeLimitRuleAction(
|
||||
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() {
|
||||
companion object {
|
||||
const val TYPE_VALUE = "UPDATE_TIMELIMIT_RULE"
|
||||
|
@ -1454,6 +1455,7 @@ data class UpdateTimeLimitRuleAction(
|
|||
private const val END = "end"
|
||||
private const val SESSION_DURATION_MILLISECONDS = "dur"
|
||||
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
||||
private const val PER_DAY = "perDay"
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -1492,6 +1494,8 @@ data class UpdateTimeLimitRuleAction(
|
|||
writer.name(SESSION_PAUSE_MILLISECONDS).value(sessionPauseMilliseconds)
|
||||
}
|
||||
|
||||
if (perDay) writer.name(PER_DAY).value(true)
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -315,7 +315,8 @@ object LocalDatabaseParentActionDispatcher {
|
|||
startMinuteOfDay = action.start,
|
||||
endMinuteOfDay = action.end,
|
||||
sessionDurationMilliseconds = action.sessionDurationMilliseconds,
|
||||
sessionPauseMilliseconds = action.sessionPauseMilliseconds
|
||||
sessionPauseMilliseconds = action.sessionPauseMilliseconds,
|
||||
perDay = action.perDay
|
||||
))
|
||||
}
|
||||
is SetDeviceUserAction -> {
|
||||
|
|
|
@ -821,7 +821,8 @@ data class ServerTimeLimitRule(
|
|||
val startMinuteOfDay: Int,
|
||||
val endMinuteOfDay: Int,
|
||||
val sessionDurationMilliseconds: Int,
|
||||
val sessionPauseMilliseconds: Int
|
||||
val sessionPauseMilliseconds: Int,
|
||||
val perDay: Boolean
|
||||
) {
|
||||
companion object {
|
||||
private const val ID = "id"
|
||||
|
@ -832,6 +833,7 @@ data class ServerTimeLimitRule(
|
|||
private const val END_MINUTE_OF_DAY = "end"
|
||||
private const val SESSION_DURATION_MILLISECONDS = "session"
|
||||
private const val SESSION_PAUSE_MILLISECONDS = "pause"
|
||||
private const val PER_DAY = "perDay"
|
||||
|
||||
fun parse(reader: JsonReader): ServerTimeLimitRule {
|
||||
var id: String? = null
|
||||
|
@ -842,6 +844,7 @@ data class ServerTimeLimitRule(
|
|||
var endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE
|
||||
var sessionDurationMilliseconds: Int = 0
|
||||
var sessionPauseMilliseconds: Int = 0
|
||||
var perDay = false
|
||||
|
||||
reader.beginObject()
|
||||
while (reader.hasNext()) {
|
||||
|
@ -854,6 +857,7 @@ data class ServerTimeLimitRule(
|
|||
END_MINUTE_OF_DAY -> endMinuteOfDay = reader.nextInt()
|
||||
SESSION_DURATION_MILLISECONDS -> sessionDurationMilliseconds = reader.nextInt()
|
||||
SESSION_PAUSE_MILLISECONDS -> sessionPauseMilliseconds = reader.nextInt()
|
||||
PER_DAY -> perDay = reader.nextBoolean()
|
||||
else -> reader.skipValue()
|
||||
}
|
||||
}
|
||||
|
@ -867,7 +871,8 @@ data class ServerTimeLimitRule(
|
|||
startMinuteOfDay = startMinuteOfDay,
|
||||
endMinuteOfDay = endMinuteOfDay,
|
||||
sessionDurationMilliseconds = sessionDurationMilliseconds,
|
||||
sessionPauseMilliseconds = sessionPauseMilliseconds
|
||||
sessionPauseMilliseconds = sessionPauseMilliseconds,
|
||||
perDay = perDay
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -893,7 +898,8 @@ data class ServerTimeLimitRule(
|
|||
startMinuteOfDay = startMinuteOfDay,
|
||||
endMinuteOfDay = endMinuteOfDay,
|
||||
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.data.model.UsedTimeItem
|
||||
import io.timelimit.android.databinding.*
|
||||
import io.timelimit.android.date.DateInTimezone
|
||||
import io.timelimit.android.extensions.MinuteOfDay
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
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 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
|
||||
|
||||
init {
|
||||
|
@ -153,13 +154,29 @@ class AppAndRuleAdapter: RecyclerView.Adapter<AppAndRuleAdapter.Holder>() {
|
|||
val binding = holder.itemView.tag as FragmentCategoryTimeLimitRuleItemBinding
|
||||
val context = binding.root.context
|
||||
val dayNames = binding.root.resources.getStringArray(R.array.days_of_week_array)
|
||||
val usedTime = usedTimes.filter { usedTime ->
|
||||
val dayOfWeek = usedTime.dayOfEpoch - epochDayOfStartOfWeek
|
||||
usedTime.startTimeOfDay == rule.startMinuteOfDay && usedTime.endTimeOfDay == rule.endMinuteOfDay &&
|
||||
(rule.dayMask.toInt() and (1 shl dayOfWeek) != 0)
|
||||
}.map { it.usedMillis }.sum().toInt()
|
||||
val usedTime = date?.let { date ->
|
||||
usedTimes.filter { usedTime ->
|
||||
val dayOfWeek = usedTime.dayOfEpoch - date.firstDayOfWeekAsEpochDay
|
||||
val matchingSlot = usedTime.startTimeOfDay == rule.startMinuteOfDay && usedTime.endTimeOfDay == rule.endMinuteOfDay
|
||||
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.usageProgressInPercent = if (rule.maximumTimeInMillis > 0)
|
||||
(usedTime * 100 / rule.maximumTimeInMillis)
|
||||
|
|
|
@ -44,16 +44,14 @@ class AppsAndRulesModel(application: Application): AndroidViewModel(application)
|
|||
|
||||
private val userDayOfWeek = userDate.map { it.dayOfWeek }
|
||||
|
||||
val firstDayOfWeekAndUsedTimes = userDate.switchMap { date ->
|
||||
val firstDayOfWeekAsEpochDay = date.dayOfEpoch - date.dayOfWeek
|
||||
|
||||
val dateAndUsedTimes = userDate.switchMap { date ->
|
||||
categoryIdLive.switchMap { categoryId ->
|
||||
database.usedTimes().getUsedTimesOfWeek(
|
||||
categoryId = categoryId,
|
||||
firstDayOfWeekAsEpochDay = firstDayOfWeekAsEpochDay
|
||||
firstDayOfWeekAsEpochDay = date.firstDayOfWeekAsEpochDay
|
||||
)
|
||||
}.map { usedTimes ->
|
||||
firstDayOfWeekAsEpochDay to usedTimes
|
||||
date to usedTimes
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,8 @@ abstract class CategoryAppsAndRulesFragment: Fragment(), Handlers, EditTimeLimit
|
|||
recycler.layoutManager = LinearLayoutManager(requireContext())
|
||||
recycler.adapter = adapter
|
||||
|
||||
model.firstDayOfWeekAndUsedTimes.observe(viewLifecycleOwner) { (firstDayOfWeek, usedTimes) ->
|
||||
adapter.epochDayOfStartOfWeek = firstDayOfWeek
|
||||
model.dateAndUsedTimes.observe(viewLifecycleOwner) { (date, usedTimes) ->
|
||||
adapter.date = date
|
||||
adapter.usedTimes = usedTimes
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,8 @@ abstract class CategoryAppsAndRulesFragment: Fragment(), Handlers, EditTimeLimit
|
|||
start = oldRule.startMinuteOfDay,
|
||||
end = oldRule.endMinuteOfDay,
|
||||
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 io.timelimit.android.R
|
||||
import io.timelimit.android.async.Threads
|
||||
import io.timelimit.android.coroutines.runAsync
|
||||
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.UserType
|
||||
import io.timelimit.android.databinding.FragmentEditTimeLimitRuleDialogBinding
|
||||
import io.timelimit.android.extensions.MinuteOfDay
|
||||
import io.timelimit.android.extensions.showSafe
|
||||
import io.timelimit.android.livedata.waitForNonNullValue
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.sync.actions.CreateTimeLimitRuleAction
|
||||
import io.timelimit.android.sync.actions.DeleteTimeLimitRuleAction
|
||||
import io.timelimit.android.sync.actions.UpdateTimeLimitRuleAction
|
||||
import io.timelimit.android.ui.main.ActivityViewModel
|
||||
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.SelectTimeSpanViewListener
|
||||
import io.timelimit.android.util.TimeTextUtil
|
||||
|
@ -86,30 +82,14 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
|||
if (existingRule != null) {
|
||||
existingRule!!.categoryId
|
||||
} 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?) {
|
||||
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)
|
||||
?: arguments?.getParcelable<TimeLimitRule?>(PARAM_EXISTING_RULE)
|
||||
}
|
||||
|
@ -124,23 +104,28 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
|||
)
|
||||
view.applyToExtraTime = newRule.applyToExtraTimeUsage
|
||||
|
||||
val affectedDays = Math.max(0, (0..6).map { (newRule.dayMask.toInt() shr it) and 1 }.sum())
|
||||
view.timeSpan.maxDays = Math.max(0, affectedDays - 1) // max prevents crash
|
||||
val affectedDays = (0..6).map { (newRule.dayMask.toInt() shr it) and 1 }.sum()
|
||||
val maxDayDuration = if (newRule.perDay) 1 else affectedDays
|
||||
|
||||
view.timeSpan.maxDays = 0.coerceAtLeast(maxDayDuration - 1)
|
||||
view.timeSpan.timeInMillis = newRule.maximumTimeInMillis.toLong()
|
||||
view.affectsMultipleDays = affectedDays >= 2
|
||||
view.showWeeklyDailyOption = affectedDays >= 2 && newRule.maximumTimeInMillis > 0
|
||||
|
||||
view.applyToWholeDay = newRule.appliesToWholeDay
|
||||
view.startTime = MinuteOfDay.format(newRule.startMinuteOfDay)
|
||||
view.endTime = MinuteOfDay.format(newRule.endMinuteOfDay)
|
||||
|
||||
view.enableSessionDurationLimit = newRule.sessionDurationLimitEnabled
|
||||
view.sessionBreakText = TimeTextUtil.minutes(newRule.sessionPauseMilliseconds / (1000 * 60), context!!)
|
||||
view.sessionLengthText = TimeTextUtil.minutes(newRule.sessionDurationMilliseconds / (1000 * 60), context!!)
|
||||
view.sessionBreakText = TimeTextUtil.minutes(newRule.sessionPauseMilliseconds / (1000 * 60), requireContext())
|
||||
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? {
|
||||
val listener = targetFragment as EditTimeLimitRuleDialogFragmentListener
|
||||
val database = DefaultAppLogic.with(context!!).database
|
||||
val database = DefaultAppLogic.with(requireContext()).database
|
||||
|
||||
view = FragmentEditTimeLimitRuleDialogBinding.inflate(layoutInflater, container, false)
|
||||
|
||||
|
@ -162,7 +147,8 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
|||
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
||||
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
||||
sessionPauseMilliseconds = 0,
|
||||
sessionDurationMilliseconds = 0
|
||||
sessionDurationMilliseconds = 0,
|
||||
perDay = true
|
||||
)
|
||||
} else {
|
||||
view.isNewRule = false
|
||||
|
@ -278,7 +264,8 @@ class EditTimeLimitRuleDialogFragment : BottomSheetDialogFragment(), DurationPic
|
|||
start = newRule.startMinuteOfDay,
|
||||
end = newRule.endMinuteOfDay,
|
||||
sessionDurationMilliseconds = newRule.sessionDurationMilliseconds,
|
||||
sessionPauseMilliseconds = newRule.sessionPauseMilliseconds
|
||||
sessionPauseMilliseconds = newRule.sessionPauseMilliseconds,
|
||||
perDay = newRule.perDay
|
||||
)
|
||||
)) {
|
||||
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 {
|
||||
view.timeSpan.enablePickerMode(it)
|
||||
})
|
||||
|
|
|
@ -42,44 +42,38 @@ class DefaultCategories private constructor(private val context: Context) {
|
|||
val rules = mutableListOf<TimeLimitRule>()
|
||||
|
||||
// maximum time for each workday
|
||||
for (day in 0..4) {
|
||||
rules.add(
|
||||
TimeLimitRule(
|
||||
id = IdGenerator.generateId(),
|
||||
categoryId = categoryId,
|
||||
applyToExtraTimeUsage = false,
|
||||
dayMask = (1 shl day).toByte(),
|
||||
dayMask = (1 or 2 or 4 or 8 or 16).toByte(),
|
||||
maximumTimeInMillis = 1000 * 60 * 30, // 30 minutes
|
||||
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
||||
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
||||
sessionPauseMilliseconds = 0,
|
||||
sessionDurationMilliseconds = 0
|
||||
sessionDurationMilliseconds = 0,
|
||||
perDay = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// maximum time for each weekend day
|
||||
for (day in 5..6) {
|
||||
rules.add(
|
||||
TimeLimitRule(
|
||||
id = IdGenerator.generateId(),
|
||||
categoryId = categoryId,
|
||||
applyToExtraTimeUsage = false,
|
||||
dayMask = (1 shl day).toByte(),
|
||||
dayMask = (32 or 64).toByte(),
|
||||
maximumTimeInMillis = 1000 * 60 * 60 * 3, // 3 hours
|
||||
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
||||
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
||||
sessionPauseMilliseconds = 0,
|
||||
sessionDurationMilliseconds = 0
|
||||
sessionDurationMilliseconds = 0,
|
||||
perDay = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// maximum time per total week
|
||||
val dayMask = BitSet()
|
||||
|
||||
dayMask.set(0, 7)
|
||||
|
||||
rules.add(
|
||||
TimeLimitRule(
|
||||
id = IdGenerator.generateId(),
|
||||
|
@ -90,7 +84,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
|||
startMinuteOfDay = TimeLimitRule.MIN_START_MINUTE,
|
||||
endMinuteOfDay = TimeLimitRule.MAX_END_MINUTE,
|
||||
sessionPauseMilliseconds = 0,
|
||||
sessionDurationMilliseconds = 0
|
||||
sessionDurationMilliseconds = 0,
|
||||
perDay = false
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -105,7 +100,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
|||
startMinuteOfDay = 0,
|
||||
endMinuteOfDay = 6 * 60 - 1,
|
||||
sessionPauseMilliseconds = 0,
|
||||
sessionDurationMilliseconds = 0
|
||||
sessionDurationMilliseconds = 0,
|
||||
perDay = false
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -119,7 +115,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
|||
startMinuteOfDay = 18 * 60,
|
||||
endMinuteOfDay = MinuteOfDay.MAX,
|
||||
sessionPauseMilliseconds = 0,
|
||||
sessionDurationMilliseconds = 0
|
||||
sessionDurationMilliseconds = 0,
|
||||
perDay = false
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -134,7 +131,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
|||
startMinuteOfDay = 0,
|
||||
endMinuteOfDay = 9 * 60 - 1,
|
||||
sessionPauseMilliseconds = 0,
|
||||
sessionDurationMilliseconds = 0
|
||||
sessionDurationMilliseconds = 0,
|
||||
perDay = false
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -148,7 +146,8 @@ class DefaultCategories private constructor(private val context: Context) {
|
|||
startMinuteOfDay = 20 * 60,
|
||||
endMinuteOfDay = MinuteOfDay.MAX,
|
||||
sessionPauseMilliseconds = 0,
|
||||
sessionDurationMilliseconds = 0
|
||||
sessionDurationMilliseconds = 0,
|
||||
perDay = false
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
name="affectsMultipleDays"
|
||||
type="boolean" />
|
||||
|
||||
<variable
|
||||
name="showWeeklyDailyOption"
|
||||
type="boolean" />
|
||||
|
||||
<variable
|
||||
name="startTime"
|
||||
type="String" />
|
||||
|
@ -80,6 +84,27 @@
|
|||
|
||||
<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
|
||||
android:id="@+id/time_span"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -107,8 +132,9 @@
|
|||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:padding="8dp"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/category_time_limit_rules_apply_to_part_day_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
@ -122,7 +148,7 @@
|
|||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:padding="8dp"
|
||||
android:text="@string/category_time_limit_rules_apply_to_part_day_2"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -161,10 +187,10 @@
|
|||
app:layout_gravity="fill_vertical|fill_horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingStart="0dp"
|
||||
app:layout_row="1"
|
||||
app:layout_column="1"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:text="@string/category_time_limit_rules_session_limit_duration"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
@ -187,8 +213,8 @@
|
|||
app:layout_gravity="fill_vertical|fill_horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:paddingStart="0dp"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
app:layout_row="2"
|
||||
app:layout_column="1"
|
||||
android:text="@string/category_time_limit_rules_session_limit_pause"
|
||||
|
@ -217,13 +243,6 @@
|
|||
android:layout_width="match_parent"
|
||||
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
|
||||
android:orientation="horizontal"
|
||||
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_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_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
|
||||
die Nutzung im angegebenen Intervall. Ohne Regeln für andere Intervalle
|
||||
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.
|
||||
Diese sind oft automatisch aktiviert, ohne das es Ihnen als Nutzer bewusst ist.
|
||||
</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">
|
||||
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>
|
||||
|
|
|
@ -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.
|
||||
Estos recursos são freqüentemente ativadas mesmo que você não o tenha feito.
|
||||
</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">
|
||||
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
|
||||
|
|
|
@ -367,15 +367,13 @@
|
|||
<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_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_updated">Rule was modified</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
|
||||
will limit the usage duration only in the specified interval.
|
||||
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.
|
||||
This features are often enabled even if you did not enable it actively.
|
||||
</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">
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue