Use CategoryAppsAndRulesFragment for CategoryTimeLimitRulesFragment

This commit is contained in:
Jonas Lochmann 2020-10-26 01:00:00 +01:00
parent 2c30b648e6
commit 2e97eaec5e
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
5 changed files with 113 additions and 161 deletions

View file

@ -39,6 +39,7 @@ class AppAndRuleAdapter: RecyclerView.Adapter<AppAndRuleAdapter.Holder>() {
private const val RULE_ENTRY = 4 private const val RULE_ENTRY = 4
private const val EXPAND_RULES_ITEM = 5 private const val EXPAND_RULES_ITEM = 5
private const val RULES_INTRO = 6 private const val RULES_INTRO = 6
private const val ADD_RULE_ITEM = 7
} }
var items: List<AppAndRuleItem> by Delegates.observable(emptyList()) { _, _, _ -> notifyDataSetChanged() } var items: List<AppAndRuleItem> by Delegates.observable(emptyList()) { _, _, _ -> notifyDataSetChanged() }
@ -67,6 +68,7 @@ class AppAndRuleAdapter: RecyclerView.Adapter<AppAndRuleAdapter.Holder>() {
is AppAndRuleItem.RuleEntry -> RULE_ENTRY is AppAndRuleItem.RuleEntry -> RULE_ENTRY
AppAndRuleItem.ExpandRulesItem -> EXPAND_RULES_ITEM AppAndRuleItem.ExpandRulesItem -> EXPAND_RULES_ITEM
AppAndRuleItem.RulesIntro -> RULES_INTRO AppAndRuleItem.RulesIntro -> RULES_INTRO
AppAndRuleItem.AddRuleItem -> ADD_RULE_ITEM
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder = Holder( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder = Holder(
@ -106,6 +108,16 @@ class AppAndRuleAdapter: RecyclerView.Adapter<AppAndRuleAdapter.Holder>() {
parent, parent,
false false
).root ).root
ADD_RULE_ITEM -> AddItemViewBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
).apply {
label = parent.context.getString(R.string.category_time_limit_rule_dialog_new)
wide = true
root.setOnClickListener { handlers?.onAddTimeLimitRuleClicked() }
}.root
else -> throw IllegalArgumentException() else -> throw IllegalArgumentException()
} }
) )
@ -173,6 +185,7 @@ class AppAndRuleAdapter: RecyclerView.Adapter<AppAndRuleAdapter.Holder>() {
} }
AppAndRuleItem.ExpandRulesItem -> {/* nothing to do */} AppAndRuleItem.ExpandRulesItem -> {/* nothing to do */}
AppAndRuleItem.RulesIntro -> {/* nothing to do */} AppAndRuleItem.RulesIntro -> {/* nothing to do */}
AppAndRuleItem.AddRuleItem -> {/* nothing to do */}
}.let { } }.let { }
} }

View file

@ -25,4 +25,5 @@ sealed class AppAndRuleItem {
data class RuleEntry(val rule: TimeLimitRule): AppAndRuleItem() data class RuleEntry(val rule: TimeLimitRule): AppAndRuleItem()
object ExpandRulesItem: AppAndRuleItem() object ExpandRulesItem: AppAndRuleItem()
object RulesIntro: AppAndRuleItem() object RulesIntro: AppAndRuleItem()
object AddRuleItem: AppAndRuleItem()
} }

View file

