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 { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
} }
} }
@ -161,6 +165,7 @@ dependencies {
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation "com.google.android.material:material:1.2.1" 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-fragment-ktx:$nav_version"
implementation "android.arch.navigation:navigation-ui:$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 lateinit var binding: BottomSheetSelectionListBinding
private var didClearList = false private var didClearList = false
abstract val title: String abstract val title: String?
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = BottomSheetSelectionListBinding.inflate(inflater, container, false) 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.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.fragment.app.viewModels
import androidx.paging.LivePagedListBuilder
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.timelimit.android.R
import io.timelimit.android.databinding.FragmentUsageHistoryBinding import io.timelimit.android.databinding.FragmentUsageHistoryBinding
import io.timelimit.android.logic.DefaultAppLogic
class UsageHistoryFragment : Fragment() { class UsageHistoryFragment : Fragment(), SelectUsageHistoryCategoryDialog.Listener {
companion object { companion object {
private const val USER_ID = "userId" private const val USER_ID = "userId"
private const val CATEGORY_ID = "categoryId" 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? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = FragmentUsageHistoryBinding.inflate(inflater, container, false) val binding = FragmentUsageHistoryBinding.inflate(inflater, container, false)
val database = DefaultAppLogic.with(context!!).database
val adapter = UsageHistoryAdapter() 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( if (!model.didInit) {
categoryId?.let { database.usedTimes().getUsedTimeListItemsByCategoryId(it) } model.userId.value = requireArguments().getString(USER_ID)!!
?: database.usedTimes().getUsedTimeListItemsByUserId(userId), model.categoryId.value = requireArguments().getString(CATEGORY_ID)
10
) model.didInit = true
.build() }
.observe(viewLifecycleOwner, Observer {
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() binding.isEmpty = it.isEmpty()
adapter.submitList(it) adapter.submitList(it)
}) }
binding.recycler.adapter = adapter binding.recycler.adapter = adapter
binding.recycler.layoutManager = LinearLayoutManager(context) 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 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" /> type="boolean" />
<import type="android.view.View" /> <import type="android.view.View" />
<import type="android.text.TextUtils" />
</data> </data>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
@ -40,6 +41,7 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<TextView <TextView
android:visibility="@{TextUtils.isEmpty(title) ? View.GONE : View.VISIBLE}"
android:textAppearance="?android:textAppearanceLarge" android:textAppearance="?android:textAppearanceLarge"
android:padding="8dp" android:padding="8dp"
android:text="@{title}" 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 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation version 3 of the License. the Free Software Foundation version 3 of the License.
@ -24,6 +24,19 @@
<import type="android.view.View" /> <import type="android.view.View" />
</data> </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 <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -64,4 +77,5 @@
</RelativeLayout> </RelativeLayout>
</FrameLayout> </FrameLayout>
</LinearLayout>
</layout> </layout>

View file

@ -23,4 +23,5 @@
<string name="usage_history_time_area">von %s bis %s</string> <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_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_item_last_usage">Letzte Verwendung: %s</string>
<string name="usage_history_filter_all_categories">Alle Kategorien</string>
</resources> </resources>

View file

@ -13,7 +13,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. 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_title">Usage history</string>
<string name="usage_history_empty_title">There is no usage history</string> <string name="usage_history_empty_title">There is no usage history</string>
<string name="usage_history_empty_text"> <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_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_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_item_last_usage">Last usage: %s</string>
<string name="usage_history_filter_all_categories" tools:ignore="MissingTranslation">All Categories</string>
</resources> </resources>