mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 01:39:22 +02:00
Allow disabling limits for single category from the lockscreen
This commit is contained in:
parent
aa06c8aaed
commit
2f68475fa3
8 changed files with 105 additions and 38 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 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
|
||||
|
@ -45,8 +45,9 @@ import io.timelimit.android.ui.help.HelpDialogFragment
|
|||
import io.timelimit.android.ui.main.ActivityViewModel
|
||||
import io.timelimit.android.ui.main.getActivityViewModel
|
||||
import io.timelimit.android.ui.manage.category.settings.networks.RequestWifiPermission
|
||||
import io.timelimit.android.ui.manage.child.advanced.managedisabletimelimits.ManageDisableTimelimitsViewHelper
|
||||
import io.timelimit.android.ui.manage.child.category.create.CreateCategoryDialogFragment
|
||||
import io.timelimit.android.ui.manage.child.category.specialmode.SetCategorySpecialModeFragment
|
||||
import io.timelimit.android.ui.manage.child.category.specialmode.SpecialModeDialogMode
|
||||
import io.timelimit.android.ui.manage.child.primarydevice.UpdatePrimaryDeviceDialogFragment
|
||||
import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment
|
||||
import io.timelimit.android.ui.view.SelectTimeSpanViewListener
|
||||
|
@ -115,6 +116,18 @@ class LockActionFragment : Fragment() {
|
|||
override fun requestLocationPermission() {
|
||||
RequestWifiPermission.doRequest(this@LockActionFragment, LOCATION_REQUEST_CODE)
|
||||
}
|
||||
|
||||
override fun disableLimitsTemporarily() {
|
||||
if (auth.requestAuthenticationOrReturnTrue()) {
|
||||
if (blockedCategoryId != null) {
|
||||
SetCategorySpecialModeFragment.newInstance(
|
||||
childId = userRelatedData.user.id,
|
||||
categoryId = blockedCategoryId,
|
||||
mode = SpecialModeDialogMode.DisableLimitsOnly
|
||||
).show(parentFragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,12 +266,6 @@ class LockActionFragment : Fragment() {
|
|||
categoryId = content.blockedCategoryId,
|
||||
timeZone = content.userRelatedData.timeZone
|
||||
)
|
||||
binding.manageDisableTimeLimits.handlers = ManageDisableTimelimitsViewHelper.createHandlers(
|
||||
childId = content.userId,
|
||||
childTimezone = content.timeZone,
|
||||
activity = requireActivity(),
|
||||
hasFullVersion = content.hasFullVersion
|
||||
)
|
||||
}
|
||||
is LockscreenContent.Blocked.BlockDueToNoCategory -> {
|
||||
binding.appCategoryTitle = null
|
||||
|
@ -298,4 +305,5 @@ interface Handlers {
|
|||
fun disableTemporarilyLockForAllCategories()
|
||||
fun setThisDeviceAsCurrentDevice()
|
||||
fun requestLocationPermission()
|
||||
fun disableLimitsTemporarily()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 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
|
||||
|
@ -43,6 +43,7 @@ import io.timelimit.android.ui.manage.child.ManageChildFragmentArgs
|
|||
import io.timelimit.android.ui.manage.child.ManageChildFragmentDirections
|
||||
import io.timelimit.android.ui.manage.child.category.create.CreateCategoryDialogFragment
|
||||
import io.timelimit.android.ui.manage.child.category.specialmode.SetCategorySpecialModeFragment
|
||||
import io.timelimit.android.ui.manage.child.category.specialmode.SpecialModeDialogMode
|
||||
|
||||
class ManageChildCategoriesFragment : Fragment() {
|
||||
companion object {
|
||||
|
@ -118,7 +119,7 @@ class ManageChildCategoriesFragment : Fragment() {
|
|||
SetCategorySpecialModeFragment.newInstance(
|
||||
childId = params.childId,
|
||||
categoryId = category.category.id,
|
||||
selfLimitMode = true
|
||||
mode = SpecialModeDialogMode.Regular
|
||||
).show(parentFragmentManager)
|
||||
|
||||
false
|
||||
|
@ -128,7 +129,7 @@ class ManageChildCategoriesFragment : Fragment() {
|
|||
SetCategorySpecialModeFragment.newInstance(
|
||||
childId = params.childId,
|
||||
categoryId = category.category.id,
|
||||
selfLimitMode = !auth.isParentAuthenticated()
|
||||
mode = if (auth.isParentAuthenticated()) SpecialModeDialogMode.Regular else SpecialModeDialogMode.SelfLimitAdd
|
||||
).show(parentFragmentManager)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 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
|
||||
|
@ -43,18 +43,18 @@ class SetCategorySpecialModeFragment: DialogFragment() {
|
|||
|
||||
private const val CHILD_ID = "childId"
|
||||
private const val CATEGORY_ID = "categoryId"
|
||||
private const val SELF_LIMIT_MODE = "selfLimitMode"
|
||||
private const val MODE = "mode"
|
||||
|
||||
private const val PAGE_TYPE = 0
|
||||
private const val PAGE_SUGGESTION = 1
|
||||
private const val PAGE_CLOCK = 2
|
||||
private const val PAGE_CALENDAR = 3
|
||||
|
||||
fun newInstance(childId: String, categoryId: String, selfLimitMode: Boolean) = SetCategorySpecialModeFragment().apply {
|
||||
fun newInstance(childId: String, categoryId: String, mode: SpecialModeDialogMode) = SetCategorySpecialModeFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(CHILD_ID, childId)
|
||||
putString(CATEGORY_ID, categoryId)
|
||||
putBoolean(SELF_LIMIT_MODE, selfLimitMode)
|
||||
putSerializable(MODE, mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,11 +73,11 @@ class SetCategorySpecialModeFragment: DialogFragment() {
|
|||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val childId = requireArguments().getString(CHILD_ID)!!
|
||||
val categoryId = requireArguments().getString(CATEGORY_ID)!!
|
||||
val selfLimitMode = requireArguments().getBoolean(SELF_LIMIT_MODE)
|
||||
val mode = requireArguments().getSerializable(MODE)!! as SpecialModeDialogMode
|
||||
|
||||
model.init(childId = childId, categoryId = categoryId, selfLimitAddMode = selfLimitMode)
|
||||
model.init(childId = childId, categoryId = categoryId, mode = mode)
|
||||
|
||||
if (selfLimitMode) {
|
||||
if (mode == SpecialModeDialogMode.SelfLimitAdd) {
|
||||
auth.authenticatedUserOrChild.observe(viewLifecycleOwner) { if (it == null) dismissAllowingStateLoss() }
|
||||
} else {
|
||||
auth.authenticatedUser.observe(viewLifecycleOwner) { if (it == null) dismissAllowingStateLoss() }
|
||||
|
@ -134,7 +134,8 @@ class SetCategorySpecialModeFragment: DialogFragment() {
|
|||
|
||||
when (screen) {
|
||||
is SetCategorySpecialModeModel.Screen.WithType.SuggestionList -> {
|
||||
setPage(PAGE_SUGGESTION)
|
||||
if (flipper.displayedChild == 0 && mode == SpecialModeDialogMode.DisableLimitsOnly) flipper.displayedChild = 1
|
||||
else setPage(PAGE_SUGGESTION)
|
||||
|
||||
specialModeOptionAdapter.items = screen.options
|
||||
}
|
||||
|
@ -148,7 +149,7 @@ class SetCategorySpecialModeFragment: DialogFragment() {
|
|||
binding.blockTemporarilyOption.setOnClickListener { model.selectType(SetCategorySpecialModeModel.Type.BlockTemporarily) }
|
||||
binding.disableLimitsOption.setOnClickListener { model.selectType(SetCategorySpecialModeModel.Type.DisableLimits) }
|
||||
|
||||
binding.isAddLimitMode = selfLimitMode
|
||||
binding.isAddLimitMode = mode == SpecialModeDialogMode.SelfLimitAdd
|
||||
|
||||
binding.suggestionList.also {
|
||||
it.adapter = specialModeOptionAdapter
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 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
|
||||
|
@ -39,7 +39,7 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
private val typeLive = MutableLiveData<Type?>().apply { value = null }
|
||||
private var didInit = false
|
||||
private val childAndCategoryId = MutableLiveData<Pair<String, String>>()
|
||||
private val selfLimitAddModeLive = MutableLiveData<Boolean>().apply { value = false }
|
||||
private val specialMode = MutableLiveData<SpecialModeDialogMode>()
|
||||
|
||||
fun now() = logic.realTimeLogic.getCurrentTimeInMillis()
|
||||
|
||||
|
@ -68,8 +68,8 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
}
|
||||
}
|
||||
|
||||
val minTimestamp = selfLimitAddModeLive.switchMap { selfLimitAddMode ->
|
||||
if (selfLimitAddMode) selfLimitAddModeMinTimestamp else nowLive
|
||||
val minTimestamp = specialMode.switchMap { mode ->
|
||||
if (mode == SpecialModeDialogMode.SelfLimitAdd) selfLimitAddModeMinTimestamp else nowLive
|
||||
}
|
||||
|
||||
val content: LiveData<Content?> = object: MediatorLiveData<Content>() {
|
||||
|
@ -80,7 +80,7 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
addSource(durationSelectionLive) { update() }
|
||||
addSource(typeLive) { update() }
|
||||
addSource(userRelatedData) { didLoadUserRelatedData = true; update() }
|
||||
addSource(selfLimitAddModeLive) { update() }
|
||||
addSource(specialMode) { update() }
|
||||
}
|
||||
|
||||
fun update() {
|
||||
|
@ -93,19 +93,22 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
val durationSelection = durationSelectionLive.value!!
|
||||
val type = typeLive.value
|
||||
val (userRelatedData, categoryId) = userRelatedData.value ?: run { value = null; return }
|
||||
val selfLimitAddMode = selfLimitAddModeLive.value ?: return
|
||||
val specialMode = specialMode.value ?: return
|
||||
|
||||
val targetCategory = userRelatedData.categoryById[categoryId] ?: run { value = null; return }
|
||||
val categoryTitle = targetCategory.category.title
|
||||
|
||||
if (targetCategory.category.temporarilyBlocked && targetCategory.category.temporarilyBlockedEndTime == 0L && selfLimitAddMode) {
|
||||
if (
|
||||
targetCategory.category.temporarilyBlocked && targetCategory.category.temporarilyBlockedEndTime == 0L &&
|
||||
specialMode == SpecialModeDialogMode.SelfLimitAdd
|
||||
) {
|
||||
value = null; return
|
||||
}
|
||||
|
||||
val screen = if (type == null) Screen.SelectType else when (durationSelection) {
|
||||
DurationSelection.SuggestionList -> when (type) {
|
||||
Type.BlockTemporarily -> {
|
||||
if (selfLimitAddMode) SpecialModeDuration.items
|
||||
if (specialMode == SpecialModeDialogMode.SelfLimitAdd) SpecialModeDuration.items
|
||||
else listOf(SpecialModeOption.NoEndTimeOption) + SpecialModeDuration.items
|
||||
}
|
||||
Type.DisableLimits -> SpecialModeDuration.items
|
||||
|
@ -136,7 +139,10 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
durationSelectionLive.value = DurationSelection.SuggestionList
|
||||
|
||||
true
|
||||
} else if (typeLive.value != null) {
|
||||
} else if (
|
||||
typeLive.value != null &&
|
||||
specialMode.value != SpecialModeDialogMode.DisableLimitsOnly
|
||||
) {
|
||||
typeLive.value = null
|
||||
|
||||
true
|
||||
|
@ -147,7 +153,7 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
fun applySelection(selection: SpecialModeOption, auth: ActivityViewModel) {
|
||||
val content = content.value
|
||||
val screen = content?.screen
|
||||
val selfLimitAddMode = selfLimitAddModeLive.value ?: return
|
||||
val specialMode = specialMode.value ?: return
|
||||
|
||||
if (screen is Screen.WithType) {
|
||||
when (selection) {
|
||||
|
@ -160,7 +166,7 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
timezone = content.childTimezone
|
||||
)
|
||||
|
||||
if (selfLimitAddMode) {
|
||||
if (specialMode == SpecialModeDialogMode.SelfLimitAdd) {
|
||||
val minTime = minTimestamp.value ?: return
|
||||
|
||||
if (endTime < minTime) {
|
||||
|
@ -176,7 +182,7 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
endTime = endTime,
|
||||
blocked = true
|
||||
),
|
||||
allowAsChild = selfLimitAddMode
|
||||
allowAsChild = specialMode == SpecialModeDialogMode.SelfLimitAdd
|
||||
)
|
||||
|
||||
requestClose.value = true
|
||||
|
@ -221,12 +227,16 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap
|
|||
auth = auth
|
||||
)
|
||||
|
||||
fun init(childId: String, categoryId: String, selfLimitAddMode: Boolean) {
|
||||
fun init(childId: String, categoryId: String, mode: SpecialModeDialogMode) {
|
||||
if (!didInit) {
|
||||
didInit = true
|
||||
|
||||
childAndCategoryId.value = childId to categoryId
|
||||
selfLimitAddModeLive.value = selfLimitAddMode
|
||||
specialMode.value = mode
|
||||
|
||||
if (mode == SpecialModeDialogMode.DisableLimitsOnly) {
|
||||
selectType(Type.DisableLimits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 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.child.category.specialmode
|
||||
|
||||
enum class SpecialModeDialogMode {
|
||||
Regular,
|
||||
DisableLimitsOnly,
|
||||
SelfLimitAdd
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2022 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.
|
||||
|
@ -282,11 +282,32 @@
|
|||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<io.timelimit.android.ui.view.ManageDisableTimelimitsView
|
||||
<androidx.cardview.widget.CardView
|
||||
android:visibility="@{reason == BlockingReason.BlockedAtThisTime || reason == BlockingReason.TimeOver || reason == BlockingReason.TimeOverExtraTimeCanBeUsedLater || reason == BlockingReason.SessionDurationLimit || reason == BlockingReason.MissingRequiredNetwork ? View.VISIBLE : View.GONE}"
|
||||
android:id="@+id/manage_disable_time_limits"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:onClick="@{() -> handlers.disableLimitsTemporarily()}"
|
||||
app:cardUseCompatPadding="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
tools:text="@string/manage_child_special_mode_wizard_disable_limits_title"
|
||||
android:text="@{@string/manage_child_special_mode_wizard_disable_limits_title(appCategoryTitle)}"
|
||||
android:textAppearance="?android:textAppearanceLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/lock_disable_limits_temporarily_text"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:visibility="@{reason == BlockingReason.RequiresCurrentDevice ? View.VISIBLE : View.GONE}"
|
||||
|
|
|
@ -620,6 +620,8 @@
|
|||
<string name="lock_grant_permission_title">Berechtigung erteilen</string>
|
||||
<string name="lock_grant_permission_text">TimeLimit ermöglichen, das verbundene Netzwerk zu erkennen</string>
|
||||
|
||||
<string name="lock_disable_limits_temporarily_text">Begrenzungen für die Kategorie vorübergehnd deaktivieren, um diese Anwendung freizugeben</string>
|
||||
|
||||
<string name="lock_reason_no_category">
|
||||
Die App wurde zu keiner Kategorie zugeordnet. Das bedeutet, dass es keine Einschränkungs-Einstellungen gibt. Und da ist es die einfachste Lösung, die App zu sperren.
|
||||
</string>
|
||||
|
|
|
@ -671,6 +671,8 @@
|
|||
<string name="lock_grant_permission_title">Grant permission</string>
|
||||
<string name="lock_grant_permission_text">Allow TimeLimit to see the connected network</string>
|
||||
|
||||
<string name="lock_disable_limits_temporarily_text">Temporarily disable limits for the category to allow using this application</string>
|
||||
|
||||
<string name="lock_reason_no_category">
|
||||
This App was not assigned to any category.
|
||||
Due to that, there are no restriction settings.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue