diff --git a/app/schemas/io.timelimit.android.data.RoomDatabase/32.json b/app/schemas/io.timelimit.android.data.RoomDatabase/32.json index 7119915..90fd1cd 100644 --- a/app/schemas/io.timelimit.android.data.RoomDatabase/32.json +++ b/app/schemas/io.timelimit.android.data.RoomDatabase/32.json @@ -81,7 +81,7 @@ "notNull": true }, { - "fieldPath": "blockedTimes", + "fieldPath": "obsoleteBlockedTimes", "columnName": "blocked_times", "affinity": "TEXT", "notNull": true diff --git a/app/src/main/java/io/timelimit/android/data/model/User.kt b/app/src/main/java/io/timelimit/android/data/model/User.kt index c162947..edffcce 100644 --- a/app/src/main/java/io/timelimit/android/data/model/User.kt +++ b/app/src/main/java/io/timelimit/android/data/model/User.kt @@ -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() diff --git a/app/src/main/java/io/timelimit/android/logic/AppSetupLogic.kt b/app/src/main/java/io/timelimit/android/logic/AppSetupLogic.kt index e724faa..5aa1d5f 100644 --- a/app/src/main/java/io/timelimit/android/logic/AppSetupLogic.kt +++ b/app/src/main/java/io/timelimit/android/logic/AppSetupLogic.kt @@ -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 ) diff --git a/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt b/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt index 3ea5d6e..57da18f 100644 --- a/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt +++ b/app/src/main/java/io/timelimit/android/sync/ApplyServerDataStatus.kt @@ -66,7 +66,6 @@ object ApplyServerDataStatus { categoryForNotAssignedApps = newEntry.categoryForNotAssignedApps, relaxPrimaryDevice = newEntry.relaxPrimaryDevice, mailNotificationFlags = newEntry.mailNotificationFlags, - blockedTimes = newEntry.blockedTimes, flags = newEntry.flags ) diff --git a/app/src/main/java/io/timelimit/android/sync/actions/Actions.kt b/app/src/main/java/io/timelimit/android/sync/actions/Actions.kt index dd0b1e4..125d550 100644 --- a/app/src/main/java/io/timelimit/android/sync/actions/Actions.kt +++ b/app/src/main/java/io/timelimit/android/sync/actions/Actions.kt @@ -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" diff --git a/app/src/main/java/io/timelimit/android/sync/actions/dispatch/ParentAction.kt b/app/src/main/java/io/timelimit/android/sync/actions/dispatch/ParentAction.kt index d62117c..79cd4a6 100644 --- a/app/src/main/java/io/timelimit/android/sync/actions/dispatch/ParentAction.kt +++ b/app/src/main/java/io/timelimit/android/sync/actions/dispatch/ParentAction.kt @@ -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") diff --git a/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt b/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt index 27a7acd..680b28b 100644 --- a/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt +++ b/app/src/main/java/io/timelimit/android/sync/network/ServerDataStatus.kt @@ -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 ) diff --git a/app/src/main/java/io/timelimit/android/ui/login/AllowUserLoginStatus.kt b/app/src/main/java/io/timelimit/android/ui/login/AllowUserLoginStatus.kt index 15e80e6..47017f5 100644 --- a/app/src/main/java/io/timelimit/android/ui/login/AllowUserLoginStatus.kt +++ b/app/src/main/java/io/timelimit/android/ui/login/AllowUserLoginStatus.kt @@ -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 } diff --git a/app/src/main/java/io/timelimit/android/ui/login/LoginDialogFragmentModel.kt b/app/src/main/java/io/timelimit/android/ui/login/LoginDialogFragmentModel.kt index 3fb874c..335d4e5 100644 --- a/app/src/main/java/io/timelimit/android/ui/login/LoginDialogFragmentModel.kt +++ b/app/src/main/java/io/timelimit/android/ui/login/LoginDialogFragmentModel.kt @@ -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().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, 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( diff --git a/app/src/main/java/io/timelimit/android/ui/login/NewLoginFragment.kt b/app/src/main/java/io/timelimit/android/ui/login/NewLoginFragment.kt index 62ad1d9..e2433b1 100644 --- a/app/src/main/java/io/timelimit/android/ui/login/NewLoginFragment.kt +++ b/app/src/main/java/io/timelimit/android/ui/login/NewLoginFragment.kt @@ -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 } diff --git a/app/src/main/java/io/timelimit/android/ui/manage/category/blocked_times/BlockedTimeAreasHelpDialog.kt b/app/src/main/java/io/timelimit/android/ui/manage/category/blocked_times/BlockedTimeAreasHelpDialog.kt index 28c03f9..84c46f5 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/category/blocked_times/BlockedTimeAreasHelpDialog.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/category/blocked_times/BlockedTimeAreasHelpDialog.kt @@ -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) } diff --git a/app/src/main/java/io/timelimit/android/ui/manage/parent/blockedtimes/ManageParentBlockedTimesFragment.kt b/app/src/main/java/io/timelimit/android/ui/manage/parent/blockedtimes/ManageParentBlockedTimesFragment.kt deleted file mode 100644 index 7553cef..0000000 --- a/app/src/main/java/io/timelimit/android/ui/manage/parent/blockedtimes/ManageParentBlockedTimesFragment.kt +++ /dev/null @@ -1,180 +0,0 @@ -/* - * TimeLimit Copyright 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 . - */ -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 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) { - 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 - } -} diff --git a/app/src/main/java/io/timelimit/android/ui/manage/parent/blockedtimes/TryResetParentBlockedTimesDialogFragment.kt b/app/src/main/java/io/timelimit/android/ui/manage/parent/blockedtimes/TryResetParentBlockedTimesDialogFragment.kt deleted file mode 100644 index 0a8d58e..0000000 --- a/app/src/main/java/io/timelimit/android/ui/manage/parent/blockedtimes/TryResetParentBlockedTimesDialogFragment.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * TimeLimit Copyright 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 . - */ -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) -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_manage_parent.xml b/app/src/main/res/layout/fragment_manage_parent.xml index 6f93bf0..62921b8 100644 --- a/app/src/main/res/layout/fragment_manage_parent.xml +++ b/app/src/main/res/layout/fragment_manage_parent.xml @@ -166,39 +166,6 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/new_login_fragment.xml b/app/src/main/res/layout/new_login_fragment.xml index 9831c42..2e173cb 100644 --- a/app/src/main/res/layout/new_login_fragment.xml +++ b/app/src/main/res/layout/new_login_fragment.xml @@ -38,8 +38,6 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-de/strings-login.xml b/app/src/main/res/values-de/strings-login.xml index 66fcfe1..d0e2fa3 100644 --- a/app/src/main/res/values-de/strings-login.xml +++ b/app/src/main/res/values-de/strings-login.xml @@ -28,7 +28,6 @@ Dadurch wirst du als aktueller Nutzer dieses Gerätes festgelegt Der Benutzer dieses Gerätes wurde geändert Zum Anmelden mit diesem Benutzer wird die Uhrzeit benötigt - Das Anmelden mit diesem Benutzer zu dieser Zeit wurde gesperrt Warte auf erfolgreiche Synchronisation Code scannen Dieser Code ist alt diff --git a/app/src/main/res/values-de/strings-manage-parent-blocked-times.xml b/app/src/main/res/values-de/strings-manage-parent-blocked-times.xml deleted file mode 100644 index 3e4c4d8..0000000 --- a/app/src/main/res/values-de/strings-manage-parent-blocked-times.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - Zeitschloss (veraltet) - - 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. - - - - Ein Elternteil kann nur für sich selbst Anmeldebeschränkungen festlegen. - Andere Elternteile können diese nur zurücksetzen. - - - Einstellungen zurücksetzen - - Um ein Aussperren zu verhindern können maximal 18 Stunden je Tag blockiert werden - - - Sie verwenden das veraltete Zeitschloss für Ihren Elternbenutzer. - Sie sollten stattdessen die Anmeldeverhinderungskategorie verwenden. - - \ No newline at end of file diff --git a/app/src/main/res/values/strings-login.xml b/app/src/main/res/values/strings-login.xml index 75011eb..2216a20 100644 --- a/app/src/main/res/values/strings-login.xml +++ b/app/src/main/res/values/strings-login.xml @@ -28,7 +28,6 @@ This will set you as the current user of this device The user of this device was changed The current time is required to sign in with this user - Signing in as this user at this time was blocked Waiting for a successful sync Scan code This code is old diff --git a/app/src/main/res/values/strings-manage-parent-blocked-times.xml b/app/src/main/res/values/strings-manage-parent-blocked-times.xml deleted file mode 100644 index cdd0669..0000000 --- a/app/src/main/res/values/strings-manage-parent-blocked-times.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - Time lock (deprecated) - - 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. - - - - A parent can only add limits for itself. - Other parents can only reset the limits. - - - Reset limits - - To prevent losing access, you can only block 18 hours per day - - - You are using the deprecated time lock for your parent user. - You should use a limit login category instead. - - \ No newline at end of file