mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 01:39:22 +02:00
Add category filter for the usage history
This commit is contained in:
parent
08f6884d80
commit
cb19f4e19f
9 changed files with 220 additions and 49 deletions
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
binding.isEmpty = it.isEmpty()
|
||||
adapter.submitList(it)
|
||||
})
|
||||
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 }
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}"
|
||||
|
|
|
@ -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,44 +24,58 @@
|
|||
<import type="android.view.View" />
|
||||
</data>
|
||||
|
||||
<FrameLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
<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="match_parent" />
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<RelativeLayout
|
||||
android:visibility="@{safeUnbox(isEmpty) ? View.VISIBLE : View.GONE}"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:padding="16dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:orientation="vertical"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceLarge"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/usage_history_empty_title"
|
||||
<RelativeLayout
|
||||
android:visibility="@{safeUnbox(isEmpty) ? View.VISIBLE : View.GONE}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:padding="16dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/usage_history_empty_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceLarge"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/usage_history_empty_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/usage_history_empty_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue