Weather widget: add warning if selected providerr is unavailable
This commit is contained in:
parent
e96a382b00
commit
845a11b54b
@ -52,7 +52,7 @@
|
||||
android:name=".settings.SettingsActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/settings"
|
||||
android:launchMode="singleTask"
|
||||
android:launchMode="singleTop"
|
||||
android:parentActivityName=".launcher.SharedLauncherActivity"
|
||||
android:taskAffinity="de.mm20.launcher2.settings"
|
||||
android:theme="@style/SettingsTheme.NoActionBar"
|
||||
|
||||
@ -23,7 +23,15 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.*
|
||||
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
|
||||
import androidx.compose.material.icons.rounded.Air
|
||||
import androidx.compose.material.icons.rounded.ErrorOutline
|
||||
import androidx.compose.material.icons.rounded.LightMode
|
||||
import androidx.compose.material.icons.rounded.LocationCity
|
||||
import androidx.compose.material.icons.rounded.MyLocation
|
||||
import androidx.compose.material.icons.rounded.North
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -42,21 +50,23 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.icons.HumidityPercentage
|
||||
import de.mm20.launcher2.icons.Rain
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.common.WeatherLocationSearchDialog
|
||||
import de.mm20.launcher2.ui.component.Banner
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.weather.AnimatedWeatherIcon
|
||||
import de.mm20.launcher2.ui.component.weather.WeatherIcon
|
||||
import de.mm20.launcher2.icons.HumidityPercentage
|
||||
import de.mm20.launcher2.icons.Rain
|
||||
import de.mm20.launcher2.ui.ktx.blendIntoViewScale
|
||||
import de.mm20.launcher2.ui.locals.LocalCardStyle
|
||||
import de.mm20.launcher2.ui.modifier.consumeAllScrolling
|
||||
@ -86,16 +96,42 @@ fun WeatherWidget(widget: WeatherWidget) {
|
||||
val imperialUnits by viewModel.imperialUnits.collectAsState(false)
|
||||
val compactMode = !widget.config.showForecast
|
||||
|
||||
val isProviderAvailable by viewModel.isProviderAvailable.collectAsStateWithLifecycle(true)
|
||||
|
||||
var showLocationDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showLocationDialog) {
|
||||
WeatherLocationSearchDialog(onDismissRequest = { showLocationDialog = false })
|
||||
}
|
||||
|
||||
val forecast = selectedForecast ?: run {
|
||||
val hasPermission by viewModel.hasLocationPermission.collectAsState()
|
||||
val autoLocation by viewModel.autoLocation.collectAsState()
|
||||
Column {
|
||||
|
||||
Column {
|
||||
if (!isProviderAvailable) {
|
||||
Banner(
|
||||
modifier = Modifier.fillMaxWidth().padding(16.dp),
|
||||
text = stringResource(R.string.weather_widget_no_provider),
|
||||
icon = Icons.Rounded.ErrorOutline,
|
||||
primaryAction = {
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.openSettings(context)
|
||||
},
|
||||
contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
|
||||
) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Rounded.OpenInNew,
|
||||
null,
|
||||
modifier = Modifier.padding(end = ButtonDefaults.IconSpacing).size(ButtonDefaults.IconSize)
|
||||
)
|
||||
Text(stringResource(R.string.settings))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val forecast = selectedForecast ?: run {
|
||||
val hasPermission by viewModel.hasLocationPermission.collectAsState()
|
||||
val autoLocation by viewModel.autoLocation.collectAsState()
|
||||
AnimatedVisibility(hasPermission == false && autoLocation == true) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier
|
||||
@ -117,11 +153,10 @@ fun WeatherWidget(widget: WeatherWidget) {
|
||||
)
|
||||
}
|
||||
NoData()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
CurrentWeather(forecast, imperialUnits)
|
||||
|
||||
if (!compactMode) {
|
||||
@ -339,7 +374,9 @@ fun WeatherTimeSelector(
|
||||
val listState = rememberLazyListState()
|
||||
LazyRow(
|
||||
state = listState,
|
||||
modifier = modifier.fillMaxWidth().consumeAllScrolling(),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.consumeAllScrolling(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
contentPadding = PaddingValues(start = 16.dp, end = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@ -398,7 +435,9 @@ fun WeatherDaySelector(
|
||||
val listState = rememberLazyListState()
|
||||
LazyRow(
|
||||
state = listState,
|
||||
modifier = modifier.fillMaxWidth().consumeAllScrolling(),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.consumeAllScrolling(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(start = 16.dp, end = 16.dp),
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.weather
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.*
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.weather.WeatherSettings
|
||||
import de.mm20.launcher2.ui.settings.SettingsActivity
|
||||
import de.mm20.launcher2.weather.DailyForecast
|
||||
import de.mm20.launcher2.weather.Forecast
|
||||
import de.mm20.launcher2.weather.WeatherRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
@ -132,4 +136,16 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
|
||||
selectDay(dayIndex)
|
||||
selectForecast(forecastIndex)
|
||||
}
|
||||
|
||||
fun openSettings(context: Context) {
|
||||
context.startActivity(
|
||||
Intent(context, SettingsActivity::class.java).apply {
|
||||
putExtra(SettingsActivity.EXTRA_ROUTE, SettingsActivity.ROUTE_WEATHER_INTEGRATION)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val isProviderAvailable: Flow<Boolean> = weatherRepository.getActiveProvider().map {
|
||||
it != null
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package de.mm20.launcher2.ui.settings
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
@ -14,7 +15,9 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.navigation.compose.NavHost
|
||||
@ -72,16 +75,22 @@ import java.util.UUID
|
||||
|
||||
class SettingsActivity : BaseActivity() {
|
||||
|
||||
private var route by mutableStateOf<String?>(null)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
val newRoute = intent?.getStringExtra(EXTRA_ROUTE)
|
||||
route = newRoute
|
||||
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
|
||||
LaunchedEffect(intent) {
|
||||
intent.getStringExtra(EXTRA_ROUTE)
|
||||
?.let { navController.navigate(it) }
|
||||
LaunchedEffect(route) {
|
||||
navController.navigate(route ?: "settings") {
|
||||
popUpTo("settings")
|
||||
}
|
||||
}
|
||||
val wallpaperColors by wallpaperColorsAsState()
|
||||
CompositionLocalProvider(
|
||||
@ -107,7 +116,7 @@ class SettingsActivity : BaseActivity() {
|
||||
navController = navController,
|
||||
startDestination = "settings",
|
||||
exitTransition = {
|
||||
slideOutHorizontally {-it / 4 }
|
||||
slideOutHorizontally { -it / 4 }
|
||||
},
|
||||
enterTransition = {
|
||||
slideInHorizontally { it / 2 } + scaleIn(initialScale = 0.9f) + fadeIn()
|
||||
@ -267,6 +276,12 @@ class SettingsActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
val newRoute = intent.getStringExtra(EXTRA_ROUTE)
|
||||
route = newRoute
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_ROUTE = "de.mm20.launcher2.settings.ROUTE"
|
||||
const val ROUTE_WEATHER_INTEGRATION = "settings/integrations/weather"
|
||||
|
||||
@ -968,4 +968,5 @@
|
||||
<item quantity="one">%1$s notification</item>
|
||||
<item quantity="other">%1$s notifications</item>
|
||||
</plurals>
|
||||
<string name="weather_widget_no_provider">No weather provider selected or the selected provider is not available</string>
|
||||
</resources>
|
||||
@ -27,6 +27,7 @@ import java.util.*
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
interface WeatherRepository {
|
||||
fun getActiveProvider(): Flow<WeatherProviderInfo?>
|
||||
fun getProviders(): Flow<List<WeatherProviderInfo>>
|
||||
fun searchLocations(query: String): Flow<List<WeatherLocation>>
|
||||
|
||||
@ -147,6 +148,14 @@ internal class WeatherRepositoryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getActiveProvider(): Flow<WeatherProviderInfo?> {
|
||||
return settings.providerId.flatMapLatest { id ->
|
||||
getProviders().map {
|
||||
it.find { it.id == id }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getProviders(): Flow<List<WeatherProviderInfo>> {
|
||||
val providers = mutableListOf<WeatherProviderInfo>()
|
||||
providers.add(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user