Skip to content

Commit 7ab73fb

Browse files
committed
Make widget refreshing configurable
1 parent 9813f9b commit 7ab73fb

File tree

8 files changed

+216
-29
lines changed

8 files changed

+216
-29
lines changed

app/src/main/kotlin/com/w2sv/wifiwidget/activities/HomeActivity.kt

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
2222
import androidx.core.splashscreen.SplashScreenViewProvider
2323
import androidx.lifecycle.LifecycleObserver
2424
import androidx.lifecycle.SavedStateHandle
25+
import androidx.lifecycle.lifecycleScope
2526
import androidx.lifecycle.viewModelScope
2627
import androidx.localbroadcastmanager.content.LocalBroadcastManager
2728
import com.w2sv.androidutils.SelfManagingLocalBroadcastReceiver
@@ -31,10 +32,12 @@ import com.w2sv.androidutils.extensions.locationServicesEnabled
3132
import com.w2sv.androidutils.extensions.showToast
3233
import com.w2sv.common.Theme
3334
import com.w2sv.kotlinutils.extensions.getByOrdinal
35+
import com.w2sv.preferences.EnumOrdinals
3436
import com.w2sv.preferences.FloatPreferences
3537
import com.w2sv.preferences.GlobalFlags
36-
import com.w2sv.preferences.EnumOrdinals
3738
import com.w2sv.preferences.WidgetProperties
39+
import com.w2sv.preferences.WidgetRefreshingParameters
40+
import com.w2sv.widget.WidgetDataRefreshWorker
3841
import com.w2sv.widget.WifiWidgetProvider
3942
import com.w2sv.wifiwidget.R
4043
import com.w2sv.wifiwidget.ui.home.HomeScreen
@@ -47,6 +50,7 @@ import dagger.hilt.android.AndroidEntryPoint
4750
import dagger.hilt.android.lifecycle.HiltViewModel
4851
import dagger.hilt.android.qualifiers.ApplicationContext
4952
import kotlinx.coroutines.flow.MutableStateFlow
53+
import kotlinx.coroutines.launch
5054
import slimber.log.i
5155
import javax.inject.Inject
5256

@@ -59,6 +63,7 @@ class HomeActivity : LifecycleObserversRegisteringActivity() {
5963
private val globalFlags: GlobalFlags,
6064
private val enumOrdinals: EnumOrdinals,
6165
private val floatPreferences: FloatPreferences,
66+
private val widgetRefreshingParameters: WidgetRefreshingParameters,
6267
savedStateHandle: SavedStateHandle,
6368
@ApplicationContext context: Context
6469
) : androidx.lifecycle.ViewModel() {
@@ -68,7 +73,8 @@ class HomeActivity : LifecycleObserversRegisteringActivity() {
6873
widgetProperties,
6974
globalFlags,
7075
enumOrdinals,
71-
floatPreferences
76+
floatPreferences,
77+
widgetRefreshingParameters
7278
)
7379

7480
/**
@@ -146,10 +152,21 @@ class HomeActivity : LifecycleObserversRegisteringActivity() {
146152
{ floatPreferences.opacity = it }
147153
)
148154

155+
val widgetRefreshingParametersState = NonAppliedSnapshotStateMap(
156+
{ widgetRefreshingParameters },
157+
{
158+
widgetRefreshingParameters.putAll(it)
159+
widgetRefreshingParametersChanged.value = true
160+
}
161+
)
162+
163+
val widgetRefreshingParametersChanged = MutableStateFlow(false)
164+
149165
val widgetConfigurationStates = CoherentNonAppliedStates(
150166
widgetPropertyStateMap,
151167
widgetThemeState,
152168
widgetOpacityState,
169+
widgetRefreshingParametersState,
153170
coroutineScope = viewModelScope
154171
)
155172

@@ -230,6 +247,18 @@ class HomeActivity : LifecycleObserversRegisteringActivity() {
230247

231248
super.onCreate(savedInstanceState)
232249

250+
lifecycleScope.launch {
251+
with(viewModel.widgetRefreshingParametersChanged) {
252+
collect {
253+
if (it) {
254+
WidgetDataRefreshWorker.Administrator.getInstance(applicationContext)
255+
.applyChangedParameters()
256+
}
257+
value = false
258+
}
259+
}
260+
}
261+
233262
setContent {
234263
val theme by viewModel.appliedInAppTheme.collectAsState()
235264

app/src/main/kotlin/com/w2sv/wifiwidget/ui/home/configurationdialog/ContentColumn.kt

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.compose.foundation.verticalScroll
1313
import androidx.compose.material3.Icon
1414
import androidx.compose.material3.MaterialTheme
1515
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.rememberCoroutineScope
1617
import androidx.compose.ui.Alignment
1718
import androidx.compose.ui.Modifier
1819
import androidx.compose.ui.res.painterResource
@@ -26,6 +27,7 @@ import com.w2sv.wifiwidget.R
2627
import com.w2sv.wifiwidget.ui.shared.JostText
2728
import com.w2sv.wifiwidget.ui.shared.ThemeSelectionRow
2829
import com.w2sv.wifiwidget.ui.shared.WifiWidgetTheme
30+
import kotlinx.coroutines.launch
2931

3032
@Preview
3133
@Composable
@@ -54,12 +56,18 @@ internal fun ContentColumn(
5456
onCheckedChange: (String, Boolean) -> Unit,
5557
onInfoButtonClick: (Int) -> Unit
5658
) {
59+
val scrollState = rememberScrollState()
60+
val scope = rememberCoroutineScope()
61+
5762
Column(
5863
horizontalAlignment = Alignment.CenterHorizontally,
5964
modifier = modifier
6065
.padding(vertical = 16.dp)
61-
.verticalScroll(rememberScrollState())
66+
.verticalScroll(scrollState)
6267
) {
68+
val checkablePropertiesColumnModifier = Modifier.padding(horizontal = 26.dp)
69+
val defaultSectionHeaderModifier = Modifier.padding(vertical = 22.dp)
70+
6371
SectionHeader(
6472
R.string.theme,
6573
R.drawable.ic_nightlight_24,
@@ -74,20 +82,34 @@ internal fun ContentColumn(
7482
SectionHeader(
7583
R.string.opacity,
7684
R.drawable.ic_opacity_24,
77-
Modifier.padding(vertical = 22.dp)
85+
defaultSectionHeaderModifier
7886
)
7987
OpacitySliderWithValue(opacity = opacity, onOpacityChanged = onOpacityChanged)
8088

8189
SectionHeader(
8290
R.string.properties,
8391
R.drawable.ic_checklist_24,
84-
Modifier.padding(vertical = 22.dp)
92+
defaultSectionHeaderModifier
8593
)
8694
PropertyColumn(
95+
modifier = checkablePropertiesColumnModifier,
8796
propertyChecked = propertyChecked,
8897
onCheckedChange = onCheckedChange,
8998
onInfoButtonClick = onInfoButtonClick
9099
)
100+
101+
SectionHeader(
102+
titleRes = R.string.refreshing,
103+
iconRes = com.w2sv.widget.R.drawable.ic_refresh_24,
104+
modifier = defaultSectionHeaderModifier
105+
)
106+
RefreshingSection(checkablePropertiesColumnModifier) {
107+
scope.launch {
108+
with(scrollState) {
109+
animateScrollTo(maxValue)
110+
}
111+
}
112+
}
91113
}
92114
}
93115

app/src/main/kotlin/com/w2sv/wifiwidget/ui/home/configurationdialog/PropertyColumn.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.w2sv.wifiwidget.ui.home.configurationdialog
22

33
import androidx.compose.foundation.layout.Column
44
import androidx.compose.foundation.layout.Row
5-
import androidx.compose.foundation.layout.padding
65
import androidx.compose.foundation.layout.size
76
import androidx.compose.material.icons.Icons
87
import androidx.compose.material.icons.outlined.Info
@@ -16,20 +15,18 @@ import androidx.compose.ui.Alignment
1615
import androidx.compose.ui.Modifier
1716
import androidx.compose.ui.res.dimensionResource
1817
import androidx.compose.ui.res.stringArrayResource
19-
import androidx.compose.ui.unit.dp
2018
import androidx.compose.ui.unit.sp
2119
import com.w2sv.wifiwidget.R
2220
import com.w2sv.wifiwidget.ui.shared.JostText
2321

2422
@Composable
2523
internal fun PropertyColumn(
24+
modifier: Modifier = Modifier,
2625
propertyChecked: (String) -> Boolean,
2726
onCheckedChange: (String, Boolean) -> Unit,
2827
onInfoButtonClick: (Int) -> Unit
2928
) {
30-
Column(
31-
modifier = Modifier.padding(horizontal = 26.dp)
32-
) {
29+
Column(modifier = modifier) {
3330
stringArrayResource(id = R.array.wifi_properties)
3431
.forEachIndexed { propertyIndex, property ->
3532
PropertyRow(
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.w2sv.wifiwidget.ui.home.configurationdialog
2+
3+
import androidx.annotation.StringRes
4+
import androidx.compose.animation.AnimatedVisibility
5+
import androidx.compose.animation.expandVertically
6+
import androidx.compose.animation.fadeIn
7+
import androidx.compose.foundation.layout.Arrangement
8+
import androidx.compose.foundation.layout.Column
9+
import androidx.compose.foundation.layout.Row
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.material3.Checkbox
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.ui.Alignment
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.res.stringResource
17+
import androidx.compose.ui.unit.TextUnit
18+
import androidx.compose.ui.unit.dp
19+
import androidx.compose.ui.unit.sp
20+
import com.w2sv.wifiwidget.R
21+
import com.w2sv.wifiwidget.activities.HomeActivity
22+
import com.w2sv.wifiwidget.ui.shared.JostText
23+
24+
@Composable
25+
internal fun RefreshingSection(
26+
modifier: Modifier = Modifier,
27+
viewModel: HomeActivity.ViewModel = androidx.lifecycle.viewmodel.compose.viewModel(),
28+
scrollToContentColumnBottom: () -> Unit
29+
) {
30+
Column(modifier = modifier, horizontalAlignment = Alignment.Start) {
31+
RefreshingParameterRow(
32+
label = R.string.refresh_periodically,
33+
parameterName = "refreshPeriodically",
34+
viewModel = viewModel
35+
)
36+
AnimatedVisibility(
37+
visible = viewModel.widgetRefreshingParametersState.getValue("refreshPeriodically"),
38+
enter = fadeIn() + expandVertically(initialHeight = { scrollToContentColumnBottom(); 0 })
39+
) {
40+
RefreshingParameterRow(
41+
label = R.string.refresh_on_low_battery,
42+
parameterName = "refreshOnBatteryLow",
43+
viewModel = viewModel,
44+
modifier = Modifier.padding(start = 12.dp),
45+
fontSize = 14.sp
46+
)
47+
}
48+
}
49+
}
50+
51+
@Composable
52+
private fun RefreshingParameterRow(
53+
@StringRes label: Int,
54+
parameterName: String,
55+
viewModel: HomeActivity.ViewModel,
56+
modifier: Modifier = Modifier,
57+
fontSize: TextUnit = TextUnit.Unspecified
58+
) {
59+
Row(
60+
verticalAlignment = Alignment.CenterVertically,
61+
horizontalArrangement = Arrangement.SpaceBetween,
62+
modifier = Modifier
63+
.fillMaxWidth()
64+
.then(modifier)
65+
) {
66+
JostText(text = stringResource(id = label), fontSize = fontSize)
67+
Checkbox(
68+
checked = viewModel.widgetRefreshingParametersState.map.getValue(parameterName),
69+
onCheckedChange = {
70+
viewModel.widgetRefreshingParametersState[parameterName] = it
71+
}
72+
)
73+
}
74+
}

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@
2121
<string name="theme">Theme</string>
2222
<string name="properties">Properties</string>
2323
<string name="opacity">Opacity</string>
24+
<string name="refreshing">Refreshing</string>
25+
<string name="refresh_periodically">Refresh periodically</string>
26+
<string name="refresh_on_low_battery">Refresh on low battery</string>
2427
</resources>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.w2sv.preferences
2+
3+
import android.content.SharedPreferences
4+
import com.w2sv.typedpreferences.BooleanPreferences
5+
import javax.inject.Inject
6+
import javax.inject.Singleton
7+
8+
@Singleton
9+
class WidgetRefreshingParameters @Inject constructor(sharedPreferences: SharedPreferences): BooleanPreferences(
10+
"refreshPeriodically" to true,
11+
"refreshOnBatteryLow" to false,
12+
sharedPreferences = sharedPreferences
13+
){
14+
val refreshPeriodically by this
15+
val refreshOnBatteryLow by this
16+
}

widget/src/main/kotlin/com/w2sv/widget/WidgetDataRefreshWorker.kt

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,30 @@ import androidx.work.PeriodicWorkRequestBuilder
88
import androidx.work.WorkManager
99
import androidx.work.Worker
1010
import androidx.work.WorkerParameters
11+
import com.w2sv.preferences.WidgetRefreshingParameters
12+
import dagger.hilt.EntryPoint
13+
import dagger.hilt.InstallIn
14+
import dagger.hilt.android.EntryPointAccessors
15+
import dagger.hilt.android.qualifiers.ApplicationContext
16+
import dagger.hilt.components.SingletonComponent
1117
import slimber.log.i
1218
import java.time.Duration
19+
import javax.inject.Inject
1320

14-
internal class WidgetDataRefreshWorker(context: Context, workerParams: WorkerParameters) :
21+
class WidgetDataRefreshWorker(context: Context, workerParams: WorkerParameters) :
1522
Worker(context, workerParams) {
1623

1724
companion object {
18-
const val UNIQUE_WORK_NAME = "WidgetDataRefreshWorker"
25+
private const val UNIQUE_WORK_NAME = "WidgetDataRefreshWorker"
1926

20-
fun enqueueAsUniquePeriodicWork(
27+
private fun enqueueAsUniquePeriodicWork(
2128
workManager: WorkManager,
2229
refreshPeriod: Duration,
2330
requiresBatteryNotLow: Boolean
2431
) {
2532
workManager.enqueueUniquePeriodicWork(
2633
UNIQUE_WORK_NAME,
27-
ExistingPeriodicWorkPolicy.KEEP,
34+
ExistingPeriodicWorkPolicy.UPDATE,
2835
PeriodicWorkRequestBuilder<WidgetDataRefreshWorker>(refreshPeriod)
2936
.setConstraints(
3037
Constraints.Builder()
@@ -34,6 +41,56 @@ internal class WidgetDataRefreshWorker(context: Context, workerParams: WorkerPar
3441
.setInitialDelay(refreshPeriod)
3542
.build()
3643
)
44+
i { "Enqueued $UNIQUE_WORK_NAME" }
45+
}
46+
}
47+
48+
class Administrator @Inject constructor(
49+
@ApplicationContext private val context: Context,
50+
private val widgetRefreshingParameters: WidgetRefreshingParameters
51+
) {
52+
53+
@InstallIn(SingletonComponent::class)
54+
@EntryPoint
55+
interface EntryPointInterface {
56+
fun getAdministratorInstance(): Administrator
57+
}
58+
59+
companion object {
60+
fun getInstance(context: Context): Administrator =
61+
EntryPointAccessors.fromApplication(
62+
context,
63+
EntryPointInterface::class.java
64+
)
65+
.getAdministratorInstance()
66+
}
67+
68+
fun applyChangedParameters(){
69+
when(widgetRefreshingParameters.refreshPeriodically){
70+
true -> enableWorker()
71+
false -> cancelWorker()
72+
}
73+
}
74+
75+
fun enableWorkerIfApplicable() {
76+
if (widgetRefreshingParameters.refreshPeriodically) {
77+
enableWorker()
78+
}
79+
}
80+
81+
private fun enableWorker(){
82+
enqueueAsUniquePeriodicWork(
83+
WorkManager.getInstance(context),
84+
Duration.ofMinutes(15L),
85+
!widgetRefreshingParameters.refreshOnBatteryLow
86+
)
87+
}
88+
89+
fun cancelWorker() {
90+
WorkManager.getInstance(context)
91+
.cancelUniqueWork(UNIQUE_WORK_NAME)
92+
93+
i { "Cancelled $UNIQUE_WORK_NAME" }
3794
}
3895
}
3996

0 commit comments

Comments
 (0)