Skip to content

Commit a6634cd

Browse files
committed
widget grid settings activity
1 parent a221ad9 commit a6634cd

13 files changed

Lines changed: 359 additions & 5 deletions

File tree

data_local/src/main/java/com/example/util/simpletimetracker/data_local/prefs/PrefsRepoImpl.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,10 +614,25 @@ class PrefsRepoImpl @Inject constructor(
614614
return prefs.getInt(key, 0)
615615
}
616616

617+
override fun setGridWidgetFilteredTypes(widgetId: Int, typeIds: Set<Long>) {
618+
val key = KEY_GRID_WIDGET_FILTERED_TYPES + widgetId
619+
logPrefsDataAccess("setGridWidgetFilteredTypes $key")
620+
prefs.edit { putStringSet(key, typeIds.map(Long::toString).toSet()) }
621+
}
622+
623+
override fun getGridWidgetFilteredTypes(widgetId: Int): Set<Long> {
624+
val key = KEY_GRID_WIDGET_FILTERED_TYPES + widgetId
625+
logPrefsDataAccess("getGridWidgetFilteredTypes $key")
626+
return prefs.getStringSet(key, emptySet())
627+
?.mapNotNull { it.toLongOrNull() }.orEmpty().toSet()
628+
}
629+
617630
override fun removeGridWidget(widgetId: Int) {
618-
val key = KEY_GRID_WIDGET_PAGE + widgetId
619-
logPrefsDataAccess("removeGridWidget $key")
620-
prefs.edit { remove(key) }
631+
logPrefsDataAccess("removeGridWidget $widgetId")
632+
prefs.edit {
633+
remove(KEY_GRID_WIDGET_PAGE + widgetId)
634+
remove(KEY_GRID_WIDGET_FILTERED_TYPES + widgetId)
635+
}
621636
}
622637

623638
override fun clear() {
@@ -779,6 +794,7 @@ class PrefsRepoImpl @Inject constructor(
779794
private const val KEY_STATISTICS_WIDGET_FILTERING_TYPE = "statistics_widget_filtering_type_"
780795
private const val KEY_QUICK_SETTINGS_WIDGET_TYPE = "quick_settings_widget_type_"
781796
private const val KEY_GRID_WIDGET_PAGE = "grid_widget_page_"
797+
private const val KEY_GRID_WIDGET_FILTERED_TYPES = "grid_widget_filtered_types_"
782798

783799
// Removed
784800
private const val KEY_SORT_RECORD_TYPES_BY_COLOR = "sortRecordTypesByColor" // Boolean

domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/interactor/PrefsInteractor.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,14 @@ class PrefsInteractor @Inject constructor(
834834
prefsRepo.getGridWidget(widgetId)
835835
}
836836

837+
suspend fun setGridWidgetFilteredTypes(widgetId: Int, typeIds: Set<Long>) = withContext(Dispatchers.IO) {
838+
prefsRepo.setGridWidgetFilteredTypes(widgetId, typeIds)
839+
}
840+
841+
suspend fun getGridWidgetFilteredTypes(widgetId: Int): Set<Long> = withContext(Dispatchers.IO) {
842+
prefsRepo.getGridWidgetFilteredTypes(widgetId)
843+
}
844+
837845
suspend fun removeGridWidget(widgetId: Int) = withContext(Dispatchers.IO) {
838846
prefsRepo.removeGridWidget(widgetId)
839847
}

domain/src/main/java/com/example/util/simpletimetracker/domain/prefs/repo/PrefsRepo.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ interface PrefsRepo {
226226

227227
fun getGridWidget(widgetId: Int): Int
228228

229+
fun setGridWidgetFilteredTypes(widgetId: Int, typeIds: Set<Long>)
230+
231+
fun getGridWidgetFilteredTypes(widgetId: Int): Set<Long>
232+
229233
fun removeGridWidget(widgetId: Int)
230234

231235
fun clear()

features/feature_widget/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
</intent-filter>
1919
</activity>
2020

21+
<activity
22+
android:name=".grid.settings.WidgetGridSettingsActivity"
23+
android:exported="true">
24+
<intent-filter>
25+
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
26+
</intent-filter>
27+
</activity>
28+
2129
<activity
2230
android:name=".quickSettings.settings.WidgetQuickSettingsConfigureActivity"
2331
android:exported="true">

features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/grid/WidgetGridProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class WidgetGridProvider : AppWidgetProvider() {
5151

5252
override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
5353
allowDiskRead { MainScope() }.launch {
54-
appWidgetIds?.forEach { prefsInteractor.removeWidget(it) }
54+
appWidgetIds?.forEach { prefsInteractor.removeGridWidget(it) }
5555
}
5656
}
5757

features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/grid/WidgetGridRemoveViewsFactory.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ class WidgetGridRemoveViewsFactory @Inject constructor(
6969
appWidgetManager: AppWidgetManager,
7070
appWidgetId: Int,
7171
): RemoteViews {
72-
val recordTypes = recordTypeInteractor.getAll().filter { !it.hidden }
72+
val filteredTypeIds = prefsInteractor.getGridWidgetFilteredTypes(appWidgetId)
73+
val recordTypes = recordTypeInteractor.getAll()
74+
.filter { !it.hidden && it.id !in filteredTypeIds }
7375
val runningRecords = runningRecordInteractor.getAll()
7476
val isDarkTheme = prefsInteractor.getDarkMode()
7577
val backgroundTransparency = prefsInteractor.getWidgetBackgroundTransparencyPercent()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.example.util.simpletimetracker.feature_widget.grid.settings
2+
3+
import android.app.Activity
4+
import android.appwidget.AppWidgetManager
5+
import android.content.Intent
6+
import android.os.Bundle
7+
import android.view.LayoutInflater
8+
import androidx.fragment.app.commit
9+
import com.example.util.simpletimetracker.core.base.BaseActivity
10+
import com.example.util.simpletimetracker.core.manager.ThemeManager
11+
import com.example.util.simpletimetracker.core.provider.ContextProvider
12+
import com.example.util.simpletimetracker.feature_widget.R
13+
import dagger.hilt.android.AndroidEntryPoint
14+
import javax.inject.Inject
15+
import com.example.util.simpletimetracker.feature_widget.databinding.WidgetGridSettingsActivityBinding as Binding
16+
17+
@AndroidEntryPoint
18+
class WidgetGridSettingsActivity : BaseActivity<Binding>() {
19+
20+
override val inflater: (LayoutInflater) -> Binding = Binding::inflate
21+
22+
@Inject
23+
override lateinit var themeManager: ThemeManager
24+
25+
@Inject
26+
override lateinit var contextProvider: ContextProvider
27+
28+
override fun onCreate(savedInstanceState: Bundle?) {
29+
super.onCreate(savedInstanceState)
30+
// Set the result to CANCELED. This will cause the widget host to cancel
31+
// out of the widget placement if they press the back button.
32+
setResult(RESULT_CANCELED)
33+
34+
if (savedInstanceState == null) {
35+
val fragment = WidgetGridSettingsFragment()
36+
supportFragmentManager.commit {
37+
replace(R.id.containerWidgetGridSettings, fragment)
38+
}
39+
}
40+
}
41+
42+
fun exit(widgetId: Int) {
43+
val resultValue = Intent().apply {
44+
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
45+
}
46+
setResult(Activity.RESULT_OK, resultValue)
47+
finish()
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.example.util.simpletimetracker.feature_widget.grid.settings
2+
3+
data class WidgetGridSettingsExtra(
4+
val widgetId: Int,
5+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.example.util.simpletimetracker.feature_widget.grid.settings
2+
3+
import android.appwidget.AppWidgetManager
4+
import android.view.LayoutInflater
5+
import android.view.ViewGroup
6+
import androidx.fragment.app.viewModels
7+
import com.example.util.simpletimetracker.core.base.BaseFragment
8+
import com.example.util.simpletimetracker.core.utils.InsetConfiguration
9+
import com.example.util.simpletimetracker.feature_base_adapter.BaseRecyclerAdapter
10+
import com.example.util.simpletimetracker.feature_base_adapter.empty.createEmptyAdapterDelegate
11+
import com.example.util.simpletimetracker.feature_base_adapter.loader.createLoaderAdapterDelegate
12+
import com.example.util.simpletimetracker.feature_base_adapter.recordType.createRecordTypeAdapterDelegate
13+
import com.example.util.simpletimetracker.feature_views.extension.setOnClick
14+
import com.google.android.flexbox.FlexDirection
15+
import com.google.android.flexbox.FlexWrap
16+
import com.google.android.flexbox.FlexboxLayoutManager
17+
import com.google.android.flexbox.JustifyContent
18+
import dagger.hilt.android.AndroidEntryPoint
19+
import com.example.util.simpletimetracker.feature_widget.databinding.WidgetGridSettingsFragmentBinding as Binding
20+
21+
@AndroidEntryPoint
22+
class WidgetGridSettingsFragment : BaseFragment<Binding>() {
23+
24+
override val inflater: (LayoutInflater, ViewGroup?, Boolean) -> Binding =
25+
Binding::inflate
26+
27+
override var insetConfiguration: InsetConfiguration =
28+
InsetConfiguration.ApplyToView { binding.root }
29+
30+
private val viewModel: WidgetGridSettingsViewModel by viewModels()
31+
32+
private val recordTypesAdapter: BaseRecyclerAdapter by lazy {
33+
BaseRecyclerAdapter(
34+
createRecordTypeAdapterDelegate(viewModel::onRecordTypeClick),
35+
createLoaderAdapterDelegate(),
36+
createEmptyAdapterDelegate(),
37+
)
38+
}
39+
40+
override fun initUi() {
41+
binding.rvWidgetGridSettingsTypes.apply {
42+
layoutManager = FlexboxLayoutManager(requireContext()).apply {
43+
flexDirection = FlexDirection.ROW
44+
justifyContent = JustifyContent.CENTER
45+
flexWrap = FlexWrap.WRAP
46+
}
47+
adapter = recordTypesAdapter
48+
}
49+
}
50+
51+
override fun initUx() = with(binding) {
52+
btnWidgetGridSettingsShowAll.setOnClick(viewModel::onShowAllClick)
53+
btnWidgetGridSettingsHideAll.setOnClick(viewModel::onHideAllClick)
54+
btnWidgetGridSettingsSave.setOnClick(throttle(viewModel::onSaveClick))
55+
}
56+
57+
override fun initViewModel() = with(viewModel) {
58+
extra = WidgetGridSettingsExtra(getWidgetId())
59+
types.observe(recordTypesAdapter::replace)
60+
handled.observe(::exit)
61+
}
62+
63+
private fun getWidgetId(): Int {
64+
return activity?.intent?.extras
65+
?.getInt(
66+
AppWidgetManager.EXTRA_APPWIDGET_ID,
67+
AppWidgetManager.INVALID_APPWIDGET_ID,
68+
)
69+
?: AppWidgetManager.INVALID_APPWIDGET_ID
70+
}
71+
72+
private fun exit(widgetId: Int) {
73+
(activity as? WidgetGridSettingsActivity)?.exit(widgetId)
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package com.example.util.simpletimetracker.feature_widget.grid.settings
2+
3+
import androidx.lifecycle.LiveData
4+
import androidx.lifecycle.MutableLiveData
5+
import androidx.lifecycle.ViewModel
6+
import androidx.lifecycle.viewModelScope
7+
import com.example.util.simpletimetracker.core.extension.set
8+
import com.example.util.simpletimetracker.core.mapper.RecordTypeViewDataMapper
9+
import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor
10+
import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTypeInteractor
11+
import com.example.util.simpletimetracker.domain.recordType.model.RecordType
12+
import com.example.util.simpletimetracker.domain.widget.interactor.WidgetInteractor
13+
import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType
14+
import com.example.util.simpletimetracker.feature_base_adapter.loader.LoaderViewData
15+
import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData
16+
import com.example.util.simpletimetracker.feature_views.GoalCheckmarkView
17+
import dagger.hilt.android.lifecycle.HiltViewModel
18+
import kotlinx.coroutines.launch
19+
import javax.inject.Inject
20+
21+
@HiltViewModel
22+
class WidgetGridSettingsViewModel @Inject constructor(
23+
private val recordTypeInteractor: RecordTypeInteractor,
24+
private val prefsInteractor: PrefsInteractor,
25+
private val widgetInteractor: WidgetInteractor,
26+
private val recordTypeViewDataMapper: RecordTypeViewDataMapper,
27+
) : ViewModel() {
28+
29+
lateinit var extra: WidgetGridSettingsExtra
30+
31+
val types: LiveData<List<ViewHolderType>> by lazy {
32+
return@lazy MutableLiveData<List<ViewHolderType>>().let { initial ->
33+
viewModelScope.launch {
34+
initializeWidgetData()
35+
initial.value = listOf(LoaderViewData())
36+
initial.value = loadTypesViewData()
37+
}
38+
initial
39+
}
40+
}
41+
val handled: LiveData<Int> = MutableLiveData()
42+
43+
private var recordTypesCache: List<RecordType>? = null
44+
private var filteredTypeIds: Set<Long> = emptySet()
45+
private var initialized: Boolean = false
46+
47+
fun onRecordTypeClick(item: RecordTypeViewData) = viewModelScope.launch {
48+
filteredTypeIds = filteredTypeIds.toMutableSet().apply {
49+
if (item.id in this) remove(item.id) else add(item.id)
50+
}
51+
updateTypesViewData()
52+
}
53+
54+
fun onShowAllClick() = viewModelScope.launch {
55+
filteredTypeIds = emptySet()
56+
updateTypesViewData()
57+
}
58+
59+
fun onHideAllClick() = viewModelScope.launch {
60+
filteredTypeIds = getTypesCache().map(RecordType::id).toSet()
61+
updateTypesViewData()
62+
}
63+
64+
fun onSaveClick() {
65+
viewModelScope.launch {
66+
prefsInteractor.setGridWidgetFilteredTypes(extra.widgetId, filteredTypeIds)
67+
widgetInteractor.updateGridWidget(extra.widgetId)
68+
(handled as MutableLiveData).value = extra.widgetId
69+
}
70+
}
71+
72+
private suspend fun initializeWidgetData() {
73+
if (initialized) return
74+
filteredTypeIds = prefsInteractor.getGridWidgetFilteredTypes(extra.widgetId)
75+
initialized = true
76+
}
77+
78+
private fun updateTypesViewData() = viewModelScope.launch {
79+
types.set(loadTypesViewData())
80+
}
81+
82+
private suspend fun loadTypesViewData(): List<ViewHolderType> {
83+
val isDarkTheme = prefsInteractor.getDarkMode()
84+
val numberOfCards = prefsInteractor.getNumberOfCards()
85+
86+
return getTypesCache()
87+
.map { type ->
88+
recordTypeViewDataMapper.mapFiltered(
89+
recordType = type,
90+
numberOfCards = numberOfCards,
91+
isDarkTheme = isDarkTheme,
92+
isFiltered = type.id in filteredTypeIds,
93+
checkState = GoalCheckmarkView.CheckState.HIDDEN,
94+
isComplete = false,
95+
)
96+
}
97+
.takeUnless { it.isEmpty() }
98+
?: recordTypeViewDataMapper.mapToEmpty()
99+
}
100+
101+
private suspend fun getTypesCache(): List<RecordType> {
102+
return recordTypesCache ?: run {
103+
recordTypeInteractor.getAll()
104+
.filter { !it.hidden }
105+
.also { recordTypesCache = it }
106+
}
107+
}
108+
}

0 commit comments

Comments
 (0)