mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +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 {
|
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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.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 {
|
|
||||||
binding.isEmpty = it.isEmpty()
|
model.categoryId.observe(viewLifecycleOwner) { adapter.showCategoryTitle = it == null }
|
||||||
adapter.submitList(it)
|
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.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 }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" />
|
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}"
|
||||||
|
|
|
@ -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,44 +24,58 @@
|
||||||
<import type="android.view.View" />
|
<import type="android.view.View" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<Button
|
||||||
android:id="@+id/recycler"
|
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_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<RelativeLayout
|
<FrameLayout
|
||||||
android:visibility="@{safeUnbox(isEmpty) ? View.VISIBLE : View.GONE}"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:padding="16dp"
|
android:id="@+id/recycler"
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
<TextView
|
<RelativeLayout
|
||||||
android:textAppearance="?android:textAppearanceLarge"
|
android:visibility="@{safeUnbox(isEmpty) ? View.VISIBLE : View.GONE}"
|
||||||
android:gravity="center_horizontal"
|
android:layout_width="match_parent"
|
||||||
android:text="@string/usage_history_empty_title"
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:padding="16dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
android:textAppearance="?android:textAppearanceLarge"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:text="@string/usage_history_empty_text"
|
android:text="@string/usage_history_empty_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
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>
|
</layout>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue