mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 17:59:51 +02:00
Remove parent blocked time areas feature
This was replaced by the limit login category
This commit is contained in:
parent
206260d068
commit
e672dd9eb1
20 changed files with 52 additions and 575 deletions
|
@ -81,7 +81,7 @@
|
|||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "blockedTimes",
|
||||
"fieldPath": "obsoleteBlockedTimes",
|
||||
"columnName": "blocked_times",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
|
|
|
@ -20,11 +20,8 @@ import android.util.JsonWriter
|
|||
import androidx.room.*
|
||||
import io.timelimit.android.data.IdGenerator
|
||||
import io.timelimit.android.data.JsonSerializable
|
||||
import io.timelimit.android.data.customtypes.ImmutableBitmask
|
||||
import io.timelimit.android.data.customtypes.ImmutableBitmaskAdapter
|
||||
import io.timelimit.android.data.customtypes.ImmutableBitmaskJson
|
||||
import io.timelimit.android.util.parseJsonArray
|
||||
import java.util.*
|
||||
|
||||
@Entity(tableName = "user")
|
||||
@TypeConverters(
|
||||
|
@ -62,7 +59,8 @@ data class User(
|
|||
@ColumnInfo(name = "mail_notification_flags")
|
||||
val mailNotificationFlags: Int,
|
||||
@ColumnInfo(name = "blocked_times")
|
||||
val blockedTimes: ImmutableBitmask,
|
||||
@Deprecated(message = "this feature was removed; the limit login category is a replacement")
|
||||
val obsoleteBlockedTimes: String = "",
|
||||
@ColumnInfo(name = "flags")
|
||||
val flags: Long
|
||||
): JsonSerializable {
|
||||
|
@ -79,7 +77,7 @@ data class User(
|
|||
private const val CATEGORY_FOR_NOT_ASSIGNED_APPS = "categoryForNotAssignedApps"
|
||||
private const val RELAX_PRIMARY_DEVICE = "relaxPrimaryDevice"
|
||||
private const val MAIL_NOTIFICATION_FLAGS = "mailNotificationFlags"
|
||||
private const val BLOCKED_TIMES = "blockedTimes"
|
||||
private const val OBSOLETE_BLOCKED_TIMES = "blockedTimes"
|
||||
private const val FLAGS = "flags"
|
||||
|
||||
fun parse(reader: JsonReader): User {
|
||||
|
@ -95,7 +93,6 @@ data class User(
|
|||
var categoryForNotAssignedApps = ""
|
||||
var relaxPrimaryDevice = false
|
||||
var mailNotificationFlags = 0
|
||||
var blockedTimes = ImmutableBitmask(BitSet())
|
||||
var flags = 0L
|
||||
|
||||
reader.beginObject()
|
||||
|
@ -113,7 +110,6 @@ data class User(
|
|||
CATEGORY_FOR_NOT_ASSIGNED_APPS -> categoryForNotAssignedApps = reader.nextString()
|
||||
RELAX_PRIMARY_DEVICE -> relaxPrimaryDevice = reader.nextBoolean()
|
||||
MAIL_NOTIFICATION_FLAGS -> mailNotificationFlags = reader.nextInt()
|
||||
BLOCKED_TIMES -> blockedTimes = ImmutableBitmaskJson.parse(reader.nextString(), Category.BLOCKED_MINUTES_IN_WEEK_LENGTH)
|
||||
FLAGS -> flags = reader.nextLong()
|
||||
else -> reader.skipValue()
|
||||
}
|
||||
|
@ -133,7 +129,6 @@ data class User(
|
|||
categoryForNotAssignedApps = categoryForNotAssignedApps,
|
||||
relaxPrimaryDevice = relaxPrimaryDevice,
|
||||
mailNotificationFlags = mailNotificationFlags,
|
||||
blockedTimes = blockedTimes,
|
||||
flags = flags
|
||||
)
|
||||
}
|
||||
|
@ -186,7 +181,7 @@ data class User(
|
|||
writer.name(CATEGORY_FOR_NOT_ASSIGNED_APPS).value(categoryForNotAssignedApps)
|
||||
writer.name(RELAX_PRIMARY_DEVICE).value(relaxPrimaryDevice)
|
||||
writer.name(MAIL_NOTIFICATION_FLAGS).value(mailNotificationFlags)
|
||||
writer.name(BLOCKED_TIMES).value(ImmutableBitmaskJson.serialize(blockedTimes))
|
||||
writer.name(OBSOLETE_BLOCKED_TIMES).value("")
|
||||
writer.name(FLAGS).value(flags)
|
||||
|
||||
writer.endObject()
|
||||
|
|
|
@ -128,7 +128,6 @@ class AppSetupLogic(private val appLogic: AppLogic) {
|
|||
categoryForNotAssignedApps = "",
|
||||
relaxPrimaryDevice = false,
|
||||
mailNotificationFlags = 0,
|
||||
blockedTimes = ImmutableBitmask(BitSet()),
|
||||
flags = 0
|
||||
)
|
||||
|
||||
|
@ -151,7 +150,6 @@ class AppSetupLogic(private val appLogic: AppLogic) {
|
|||
categoryForNotAssignedApps = "",
|
||||
relaxPrimaryDevice = false,
|
||||
mailNotificationFlags = 0,
|
||||
blockedTimes = ImmutableBitmask(BitSet()),
|
||||
flags = 0
|
||||
)
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@ object ApplyServerDataStatus {
|
|||
categoryForNotAssignedApps = newEntry.categoryForNotAssignedApps,
|
||||
relaxPrimaryDevice = newEntry.relaxPrimaryDevice,
|
||||
mailNotificationFlags = newEntry.mailNotificationFlags,
|
||||
blockedTimes = newEntry.blockedTimes,
|
||||
flags = newEntry.flags
|
||||
)
|
||||
|
||||
|
|
|
@ -1832,48 +1832,6 @@ data class RenameChildAction(val childId: String, val newName: String): ParentAc
|
|||
}
|
||||
}
|
||||
|
||||
data class UpdateParentBlockedTimesAction(val parentId: String, val blockedTimes: ImmutableBitmask): ParentAction() {
|
||||
companion object {
|
||||
const val TYPE_VALUE = "UPDATE_PARENT_BLOCKED_TIMES"
|
||||
private const val PARENT_ID = "parentId"
|
||||
private const val BLOCKED_TIMES = "times"
|
||||
}
|
||||
|
||||
init {
|
||||
IdGenerator.assertIdValid(parentId)
|
||||
}
|
||||
|
||||
override fun serialize(writer: JsonWriter) {
|
||||
writer.beginObject()
|
||||
|
||||
writer.name(TYPE).value(TYPE_VALUE)
|
||||
writer.name(PARENT_ID).value(parentId)
|
||||
writer.name(BLOCKED_TIMES).value(ImmutableBitmaskJson.serialize(blockedTimes))
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
}
|
||||
|
||||
data class ResetParentBlockedTimesAction(val parentId: String): ParentAction() {
|
||||
companion object {
|
||||
const val TYPE_VALUE = "RESET_PARENT_BLOCKED_TIMES"
|
||||
private const val PARENT_ID = "parentId"
|
||||
}
|
||||
|
||||
init {
|
||||
IdGenerator.assertIdValid(parentId)
|
||||
}
|
||||
|
||||
override fun serialize(writer: JsonWriter) {
|
||||
writer.beginObject()
|
||||
|
||||
writer.name(TYPE).value(TYPE_VALUE)
|
||||
writer.name(PARENT_ID).value(parentId)
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
}
|
||||
|
||||
data class UpdateUserFlagsAction(val userId: String, val modifiedBits: Long, val newValues: Long): ParentAction() {
|
||||
companion object {
|
||||
private const val TYPE_VALUE = "UPDATE_USER_FLAGS"
|
||||
|
|
|
@ -252,7 +252,6 @@ object LocalDatabaseParentActionDispatcher {
|
|||
categoryForNotAssignedApps = "",
|
||||
relaxPrimaryDevice = false,
|
||||
mailNotificationFlags = 0,
|
||||
blockedTimes = ImmutableBitmask(BitSet()),
|
||||
flags = 0
|
||||
))
|
||||
}
|
||||
|
@ -674,32 +673,6 @@ object LocalDatabaseParentActionDispatcher {
|
|||
|
||||
null
|
||||
}
|
||||
is UpdateParentBlockedTimesAction -> {
|
||||
val userEntry = database.user().getUserByIdSync(action.parentId)
|
||||
|
||||
if (userEntry?.type != UserType.Parent) {
|
||||
throw IllegalArgumentException("no valid parent id")
|
||||
}
|
||||
|
||||
database.user().updateUserSync(
|
||||
userEntry.copy(
|
||||
blockedTimes = action.blockedTimes
|
||||
)
|
||||
)
|
||||
}
|
||||
is ResetParentBlockedTimesAction -> {
|
||||
val userEntry = database.user().getUserByIdSync(action.parentId)
|
||||
|
||||
if (userEntry?.type != UserType.Parent) {
|
||||
throw IllegalArgumentException("no valid parent id")
|
||||
}
|
||||
|
||||
database.user().updateUserSync(
|
||||
userEntry.copy(
|
||||
blockedTimes = ImmutableBitmask(BitSet())
|
||||
)
|
||||
)
|
||||
}
|
||||
is UpdateCategoryBatteryLimit -> {
|
||||
val categoryEntry = database.category().getCategoryByIdSync(action.categoryId)
|
||||
?: throw IllegalArgumentException("can not update battery limit for a category which does not exist")
|
||||
|
|
|
@ -174,7 +174,6 @@ data class ServerUserData(
|
|||
val categoryForNotAssignedApps: String,
|
||||
val relaxPrimaryDevice: Boolean,
|
||||
val mailNotificationFlags: Int,
|
||||
val blockedTimes: ImmutableBitmask,
|
||||
val flags: Long,
|
||||
val limitLoginCategory: String?
|
||||
) {
|
||||
|
@ -191,7 +190,6 @@ data class ServerUserData(
|
|||
private const val CATEGORY_FOR_NOT_ASSIGNED_APPS = "categoryForNotAssignedApps"
|
||||
private const val RELAX_PRIMARY_DEVICE = "relaxPrimaryDevice"
|
||||
private const val MAIL_NOTIFICATION_FLAGS = "mailNotificationFlags"
|
||||
private const val BLOCKED_TIMES = "blockedTimes"
|
||||
private const val FLAGS = "flags"
|
||||
private const val USER_LIMIT_LOGIN_CATEGORY = "llc"
|
||||
|
||||
|
@ -227,7 +225,6 @@ data class ServerUserData(
|
|||
CATEGORY_FOR_NOT_ASSIGNED_APPS -> categoryForNotAssignedApps = reader.nextString()
|
||||
RELAX_PRIMARY_DEVICE -> relaxPrimaryDevice = reader.nextBoolean()
|
||||
MAIL_NOTIFICATION_FLAGS -> mailNotificationFlags = reader.nextInt()
|
||||
BLOCKED_TIMES -> blockedTimes = ImmutableBitmaskJson.parse(reader.nextString(), Category.BLOCKED_MINUTES_IN_WEEK_LENGTH)
|
||||
FLAGS -> flags = reader.nextLong()
|
||||
USER_LIMIT_LOGIN_CATEGORY -> if (reader.peek() == JsonToken.NULL) reader.nextNull() else limitLoginCategory = reader.nextString()
|
||||
else -> reader.skipValue()
|
||||
|
@ -248,7 +245,6 @@ data class ServerUserData(
|
|||
categoryForNotAssignedApps = categoryForNotAssignedApps,
|
||||
relaxPrimaryDevice = relaxPrimaryDevice,
|
||||
mailNotificationFlags = mailNotificationFlags,
|
||||
blockedTimes = blockedTimes,
|
||||
flags = flags,
|
||||
limitLoginCategory = limitLoginCategory
|
||||
)
|
||||
|
|
|
@ -33,7 +33,6 @@ import java.util.concurrent.CountDownLatch
|
|||
|
||||
sealed class AllowUserLoginStatus {
|
||||
data class Allow(val maxTime: Long): AllowUserLoginStatus()
|
||||
data class ForbidByCurrentTime(val missingNetworkTime: Boolean, val maxTime: Long): AllowUserLoginStatus()
|
||||
data class ForbidByCategory(val categoryTitle: String, val blockingReason: BlockingReason, val maxTime: Long): AllowUserLoginStatus()
|
||||
object ForbidByMissingSync: AllowUserLoginStatus()
|
||||
object ForbidUserNotFound: AllowUserLoginStatus()
|
||||
|
@ -47,23 +46,6 @@ object AllowUserLoginStatusUtil {
|
|||
return AllowUserLoginStatus.Allow(maxTime = Long.MAX_VALUE)
|
||||
}
|
||||
|
||||
if (!data.loginRelatedData.user.blockedTimes.dataNotToModify.isEmpty) {
|
||||
if (!time.shouldTrustTimePermanently) {
|
||||
return AllowUserLoginStatus.ForbidByCurrentTime(missingNetworkTime = true, maxTime = Long.MAX_VALUE)
|
||||
} else {
|
||||
val minuteOfWeek = getMinuteOfWeek(time.timeInMillis, TimeZone.getTimeZone(data.loginRelatedData.user.timeZone))
|
||||
|
||||
if (data.loginRelatedData.user.blockedTimes.dataNotToModify[minuteOfWeek]) {
|
||||
val nextAllowedSlot = data.loginRelatedData.user.blockedTimes.dataNotToModify.nextClearBit(minuteOfWeek)
|
||||
val minutesToWait: Long = (nextAllowedSlot - minuteOfWeek).toLong()
|
||||
// not very nice but it works
|
||||
val msToWait = if (minutesToWait <= 1) 5000 else (minutesToWait - 1) * 1000 * 60
|
||||
|
||||
return AllowUserLoginStatus.ForbidByCurrentTime(missingNetworkTime = false, maxTime = time.timeInMillis + msToWait)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (data.limitLoginCategoryUserRelatedData != null && data.loginRelatedData.limitLoginCategory != null) {
|
||||
if (missingSyncForLimitLoginUser) {
|
||||
AllowUserLoginStatus.ForbidByMissingSync
|
||||
|
@ -87,33 +69,20 @@ object AllowUserLoginStatusUtil {
|
|||
AllowUserLoginStatus.ForbidByCategory(
|
||||
categoryTitle = blockingHandling.createdWithCategoryRelatedData.category.title,
|
||||
blockingReason = blockingHandling.systemLevelBlockingReason,
|
||||
maxTime = blockingHandling.dependsOnMaxTime.coerceAtMost(
|
||||
if (data.loginRelatedData.user.blockedTimes.dataNotToModify.isEmpty)
|
||||
Long.MAX_VALUE
|
||||
else
|
||||
time.timeInMillis + 1000 * 5
|
||||
)
|
||||
maxTime = blockingHandling.dependsOnMaxTime
|
||||
)
|
||||
} else {
|
||||
val maxTimeByCategories = handlings.minBy { it.dependsOnMaxTime }?.dependsOnMaxTime
|
||||
?: Long.MAX_VALUE
|
||||
|
||||
AllowUserLoginStatus.Allow(
|
||||
maxTime = maxTimeByCategories.coerceAtMost(
|
||||
if (data.loginRelatedData.user.blockedTimes.dataNotToModify.isEmpty)
|
||||
Long.MAX_VALUE
|
||||
else
|
||||
time.timeInMillis + 1000 * 5
|
||||
)
|
||||
maxTime = maxTimeByCategories
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AllowUserLoginStatus.Allow(
|
||||
maxTime = if (data.loginRelatedData.user.blockedTimes.dataNotToModify.isEmpty)
|
||||
Long.MAX_VALUE
|
||||
else
|
||||
time.timeInMillis + 1000 * 5
|
||||
maxTime = Long.MAX_VALUE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +180,6 @@ object AllowUserLoginStatusUtil {
|
|||
val scheduledTime: Long = when (result) {
|
||||
AllowUserLoginStatus.ForbidUserNotFound -> Long.MAX_VALUE
|
||||
AllowUserLoginStatus.ForbidByMissingSync -> Long.MAX_VALUE
|
||||
is AllowUserLoginStatus.ForbidByCurrentTime -> result.maxTime
|
||||
is AllowUserLoginStatus.Allow -> result.maxTime
|
||||
is AllowUserLoginStatus.ForbidByCategory -> result.maxTime
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package io.timelimit.android.ui.login
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
|
@ -43,6 +44,34 @@ import kotlinx.coroutines.sync.Mutex
|
|||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
class LoginDialogFragmentModel(application: Application): AndroidViewModel(application) {
|
||||
companion object {
|
||||
private fun formatAllowLoginStatusError(status: AllowUserLoginStatus, context: Context): String = when (status) {
|
||||
is AllowUserLoginStatus.Allow -> context.getString(R.string.error_general)
|
||||
is AllowUserLoginStatus.ForbidUserNotFound -> context.getString(R.string.error_general)
|
||||
is AllowUserLoginStatus.ForbidByCategory -> context.getString(
|
||||
R.string.login_category_blocked,
|
||||
status.categoryTitle,
|
||||
formatBlockingReasonForLimitLoginCategory(status.blockingReason, context)
|
||||
)
|
||||
is AllowUserLoginStatus.ForbidByMissingSync -> context.getString(R.string.login_missing_sync)
|
||||
}
|
||||
|
||||
fun formatBlockingReasonForLimitLoginCategory(reason: BlockingReason, context: Context) = when (reason) {
|
||||
BlockingReason.TemporarilyBlocked -> context.getString(R.string.lock_reason_short_temporarily_blocked)
|
||||
BlockingReason.TimeOver -> context.getString(R.string.lock_reason_short_time_over)
|
||||
BlockingReason.TimeOverExtraTimeCanBeUsedLater -> context.getString(R.string.lock_reason_short_time_over)
|
||||
BlockingReason.BlockedAtThisTime -> context.getString(R.string.lock_reason_short_blocked_time_area)
|
||||
BlockingReason.MissingNetworkTime -> context.getString(R.string.lock_reason_short_missing_network_time)
|
||||
BlockingReason.RequiresCurrentDevice -> context.getString(R.string.lock_reason_short_requires_current_device)
|
||||
BlockingReason.NotificationsAreBlocked -> context.getString(R.string.lock_reason_short_notification_blocking)
|
||||
BlockingReason.BatteryLimit -> context.getString(R.string.lock_reason_short_battery_limit)
|
||||
BlockingReason.SessionDurationLimit -> context.getString(R.string.lock_reason_short_session_duration)
|
||||
BlockingReason.MissingRequiredNetwork -> context.getString(R.string.lock_reason_short_missing_required_network)
|
||||
BlockingReason.NotPartOfAnCategory -> "???"
|
||||
BlockingReason.None -> "???"
|
||||
}
|
||||
}
|
||||
|
||||
val selectedUserId = MutableLiveData<String?>().apply { value = null }
|
||||
private val logic = DefaultAppLogic.with(application)
|
||||
private val users = logic.database.user().getAllUsersLive()
|
||||
|
@ -85,12 +114,9 @@ class LoginDialogFragmentModel(application: Application): AndroidViewModel(appli
|
|||
if (status is AllowUserLoginStatus.Allow) {
|
||||
loginScreen
|
||||
} else if (
|
||||
(status is AllowUserLoginStatus.ForbidByCurrentTime && status.missingNetworkTime) ||
|
||||
(status is AllowUserLoginStatus.ForbidByCategory && status.blockingReason == BlockingReason.MissingNetworkTime)
|
||||
) {
|
||||
liveDataFromValue(ParentUserLoginMissingTrustedTime as LoginDialogStatus)
|
||||
} else if (status is AllowUserLoginStatus.ForbidByCurrentTime) {
|
||||
liveDataFromValue(ParentUserLoginBlockedTime as LoginDialogStatus)
|
||||
} else if (status is AllowUserLoginStatus.ForbidByCategory) {
|
||||
liveDataFromValue(
|
||||
ParentUserLoginBlockedByCategory(
|
||||
|
@ -167,7 +193,6 @@ class LoginDialogFragmentModel(application: Application): AndroidViewModel(appli
|
|||
|
||||
allUsers.singleOrNull { it.type == UserType.Parent }?.let { user ->
|
||||
val emptyPasswordValid = Threads.crypto.executeAndWait { PasswordHashing.validateSync("", user.password) }
|
||||
val hasBlockedTimes = !user.blockedTimes.dataNotToModify.isEmpty
|
||||
|
||||
val shouldSignIn = if (emptyPasswordValid) {
|
||||
Threads.database.executeAndWait {
|
||||
|
@ -188,10 +213,6 @@ class LoginDialogFragmentModel(application: Application): AndroidViewModel(appli
|
|||
secondPasswordHash = Threads.crypto.executeAndWait { PasswordHashing.hashSyncWithSalt("", user.secondPasswordSalt) }
|
||||
))
|
||||
|
||||
if (hasBlockedTimes) {
|
||||
Toast.makeText(getApplication(), R.string.manage_parent_blocked_times_toast, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
isLoginDone.value = true
|
||||
}
|
||||
}
|
||||
|
@ -241,16 +262,16 @@ class LoginDialogFragmentModel(application: Application): AndroidViewModel(appli
|
|||
}
|
||||
|
||||
if (user != null && user.type == UserType.Parent) {
|
||||
val hasBlockedTimes = !user.blockedTimes.dataNotToModify.isEmpty
|
||||
|
||||
val shouldSignIn = Threads.database.executeAndWait {
|
||||
val allowLoginStatus = Threads.database.executeAndWait {
|
||||
AllowUserLoginStatusUtil.calculateSync(
|
||||
logic = logic,
|
||||
userId = user.id,
|
||||
didSync = didSync.value ?: false
|
||||
) is AllowUserLoginStatus.Allow
|
||||
)
|
||||
}
|
||||
|
||||
val shouldSignIn = allowLoginStatus is AllowUserLoginStatus.Allow
|
||||
|
||||
if (shouldSignIn) {
|
||||
// this feature is limited to the local mode
|
||||
model.setAuthenticatedUser(AuthenticatedUser(
|
||||
|
@ -259,13 +280,9 @@ class LoginDialogFragmentModel(application: Application): AndroidViewModel(appli
|
|||
secondPasswordHash = "device"
|
||||
))
|
||||
|
||||
if (hasBlockedTimes) {
|
||||
Toast.makeText(getApplication(), R.string.manage_parent_blocked_times_toast, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
isLoginDone.value = true
|
||||
} else {
|
||||
Toast.makeText(getApplication(), R.string.login_blocked_time, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(getApplication(), formatAllowLoginStatusError(allowLoginStatus, getApplication()), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,27 +327,24 @@ class LoginDialogFragmentModel(application: Application): AndroidViewModel(appli
|
|||
secondPasswordHash = secondPasswordHash
|
||||
)
|
||||
|
||||
val hasBlockedTimes = !userEntry.blockedTimes.dataNotToModify.isEmpty
|
||||
val shouldSignIn = Threads.database.executeAndWait {
|
||||
val allowLoginStatus = Threads.database.executeAndWait {
|
||||
AllowUserLoginStatusUtil.calculateSync(
|
||||
logic = logic,
|
||||
userId = userEntry.id,
|
||||
didSync = didSync.value ?: false
|
||||
) is AllowUserLoginStatus.Allow
|
||||
)
|
||||
}
|
||||
|
||||
val shouldSignIn = allowLoginStatus is AllowUserLoginStatus.Allow
|
||||
|
||||
if (!shouldSignIn) {
|
||||
Toast.makeText(getApplication(), R.string.login_blocked_time, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(getApplication(), formatAllowLoginStatusError(allowLoginStatus, getApplication()), Toast.LENGTH_SHORT).show()
|
||||
|
||||
return@runAsync
|
||||
}
|
||||
|
||||
model.setAuthenticatedUser(authenticatedUser)
|
||||
|
||||
if (hasBlockedTimes) {
|
||||
Toast.makeText(getApplication(), R.string.manage_parent_blocked_times_toast, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
if (setAsDeviceUser) {
|
||||
if (userEntryInfo.deviceRelatedData.deviceEntry.currentUserId != userEntry.id) {
|
||||
ActivityViewModel.dispatchWithoutCheckOrCatching(
|
||||
|
@ -435,7 +449,6 @@ class LoginDialogFragmentModel(application: Application): AndroidViewModel(appli
|
|||
sealed class LoginDialogStatus
|
||||
data class UserListLoginDialogStatus(val usersToShow: List<User>, val isLocalMode: Boolean): LoginDialogStatus()
|
||||
object ParentUserLoginMissingTrustedTime: LoginDialogStatus()
|
||||
object ParentUserLoginBlockedTime: LoginDialogStatus()
|
||||
object ParentUserLoginWaitingForSync: LoginDialogStatus()
|
||||
data class ParentUserLoginBlockedByCategory(val categoryTitle: String, val reason: BlockingReason): LoginDialogStatus()
|
||||
data class ParentUserLogin(
|
||||
|
|
|
@ -34,7 +34,6 @@ import io.timelimit.android.async.Threads
|
|||
import io.timelimit.android.data.model.User
|
||||
import io.timelimit.android.databinding.NewLoginFragmentBinding
|
||||
import io.timelimit.android.extensions.setOnEnterListenr
|
||||
import io.timelimit.android.logic.BlockingReason
|
||||
import io.timelimit.android.ui.main.getActivityViewModel
|
||||
import io.timelimit.android.ui.manage.parent.key.ScannedKey
|
||||
import io.timelimit.android.ui.view.KeyboardViewListener
|
||||
|
@ -50,10 +49,9 @@ class NewLoginFragment: DialogFragment() {
|
|||
private const val CHILD_ALREADY_CURRENT_USER = 3
|
||||
private const val CHILD_AUTH = 4
|
||||
private const val CHILD_LOGIN_REQUIRES_PREMIUM = 5
|
||||
private const val BLOCKED_LOGIN_TIME = 6
|
||||
private const val UNVERIFIED_TIME = 7
|
||||
private const val PARENT_LOGIN_BLOCKED = 8
|
||||
private const val WAITING_FOR_SYNC = 9
|
||||
private const val UNVERIFIED_TIME = 6
|
||||
private const val PARENT_LOGIN_BLOCKED = 7
|
||||
private const val WAITING_FOR_SYNC = 8
|
||||
}
|
||||
|
||||
private val model: LoginDialogFragmentModel by lazy {
|
||||
|
@ -252,15 +250,6 @@ class NewLoginFragment: DialogFragment() {
|
|||
|
||||
null
|
||||
}
|
||||
ParentUserLoginBlockedTime -> {
|
||||
if (binding.switcher.displayedChild != BLOCKED_LOGIN_TIME) {
|
||||
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
|
||||
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
|
||||
binding.switcher.displayedChild = BLOCKED_LOGIN_TIME
|
||||
}
|
||||
|
||||
null
|
||||
}
|
||||
is CanNotSignInChildHasNoPassword -> {
|
||||
if (binding.switcher.displayedChild != CHILD_MISSING_PASSWORD) {
|
||||
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
|
||||
|
@ -319,20 +308,7 @@ class NewLoginFragment: DialogFragment() {
|
|||
}
|
||||
|
||||
binding.parentLoginBlocked.categoryTitle = status.categoryTitle
|
||||
binding.parentLoginBlocked.reason = when (status.reason) {
|
||||
BlockingReason.TemporarilyBlocked -> getString(R.string.lock_reason_short_temporarily_blocked)
|
||||
BlockingReason.TimeOver -> getString(R.string.lock_reason_short_time_over)
|
||||
BlockingReason.TimeOverExtraTimeCanBeUsedLater -> getString(R.string.lock_reason_short_time_over)
|
||||
BlockingReason.BlockedAtThisTime -> getString(R.string.lock_reason_short_blocked_time_area)
|
||||
BlockingReason.MissingNetworkTime -> getString(R.string.lock_reason_short_missing_network_time)
|
||||
BlockingReason.RequiresCurrentDevice -> getString(R.string.lock_reason_short_requires_current_device)
|
||||
BlockingReason.NotificationsAreBlocked -> getString(R.string.lock_reason_short_notification_blocking)
|
||||
BlockingReason.BatteryLimit -> getString(R.string.lock_reason_short_battery_limit)
|
||||
BlockingReason.SessionDurationLimit -> getString(R.string.lock_reason_short_session_duration)
|
||||
BlockingReason.MissingRequiredNetwork -> getString(R.string.lock_reason_short_missing_required_network)
|
||||
BlockingReason.NotPartOfAnCategory -> "???"
|
||||
BlockingReason.None -> "???"
|
||||
}
|
||||
binding.parentLoginBlocked.reason = LoginDialogFragmentModel.formatBlockingReasonForLimitLoginCategory(status.reason, context!!)
|
||||
|
||||
null
|
||||
}
|
||||
|
|
|
@ -24,34 +24,18 @@ import androidx.fragment.app.FragmentManager
|
|||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.extensions.showSafe
|
||||
import kotlinx.android.synthetic.main.fragment_blocked_time_areas_help_dialog.*
|
||||
|
||||
class BlockedTimeAreasHelpDialog : BottomSheetDialogFragment() {
|
||||
companion object {
|
||||
private const val DIALOG_TAG = "r"
|
||||
private const val FOR_USER = "forUser"
|
||||
|
||||
fun newInstance(forUser: Boolean) = BlockedTimeAreasHelpDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putBoolean(FOR_USER, forUser)
|
||||
}
|
||||
}
|
||||
fun newInstance() = BlockedTimeAreasHelpDialog()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_blocked_time_areas_help_dialog, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val forUser = arguments?.getBoolean(FOR_USER, false)
|
||||
|
||||
if (forUser == true) {
|
||||
text1.setText(R.string.manage_parent_blocked_times_description)
|
||||
}
|
||||
}
|
||||
|
||||
fun show(manager: FragmentManager) {
|
||||
showSafe(manager, DIALOG_TAG)
|
||||
}
|
||||
|
|
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
* 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.manage.parent.blockedtimes
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.data.customtypes.ImmutableBitmask
|
||||
import io.timelimit.android.data.model.User
|
||||
import io.timelimit.android.data.model.withConfigCopiedToOtherDates
|
||||
import io.timelimit.android.databinding.ManageParentBlockedTimesFragmentBinding
|
||||
import io.timelimit.android.livedata.liveDataFromValue
|
||||
import io.timelimit.android.livedata.map
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.sync.actions.UpdateParentBlockedTimesAction
|
||||
import io.timelimit.android.ui.main.ActivityViewModel
|
||||
import io.timelimit.android.ui.main.ActivityViewModelHolder
|
||||
import io.timelimit.android.ui.main.AuthenticationFab
|
||||
import io.timelimit.android.ui.main.FragmentWithCustomTitle
|
||||
import io.timelimit.android.ui.manage.category.blocked_times.*
|
||||
import kotlinx.android.synthetic.main.fragment_blocked_time_areas.*
|
||||
import java.util.*
|
||||
|
||||
class ManageParentBlockedTimesFragment : Fragment(), FragmentWithCustomTitle, CopyBlockedTimeAreasDialogFragmentListener {
|
||||
companion object {
|
||||
private const val MINUTES_PER_DAY = 60 * 24
|
||||
private const val MAX_BLOCKED_MINUTES_PER_DAY = 60 * 18 + 1
|
||||
}
|
||||
|
||||
private val params: ManageParentBlockedTimesFragmentArgs by lazy {
|
||||
ManageParentBlockedTimesFragmentArgs.fromBundle(arguments!!)
|
||||
}
|
||||
|
||||
private val authActivity: ActivityViewModelHolder by lazy {
|
||||
activity!! as ActivityViewModelHolder
|
||||
}
|
||||
|
||||
private val auth: ActivityViewModel by lazy {
|
||||
authActivity.getActivityViewModel()
|
||||
}
|
||||
|
||||
private val parent: LiveData<User?> by lazy {
|
||||
DefaultAppLogic.with(context!!).database.user().getParentUserByIdLive(params.parentUserId)
|
||||
}
|
||||
|
||||
override fun getCustomTitle() = parent.map { "${getString(R.string.manage_parent_blocked_times_title)} < ${it?.name} < ${getString(R.string.main_tab_overview)}" as String? }
|
||||
|
||||
override fun onCopyBlockedTimeAreasConfirmed(sourceDay: Int, targetDays: Set<Int>) {
|
||||
parent.value?.blockedTimes?.let { current ->
|
||||
updateBlockedTimes(current, current.withConfigCopiedToOtherDates(sourceDay, targetDays))
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateBlockedTimeAreas(newMask: BitSet): Boolean {
|
||||
for (day in 0 until 7) {
|
||||
var blocked = 0
|
||||
|
||||
for (minute in 0 until MINUTES_PER_DAY) {
|
||||
if (newMask[day * MINUTES_PER_DAY + minute]) {
|
||||
blocked++
|
||||
}
|
||||
}
|
||||
|
||||
if (blocked >= MAX_BLOCKED_MINUTES_PER_DAY) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun updateBlockedTimes(oldMask: ImmutableBitmask, newMask: ImmutableBitmask) {
|
||||
if (!validateBlockedTimeAreas(newMask.dataNotToModify)) {
|
||||
Snackbar.make(coordinator, R.string.manage_parent_lockout_hour_rule, Snackbar.LENGTH_LONG).show()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
auth.tryDispatchParentAction(
|
||||
UpdateParentBlockedTimesAction(
|
||||
parentId = params.parentUserId,
|
||||
blockedTimes = newMask
|
||||
)
|
||||
)
|
||||
) {
|
||||
Snackbar.make(coordinator, R.string.blocked_time_areas_snackbar_modified, Snackbar.LENGTH_SHORT)
|
||||
.setAction(R.string.generic_undo) {
|
||||
auth.tryDispatchParentAction(
|
||||
UpdateParentBlockedTimesAction(
|
||||
parentId = params.parentUserId,
|
||||
blockedTimes = oldMask
|
||||
)
|
||||
)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val binding = ManageParentBlockedTimesFragmentBinding.inflate(inflater, container, false)
|
||||
|
||||
// auth button
|
||||
AuthenticationFab.manageAuthenticationFab(
|
||||
fab = binding.fab,
|
||||
fragment = this,
|
||||
shouldHighlight = auth.shouldHighlightAuthenticationButton,
|
||||
authenticatedUser = auth.authenticatedUser,
|
||||
doesSupportAuth = liveDataFromValue(true)
|
||||
)
|
||||
|
||||
binding.fab.setOnClickListener { authActivity.showAuthenticationScreen() }
|
||||
|
||||
// dispatching
|
||||
fun requestAuthenticationOrReturnTrue(): Boolean {
|
||||
if (!auth.requestAuthenticationOrReturnTrue()) {
|
||||
return false
|
||||
}
|
||||
|
||||
val authenticatedUser = auth.authenticatedUser.value?.second?.id ?: return false
|
||||
val targetUser = params.parentUserId
|
||||
|
||||
if (authenticatedUser == targetUser) {
|
||||
return true
|
||||
} else {
|
||||
TryResetParentBlockedTimesDialogFragment.newInstance(parentUserId = params.parentUserId).show(fragmentManager!!)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// UI
|
||||
binding.btnHelp.setOnClickListener {
|
||||
BlockedTimeAreasHelpDialog.newInstance(forUser = true).show(fragmentManager!!)
|
||||
}
|
||||
|
||||
binding.btnCopyToOtherDays.setOnClickListener {
|
||||
if (requestAuthenticationOrReturnTrue()) {
|
||||
CopyBlockedTimeAreasDialogFragment.newInstance(this@ManageParentBlockedTimesFragment).show(fragmentManager!!)
|
||||
}
|
||||
}
|
||||
|
||||
BlockedTimeAreasLogic.init(
|
||||
recycler = binding.recycler,
|
||||
daySpinner = binding.spinnerDay,
|
||||
detailedModeCheckbox = binding.detailedMode,
|
||||
checkAuthentication = {
|
||||
if (requestAuthenticationOrReturnTrue()) {
|
||||
BlockedTimeAreasLogic.Authentication.FullyAvailable
|
||||
} else {
|
||||
// empty hook ... not clean, but it works good enough (and this component is old)
|
||||
BlockedTimeAreasLogic.Authentication.Missing({})
|
||||
}
|
||||
},
|
||||
updateBlockedTimes = { a, b -> updateBlockedTimes(a, b) },
|
||||
currentData = parent.map { it?.blockedTimes },
|
||||
lifecycleOwner = this
|
||||
)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 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.manage.parent.blockedtimes
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Observer
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.data.model.UserType
|
||||
import io.timelimit.android.extensions.showSafe
|
||||
import io.timelimit.android.sync.actions.ResetParentBlockedTimesAction
|
||||
import io.timelimit.android.ui.main.ActivityViewModelHolder
|
||||
|
||||
class TryResetParentBlockedTimesDialogFragment: DialogFragment() {
|
||||
companion object {
|
||||
private const val DIALOG_TAG = "TryResetParentBlockedTimesDialogFragment"
|
||||
private const val PARENT_USER_ID = "parentUserId"
|
||||
|
||||
fun newInstance(parentUserId: String) = TryResetParentBlockedTimesDialogFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(PARENT_USER_ID, parentUserId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val parentUserId = arguments!!.getString(PARENT_USER_ID)!!
|
||||
val auth = (activity!! as ActivityViewModelHolder).getActivityViewModel()
|
||||
|
||||
auth.authenticatedUser.observe(this, Observer {
|
||||
if (it?.second?.type != UserType.Parent) {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
})
|
||||
|
||||
return AlertDialog.Builder(context!!, theme)
|
||||
.setMessage(R.string.manage_parent_blocked_times_info)
|
||||
.setPositiveButton(R.string.manage_parent_blocked_action_reset) { _, _ ->
|
||||
auth.tryDispatchParentAction(
|
||||
ResetParentBlockedTimesAction(
|
||||
parentId = parentUserId
|
||||
)
|
||||
)
|
||||
}
|
||||
.setNegativeButton(R.string.generic_cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
|
||||
}
|
|
@ -166,39 +166,6 @@
|
|||
<include android:id="@+id/parent_limit_login"
|
||||
layout="@layout/parent_limit_login_view" />
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
app:cardUseCompatPadding="true"
|
||||
android:onClick="@{() -> handlers.onManageBlockedTimesClicked()}"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="@string/manage_parent_blocked_times_title"
|
||||
android:textAppearance="?android:textAppearanceLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/manage_parent_blocked_times_description"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:text="@string/purchase_required_info_local_mode_free"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<include android:id="@+id/delete_parent"
|
||||
layout="@layout/delete_parent_view" />
|
||||
|
||||
|
|
|
@ -38,8 +38,6 @@
|
|||
<include android:id="@+id/child_without_premium"
|
||||
layout="@layout/new_login_fragment_child_without_premium" />
|
||||
|
||||
<include layout="@layout/new_login_fragment_blocked_time" />
|
||||
|
||||
<include layout="@layout/new_login_fragment_missing_trusted_time" />
|
||||
|
||||
<include layout="@layout/new_login_fragment_parent_login_blocked"
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 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">
|
||||
|
||||
<TextView
|
||||
android:gravity="center_horizontal"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:padding="8dp"
|
||||
android:text="@string/login_blocked_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</layout>
|
|
@ -28,7 +28,6 @@
|
|||
<string name="login_child_info">Dadurch wirst du als aktueller Nutzer dieses Gerätes festgelegt</string>
|
||||
<string name="login_child_done_toast">Der Benutzer dieses Gerätes wurde geändert</string>
|
||||
<string name="login_missing_trusted_time">Zum Anmelden mit diesem Benutzer wird die Uhrzeit benötigt</string>
|
||||
<string name="login_blocked_time">Das Anmelden mit diesem Benutzer zu dieser Zeit wurde gesperrt</string>
|
||||
<string name="login_missing_sync">Warte auf erfolgreiche Synchronisation</string>
|
||||
<string name="login_scan_code">Code scannen</string>
|
||||
<string name="login_scan_code_err_expired">Dieser Code ist alt</string>
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<?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/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="manage_parent_blocked_times_title">Zeitschloss (veraltet)</string>
|
||||
<string name="manage_parent_blocked_times_description">
|
||||
Sie können anstelle dieser Funktion eine Anmeldeverhinderungskategorie verwenden.
|
||||
Hiermit können Zeiten festgelegt werden, an denen sich ein Elternteil nicht anmelden kann.
|
||||
Das ist bei der Selbstbeschränkung relevant.
|
||||
</string>
|
||||
|
||||
<string name="manage_parent_blocked_times_info">
|
||||
Ein Elternteil kann nur für sich selbst Anmeldebeschränkungen festlegen.
|
||||
Andere Elternteile können diese nur zurücksetzen.
|
||||
</string>
|
||||
|
||||
<string name="manage_parent_blocked_action_reset">Einstellungen zurücksetzen</string>
|
||||
|
||||
<string name="manage_parent_lockout_hour_rule">Um ein Aussperren zu verhindern können maximal 18 Stunden je Tag blockiert werden</string>
|
||||
|
||||
<string name="manage_parent_blocked_times_toast">
|
||||
Sie verwenden das veraltete Zeitschloss für Ihren Elternbenutzer.
|
||||
Sie sollten stattdessen die Anmeldeverhinderungskategorie verwenden.
|
||||
</string>
|
||||
</resources>
|
|
@ -28,7 +28,6 @@
|
|||
<string name="login_child_info">This will set you as the current user of this device</string>
|
||||
<string name="login_child_done_toast">The user of this device was changed</string>
|
||||
<string name="login_missing_trusted_time">The current time is required to sign in with this user</string>
|
||||
<string name="login_blocked_time">Signing in as this user at this time was blocked</string>
|
||||
<string name="login_missing_sync">Waiting for a successful sync</string>
|
||||
<string name="login_scan_code">Scan code</string>
|
||||
<string name="login_scan_code_err_expired">This code is old</string>
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<?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/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="manage_parent_blocked_times_title">Time lock (deprecated)</string>
|
||||
<string name="manage_parent_blocked_times_description">
|
||||
You can use a limit login category instead.
|
||||
This allows to set times at which the parent can not sign in.
|
||||
This is relevant for limiting ones own usage.
|
||||
</string>
|
||||
|
||||
<string name="manage_parent_blocked_times_info">
|
||||
A parent can only add limits for itself.
|
||||
Other parents can only reset the limits.
|
||||
</string>
|
||||
|
||||
<string name="manage_parent_blocked_action_reset">Reset limits</string>
|
||||
|
||||
<string name="manage_parent_lockout_hour_rule">To prevent losing access, you can only block 18 hours per day</string>
|
||||
|
||||
<string name="manage_parent_blocked_times_toast">
|
||||
You are using the deprecated time lock for your parent user.
|
||||
You should use a limit login category instead.
|
||||
</string>
|
||||
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue