Add category filter for the usage history

This commit is contained in:
Jonas Lochmann 2020-10-26 01:00:00 +01:00
parent 08f6884d80
commit cb19f4e19f
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
9 changed files with 220 additions and 49 deletions

View file

@ -143,8 +143,12 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
@ -161,6 +165,7 @@ dependencies {
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation "com.google.android.material:material:1.2.1"
implementation 'androidx.fragment:fragment-ktx:1.2.5'
implementation "android.arch.navigation:navigation-fragment-ktx:$nav_version"
implementation "android.arch.navigation:navigation-ui:$nav_version"

View file

@ -28,7 +28,7 @@ abstract class BottomSheetSelectionListDialog: BottomSheetDialogFragment() {
private lateinit var binding: BottomSheetSelectionListBinding
private var didClearList = false
abstract val title: String
abstract val title: String?
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = BottomSheetSelectionListBinding.inflate(inflater, container, false)

View file

@ -0,0 +1,76 @@
/*
* 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.category.usagehistory
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import io.timelimit.android.R
import io.timelimit.android.extensions.showSafe
import io.timelimit.android.logic.DefaultAppLogic
import io.timelimit.android.ui.fragment.BottomSheetSelectionListDialog
class SelectUsageHistoryCategoryDialog: BottomSheetSelectionListDialog() {
companion object {
private const val USER_ID = "userId"
private const val CURRENT_CATEGORY_ID = "currentCategoryId"
private const val DIALOG_TAG = "SelectUsageHistoryCategoryDialog"
fun newInstance(userId: String, currentCategoryId: String?, target: Fragment) = SelectUsageHistoryCategoryDialog().apply {
setTargetFragment(target, 0)
arguments = Bundle().apply {
putString(USER_ID, userId)
if (currentCategoryId != null) putString(CURRENT_CATEGORY_ID, currentCategoryId)
}
}
}
override val title: String? get() = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val userId: String = requireArguments().getString(USER_ID)!!
val currentCategoryId: String? = requireArguments().getString(CURRENT_CATEGORY_ID)
val target = targetFragment as Listener
DefaultAppLogic.with(requireContext()).database.category().getCategoriesByChildId(userId).observe(viewLifecycleOwner) { categories ->
clearList()
categories.forEach {
addListItem(
label = it.title,
checked = it.id == currentCategoryId,
click = { target.onCategoryFilterSelected(it.id); dismiss() }
)
}
addListItem(
labelRes = R.string.usage_history_filter_all_categories,
checked = currentCategoryId == null,
click = { target.onAllCategoriesSelected(); dismiss() }
)
}
}
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
interface Listener {
fun onAllCategoriesSelected()
fun onCategoryFilterSelected(categoryId: String)
}
}

View file

@ -20,13 +20,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import io.timelimit.android.R
import io.timelimit.android.databinding.FragmentUsageHistoryBinding
import io.timelimit.android.logic.DefaultAppLogic
class UsageHistoryFragment : Fragment() {
class UsageHistoryFragment : Fragment(), SelectUsageHistoryCategoryDialog.Listener {
companion object {
private const val USER_ID = "userId"
private const val CATEGORY_ID = "categoryId"
@ -39,30 +38,44 @@ class UsageHistoryFragment : Fragment() {
}
}
private val model: UsageHistoryModel by viewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = FragmentUsageHistoryBinding.inflate(inflater, container, false)
val database = DefaultAppLogic.with(context!!).database
val adapter = UsageHistoryAdapter()
val userId = requireArguments().getString(USER_ID)!!
val categoryId = requireArguments().getString(CATEGORY_ID)
adapter.showCategoryTitle = categoryId == null
if (requireArguments().getString(CATEGORY_ID) != null) {
binding.selectCategoryButton.visibility = View.GONE
}
LivePagedListBuilder(
categoryId?.let { database.usedTimes().getUsedTimeListItemsByCategoryId(it) }
?: database.usedTimes().getUsedTimeListItemsByUserId(userId),
10
)
.build()
.observe(viewLifecycleOwner, Observer {
if (!model.didInit) {
model.userId.value = requireArguments().getString(USER_ID)!!
model.categoryId.value = requireArguments().getString(CATEGORY_ID)
model.didInit = true
}
model.categoryId.observe(viewLifecycleOwner) { adapter.showCategoryTitle = it == null }
model.selectedCategoryName.observe(viewLifecycleOwner) { binding.selectCategoryButton.text = it ?: getString(R.string.usage_history_filter_all_categories) }
model.listContent.observe(viewLifecycleOwner) {
binding.isEmpty = it.isEmpty()
adapter.submitList(it)
})
}
binding.recycler.adapter = adapter
binding.recycler.layoutManager = LinearLayoutManager(context)
binding.selectCategoryButton.setOnClickListener {
SelectUsageHistoryCategoryDialog.newInstance(
userId = model.userId.value!!,
currentCategoryId = model.categoryId.value,
target = this
).show(parentFragmentManager)
}
return binding.root
}
override fun onAllCategoriesSelected() { model.categoryId.value = null }
override fun onCategoryFilterSelected(categoryId: String) { model.categoryId.value = categoryId }
}

View file

@ -0,0 +1,59 @@
/*
* 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.category.usagehistory
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.paging.LivePagedListBuilder
import io.timelimit.android.livedata.liveDataFromValue
import io.timelimit.android.livedata.map
import io.timelimit.android.livedata.switchMap
import io.timelimit.android.logic.DefaultAppLogic
class UsageHistoryModel(application: Application): AndroidViewModel(application) {
private val database = DefaultAppLogic.with(application).database
var didInit = false
val userId = MutableLiveData<String>()
val categoryId = MutableLiveData<String?>()
val listContent = userId.switchMap { userId ->
categoryId.switchMap { categoryId ->
val items = if (categoryId == null) {
database.usedTimes().getUsedTimeListItemsByUserId(userId)
} else {
database.usedTimes().getUsedTimeListItemsByCategoryId(categoryId)
}
LivePagedListBuilder(items, 10).build()
}
}
val selectedCategoryName = userId.switchMap { userId ->
categoryId.switchMap { categoryId ->
if (categoryId == null)
liveDataFromValue(null as String?)
else
database.category().getCategoryByChildIdAndId(childId = userId, categoryId = categoryId).map {
if (it == null) this.categoryId.value = null
it?.title
}
}
}
}

View file

@ -27,6 +27,7 @@
type="boolean" />
<import type="android.view.View" />
<import type="android.text.TextUtils" />
</data>
<androidx.core.widget.NestedScrollView
@ -40,6 +41,7 @@
android:layout_height="wrap_content">
<TextView
android:visibility="@{TextUtils.isEmpty(title) ? View.GONE : View.VISIBLE}"
android:textAppearance="?android:textAppearanceLarge"
android:padding="8dp"
android:text="@{title}"

View file

@ -1,5 +1,5 @@
<!--
TimeLimit Copyright <C> 2019 Jonas Lochmann
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.
@ -24,6 +24,19 @@
<import type="android.view.View" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/select_category_button"
tools:text="Alle Kategorien"
android:drawableEnd="@drawable/ic_baseline_expand_more_24"
style="?borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -64,4 +77,5 @@
</RelativeLayout>
</FrameLayout>
</LinearLayout>
</layout>

View file

@ -23,4 +23,5 @@
<string name="usage_history_time_area">von %s bis %s</string>
<string name="usage_history_item_session_duration_limit">Sitzungsdauerbegrenzung von %s mit %s Pause</string>
<string name="usage_history_item_last_usage">Letzte Verwendung: %s</string>
<string name="usage_history_filter_all_categories">Alle Kategorien</string>
</resources>

View file

@ -13,7 +13,7 @@
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>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="usage_history_title">Usage history</string>
<string name="usage_history_empty_title">There is no usage history</string>
<string name="usage_history_empty_text">
@ -24,4 +24,5 @@
<string name="usage_history_time_area">from %s until %s</string>
<string name="usage_history_item_session_duration_limit">Session duration limit of %s with %s break</string>
<string name="usage_history_item_last_usage">Last usage: %s</string>
<string name="usage_history_filter_all_categories" tools:ignore="MissingTranslation">All Categories</string>
</resources>