@ -21,24 +21,34 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import io.timelimit.android.R import io.timelimit.android.R
import io.timelimit.android.async.Threads
import io.timelimit.android.data.Database import io.timelimit.android.data.Database
import io.timelimit.android.data.extensions.getDateLive
import io.timelimit.android.data.model.HintsToShow
import io.timelimit.android.data.model.TimeLimitRule import io.timelimit.android.data.model.TimeLimitRule
import io.timelimit.android.livedata.map
import io.timelimit.android.livedata.switchMap
import io.timelimit.android.logic.DefaultAppLogic import io.timelimit.android.logic.DefaultAppLogic
import io.timelimit.android.sync.actions.AddCategoryAppsAction import io.timelimit.android.sync.actions.AddCategoryAppsAction
import io.timelimit.android.sync.actions.CreateTimeLimitRuleAction
import io.timelimit.android.sync.actions.RemoveCategoryAppsAction import io.timelimit.android.sync.actions.RemoveCategoryAppsAction
import io.timelimit.android.sync.actions.UpdateTimeLimitRuleAction
import io.timelimit.android.ui.main.ActivityViewModel import io.timelimit.android.ui.main.ActivityViewModel
import io.timelimit.android.ui.main.getActivityViewModel import io.timelimit.android.ui.main.getActivityViewModel
import io.timelimit.android.ui.manage.category.apps.add.AddCategoryAppsFragment import io.timelimit.android.ui.manage.category.apps.add.AddCategoryAppsFragment
import io.timelimit.android.ui.manage.category.timelimit_rules.edit.EditTimeLimitRuleDialogFragment import io.timelimit.android.ui.manage.category.timelimit_rules.edit.EditTimeLimitRuleDialogFragment
import io.timelimit.android.ui.manage.category.timelimit_rules.edit.EditTimeLimitRuleDialogFragmentListener
import kotlinx.android.synthetic.main.fragment_category_apps_and_rules.* import kotlinx.android.synthetic.main.fragment_category_apps_and_rules.*
abstract class CategoryAppsAndRulesFragment: Fragment(), Handlers { abstract class CategoryAppsAndRulesFragment: Fragment(), Handlers, EditTimeLimitRuleDialogFragmentListener {
private val adapter = AppAndRuleAdapter().also { it.handlers = this } private val adapter = AppAndRuleAdapter().also { it.handlers = this }
private val database: Database by lazy { DefaultAppLogic.with(requireContext()).database } val auth: ActivityViewModel by lazy { getActivityViewModel(requireActivity()) }
private val auth: ActivityViewModel by lazy { getActivityViewModel(requireActivity()) } val database: Database by lazy { DefaultAppLogic.with(requireContext()).database }
abstract val childId: String abstract val childId: String
abstract val categoryId: String abstract val categoryId: String
@ -51,10 +61,86 @@ abstract class CategoryAppsAndRulesFragment: Fragment(), Handlers {
recycler.layoutManager = LinearLayoutManager(requireContext()) recycler.layoutManager = LinearLayoutManager(requireContext())
recycler.adapter = adapter recycler.adapter = adapter
ItemTouchHelper(object: ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
val index = viewHolder.adapterPosition
val item = if (index == RecyclerView.NO_POSITION) null else adapter.items[index]
if (item == AppAndRuleItem.RulesIntro) {
return makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE, ItemTouchHelper.END or ItemTouchHelper.START) or
makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.END or ItemTouchHelper.START)
} else {
return 0
}
}
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder) = throw IllegalStateException()
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val database = database
Threads.database.submit {
database.config().setHintsShownSync(HintsToShow.TIME_LIMIT_RULE_INTRODUCTION)
}
}
}).attachToRecyclerView(recycler)
val userDate = database.user().getUserByIdLive(childId).getDateLive(auth.logic.realTimeLogic)
userDate.switchMap { date ->
val firstDayOfWeekAsEpochDay = date.dayOfEpoch - date.dayOfWeek
database.usedTimes().getUsedTimesOfWeek(
categoryId = categoryId,
firstDayOfWeekAsEpochDay = firstDayOfWeekAsEpochDay
).map { res ->
firstDayOfWeekAsEpochDay to res
}
}.observe(viewLifecycleOwner) {
adapter.epochDayOfStartOfWeek = it.first
adapter.usedTimes = it.second
}
} }
fun setListContent(items: List<AppAndRuleItem>) { adapter.items = items } fun setListContent(items: List<AppAndRuleItem>) { adapter.items = items }
override fun notifyRuleCreated() {
Snackbar.make(requireView(), R.string.category_time_limit_rules_snackbar_created, Snackbar.LENGTH_SHORT)
.show()
}
override fun notifyRuleDeleted(oldRule: TimeLimitRule) {
Snackbar.make(requireView(), R.string.category_time_limit_rules_snackbar_deleted, Snackbar.LENGTH_SHORT)
.setAction(R.string.generic_undo) {
auth.tryDispatchParentAction(
CreateTimeLimitRuleAction(
rule = oldRule
)
)
}
.show()
}
override fun notifyRuleUpdated(oldRule: TimeLimitRule, newRule: TimeLimitRule) {
Snackbar.make(requireView(), R.string.category_time_limit_rules_snackbar_updated, Snackbar.LENGTH_SHORT)
.setAction(R.string.generic_undo) {
auth.tryDispatchParentAction(
UpdateTimeLimitRuleAction(
ruleId = oldRule.id,
applyToExtraTimeUsage = oldRule.applyToExtraTimeUsage,
maximumTimeInMillis = oldRule.maximumTimeInMillis,
dayMask = oldRule.dayMask,
start = oldRule.startMinuteOfDay,
end = oldRule.endMinuteOfDay,
sessionDurationMilliseconds = oldRule.sessionDurationMilliseconds,
sessionPauseMilliseconds = oldRule.sessionPauseMilliseconds
)
)
}
.show()
}
override fun onAppClicked(app: AppAndRuleItem.AppEntry) { override fun onAppClicked(app: AppAndRuleItem.AppEntry) {
if (auth.tryDispatchParentAction( if (auth.tryDispatchParentAction(
RemoveCategoryAppsAction( RemoveCategoryAppsAction(

View file

@ -16,36 +16,17 @@
package io.timelimit.android.ui.manage.category.timelimit_rules package io.timelimit.android.ui.manage.category.timelimit_rules
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import io.timelimit.android.R
import io.timelimit.android.async.Threads
import io.timelimit.android.data.Database
import io.timelimit.android.data.extensions.getDateLive
import io.timelimit.android.data.model.HintsToShow import io.timelimit.android.data.model.HintsToShow
import io.timelimit.android.data.model.TimeLimitRule import io.timelimit.android.data.model.TimeLimitRule
import io.timelimit.android.livedata.map import io.timelimit.android.livedata.map
import io.timelimit.android.livedata.switchMap import io.timelimit.android.livedata.switchMap
import io.timelimit.android.logic.AppLogic
import io.timelimit.android.logic.DefaultAppLogic
import io.timelimit.android.sync.actions.CreateTimeLimitRuleAction
import io.timelimit.android.sync.actions.UpdateTimeLimitRuleAction
import io.timelimit.android.ui.main.ActivityViewModel
import io.timelimit.android.ui.main.getActivityViewModel
import io.timelimit.android.ui.manage.category.ManageCategoryFragmentArgs import io.timelimit.android.ui.manage.category.ManageCategoryFragmentArgs
import io.timelimit.android.ui.manage.category.timelimit_rules.edit.EditTimeLimitRuleDialogFragment import io.timelimit.android.ui.manage.category.appsandrules.AppAndRuleItem
import io.timelimit.android.ui.manage.category.timelimit_rules.edit.EditTimeLimitRuleDialogFragmentListener import io.timelimit.android.ui.manage.category.appsandrules.CategoryAppsAndRulesFragment
import kotlinx.android.synthetic.main.fragment_category_time_limit_rules.*
class CategoryTimeLimitRulesFragment : Fragment(), EditTimeLimitRuleDialogFragmentListener { class CategoryTimeLimitRulesFragment: CategoryAppsAndRulesFragment() {
companion object { companion object {
fun newInstance(params: ManageCategoryFragmentArgs): CategoryTimeLimitRulesFragment { fun newInstance(params: ManageCategoryFragmentArgs): CategoryTimeLimitRulesFragment {
val result = CategoryTimeLimitRulesFragment() val result = CategoryTimeLimitRulesFragment()
@ -54,130 +35,28 @@ class CategoryTimeLimitRulesFragment : Fragment(), EditTimeLimitRuleDialogFragme
} }
} }
private val logic: AppLogic by lazy { DefaultAppLogic.with(context!!) } private val params: ManageCategoryFragmentArgs by lazy { ManageCategoryFragmentArgs.fromBundle(requireArguments()) }
private val params: ManageCategoryFragmentArgs by lazy { ManageCategoryFragmentArgs.fromBundle(arguments!!) }
private val database: Database by lazy { logic.database }
private val rules: LiveData<List<TimeLimitRule>> by lazy { database.timeLimitRules().getTimeLimitRulesByCategory(params.categoryId) } private val rules: LiveData<List<TimeLimitRule>> by lazy { database.timeLimitRules().getTimeLimitRulesByCategory(params.categoryId) }
private val auth: ActivityViewModel by lazy { getActivityViewModel(activity!!) } override val childId: String get() = params.childId
override val categoryId: String get() = params.categoryId
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_category_time_limit_rules, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val adapter = Adapter()
recycler.layoutManager = LinearLayoutManager(context!!)
recycler.adapter = adapter
val userDate = database.user().getUserByIdLive(params.childId).getDateLive(logic.realTimeLogic)
userDate.switchMap { date ->
val firstDayOfWeekAsEpochDay = date.dayOfEpoch - date.dayOfWeek
database.usedTimes().getUsedTimesOfWeek(
categoryId = params.categoryId,
firstDayOfWeekAsEpochDay = firstDayOfWeekAsEpochDay
).map { res ->
firstDayOfWeekAsEpochDay to res
}
}.observe(viewLifecycleOwner, Observer {
adapter.epochDayOfStartOfWeek = it.first
adapter.usedTimes = it.second
})
val hasHiddenIntro = database.config().wereHintsShown(HintsToShow.TIME_LIMIT_RULE_INTRODUCTION) val hasHiddenIntro = database.config().wereHintsShown(HintsToShow.TIME_LIMIT_RULE_INTRODUCTION)
rules.map { rules -> rules.map { rules ->
rules.sortedBy { it.dayMask }.map { TimeLimitRuleRuleItem(it) } rules.sortedBy { it.dayMask }.map { AppAndRuleItem.RuleEntry(it) }
}.switchMap { }.switchMap {
val baseList = it + listOf(AddTimeLimitRuleItem) val baseList = it + listOf(AppAndRuleItem.AddRuleItem)
hasHiddenIntro.map { hasHiddenIntro -> hasHiddenIntro.map { hasHiddenIntro ->
if (hasHiddenIntro) { if (hasHiddenIntro) {
baseList baseList
} else { } else {
listOf(TimeLimitRuleIntroductionItem) + baseList listOf(AppAndRuleItem.RulesIntro) + baseList
} }
} }
}.observe(viewLifecycleOwner, Observer { }.observe(viewLifecycleOwner) { setListContent(it) }
adapter.data = it
})
adapter.handlers = object: TimeLimitRulesHandlers {
override fun onTimeLimitRuleClicked(rule: TimeLimitRule) {
if (auth.requestAuthenticationOrReturnTrue()) {
EditTimeLimitRuleDialogFragment.newInstance(rule, this@CategoryTimeLimitRulesFragment).show(fragmentManager!!)
}
}
override fun onAddTimeLimitRuleClicked() {
if (auth.requestAuthenticationOrReturnTrueAllowChild(childId = params.childId)) {
EditTimeLimitRuleDialogFragment.newInstance(params.categoryId, this@CategoryTimeLimitRulesFragment).show(fragmentManager!!)
}
}
}
ItemTouchHelper(object: ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
val index = viewHolder.adapterPosition
val item = if (index == RecyclerView.NO_POSITION) null else adapter.data[index]
if (item == TimeLimitRuleIntroductionItem) {
return makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE, ItemTouchHelper.END or ItemTouchHelper.START) or
makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.END or ItemTouchHelper.START)
} else {
return 0
}
}
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder) = throw IllegalStateException()
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val database = logic.database
Threads.database.submit {
database.config().setHintsShownSync(HintsToShow.TIME_LIMIT_RULE_INTRODUCTION)
}
}
}).attachToRecyclerView(recycler)
}
override fun notifyRuleCreated() {
Snackbar.make(view!!, R.string.category_time_limit_rules_snackbar_created, Snackbar.LENGTH_SHORT)
.show()
}
override fun notifyRuleDeleted(oldRule: TimeLimitRule) {
Snackbar.make(view!!, R.string.category_time_limit_rules_snackbar_deleted, Snackbar.LENGTH_SHORT)
.setAction(R.string.generic_undo) {
auth.tryDispatchParentAction(
CreateTimeLimitRuleAction(
rule = oldRule
)
)
}
.show()
}
override fun notifyRuleUpdated(oldRule: TimeLimitRule, newRule: TimeLimitRule) {
Snackbar.make(view!!, R.string.category_time_limit_rules_snackbar_updated, Snackbar.LENGTH_SHORT)
.setAction(R.string.generic_undo) {
auth.tryDispatchParentAction(
UpdateTimeLimitRuleAction(
ruleId = oldRule.id,
applyToExtraTimeUsage = oldRule.applyToExtraTimeUsage,
maximumTimeInMillis = oldRule.maximumTimeInMillis,
dayMask = oldRule.dayMask,
start = oldRule.startMinuteOfDay,
end = oldRule.endMinuteOfDay,
sessionDurationMilliseconds = oldRule.sessionDurationMilliseconds,
sessionPauseMilliseconds = oldRule.sessionPauseMilliseconds
)
)
}
.show()
} }
} }

View file

@ -1,27 +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/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="io.timelimit.android.ui.manage.category.timelimit_rules.CategoryTimeLimitRulesFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>