Always show weather and music widgets even if there's no data available

This commit is contained in:
MM20 2021-12-15 20:26:55 +01:00
parent 960c645010
commit fb50c9bd9d
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
4 changed files with 130 additions and 106 deletions

View File

@ -415,4 +415,5 @@
<string name="weather_wind">Wind:</string> <string name="weather_wind">Wind:</string>
<string name="preference_themed_icons">Eingefärbte Symbole</string> <string name="preference_themed_icons">Eingefärbte Symbole</string>
<string name="preference_themed_icons_summary">Symbole an das Farbschema der App anpassen</string> <string name="preference_themed_icons_summary">Symbole an das Farbschema der App anpassen</string>
<string name="weather_no_data">Keine Wetterdaten verfügbar.</string>
</resources> </resources>

View File

@ -445,6 +445,8 @@
<string name="weather_widget_hide_details">Hide details</string> <string name="weather_widget_hide_details">Hide details</string>
<string name="weather_humidity">Humidity:</string> <string name="weather_humidity">Humidity:</string>
<string name="weather_wind">Wind:</string> <string name="weather_wind">Wind:</string>
<string name="weather_no_data">No weather data available.</string>
<string name="weather_precipitation">Precipitation:</string> <string name="weather_precipitation">Precipitation:</string>
<string name="preference_category_license">License</string> <string name="preference_category_license">License</string>
<string name="preference_about_license">This app is free software.</string> <string name="preference_about_license">This app is free software.</string>

View File

@ -7,14 +7,14 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.MusicNote import androidx.compose.material.icons.rounded.MusicNote
import androidx.compose.material.icons.rounded.SkipNext import androidx.compose.material.icons.rounded.SkipNext
import androidx.compose.material.icons.rounded.SkipPrevious import androidx.compose.material.icons.rounded.SkipPrevious
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
@ -29,7 +29,6 @@ import de.mm20.launcher2.music.MusicViewModel
import de.mm20.launcher2.music.PlaybackState import de.mm20.launcher2.music.PlaybackState
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.ktx.conditional import de.mm20.launcher2.ui.ktx.conditional
import de.mm20.launcher2.ui.locals.LocalColorScheme
import org.koin.androidx.compose.getViewModel import org.koin.androidx.compose.getViewModel
@OptIn( @OptIn(
@ -52,111 +51,106 @@ fun MusicWidget() {
Row( Row(
modifier = Modifier.height(IntrinsicSize.Min) modifier = Modifier.height(IntrinsicSize.Min)
) { ) {
if (title != null) { Column(
modifier = Modifier
.padding(top = 16.dp)
.fillMaxHeight()
.weight(2f),
verticalArrangement = Arrangement.SpaceBetween
) {
Column( Column(
modifier = Modifier modifier = Modifier.padding(horizontal = 16.dp)
.padding(top = 16.dp)
.fillMaxHeight()
.weight(2f),
verticalArrangement = Arrangement.SpaceBetween
) { ) {
Column( Text(
modifier = Modifier.padding(horizontal = 16.dp) text = title ?: "---",
) { style = MaterialTheme.typography.titleMedium,
Text( maxLines = 1,
text = title ?: "---", overflow = TextOverflow.Ellipsis
style = MaterialTheme.typography.titleMedium, )
maxLines = 1, Text(
overflow = TextOverflow.Ellipsis text = artist ?: "---",
) modifier = Modifier.padding(vertical = 2.dp),
Text( style = MaterialTheme.typography.bodySmall,
text = artist ?: "---", maxLines = 1,
modifier = Modifier.padding(vertical = 2.dp), overflow = TextOverflow.Ellipsis
style = MaterialTheme.typography.bodySmall, )
maxLines = 1, Text(
overflow = TextOverflow.Ellipsis text = album ?: "---",
) style = MaterialTheme.typography.bodySmall,
Text( maxLines = 1,
text = album ?: "---", overflow = TextOverflow.Ellipsis
style = MaterialTheme.typography.bodySmall, )
maxLines = 1, }
overflow = TextOverflow.Ellipsis Row(
) modifier = Modifier
} .fillMaxWidth()
Row( .padding(bottom = 4.dp, end = 4.dp),
modifier = Modifier horizontalArrangement = Arrangement.SpaceBetween
.fillMaxWidth() ) {
.padding(bottom = 4.dp, end = 4.dp), IconButton(
horizontalArrangement = Arrangement.SpaceBetween onClick = {
) { viewModel.previous()
IconButton(
onClick = {
viewModel.previous()
}) {
Icon(
imageVector = Icons.Rounded.SkipPrevious,
null
)
}
val playPauseIcon = animatedVectorResource(R.drawable.anim_ic_play_pause)
IconButton(onClick = { viewModel.togglePause() }) {
Icon(
painter = playPauseIcon.painterFor(atEnd = playbackState == PlaybackState.Playing),
contentDescription = ""
)
}
IconButton(onClick = {
viewModel.next()
}) { }) {
Icon(
imageVector = Icons.Rounded.SkipNext,
null
)
}
}
}
Box(
modifier = Modifier
.size(144.dp)
.combinedClickable(
onClick = {
viewModel
.getLaunchIntent(context)
.send()
},
onLongClick = {
viewModel.openPlayerChooser(context)
}
)
.conditional(
albumArt == null,
Modifier.background(
MaterialTheme.colorScheme.primaryContainer,
)
),
contentAlignment = Alignment.Center
) {
if (albumArt != null) {
albumArt?.let {
Image(
bitmap = it.asImageBitmap(),
modifier = Modifier
.fillMaxSize(),
contentDescription = null
)
}
} else {
Icon( Icon(
imageVector = Icons.Rounded.MusicNote, imageVector = Icons.Rounded.SkipPrevious,
contentDescription = null, null
tint = MaterialTheme.colorScheme.onPrimaryContainer, )
modifier = Modifier.size(56.dp) }
val playPauseIcon = animatedVectorResource(R.drawable.anim_ic_play_pause)
IconButton(onClick = { viewModel.togglePause() }) {
Icon(
painter = playPauseIcon.painterFor(atEnd = playbackState == PlaybackState.Playing),
contentDescription = ""
)
}
IconButton(onClick = {
viewModel.next()
}) {
Icon(
imageVector = Icons.Rounded.SkipNext,
null
) )
} }
} }
} else {
// TODO
} }
Box(
modifier = Modifier
.size(144.dp)
.combinedClickable(
onClick = {
viewModel
.getLaunchIntent(context)
.send()
},
onLongClick = {
viewModel.openPlayerChooser(context)
}
)
.conditional(
albumArt == null,
Modifier.background(
MaterialTheme.colorScheme.primaryContainer,
)
),
contentAlignment = Alignment.Center
) {
if (albumArt != null) {
albumArt?.let {
Image(
bitmap = it.asImageBitmap(),
modifier = Modifier
.fillMaxSize(),
contentDescription = null
)
}
} else {
Icon(
imageVector = Icons.Rounded.MusicNote,
contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier = Modifier.size(56.dp)
)
}
}
} }
} }

View File

@ -13,6 +13,7 @@ import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowDropDown import androidx.compose.material.icons.rounded.ArrowDropDown
import androidx.compose.material.icons.rounded.LightMode
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
@ -49,18 +50,22 @@ fun WeatherWidget() {
var selectedForecastIndex by remember { mutableStateOf(0) } var selectedForecastIndex by remember { mutableStateOf(0) }
var detailsExpanded by remember { mutableStateOf(false) } var detailsExpanded by remember { mutableStateOf(false) }
if (weatherData.isEmpty()) return
if (weatherData.size <= selectedDayIndex) { if (weatherData.isNotEmpty() && weatherData.size <= selectedDayIndex) {
selectedDayIndex = 0 selectedDayIndex = 0
return return
} }
if (weatherData[selectedDayIndex].hourlyForecasts.size <= selectedForecastIndex) { if (weatherData.isNotEmpty() && weatherData[selectedDayIndex].hourlyForecasts.size <= selectedForecastIndex) {
selectedForecastIndex = 0 selectedForecastIndex = 0
return return
} }
if (weatherData.isEmpty()) {
NoData()
return
}
val selectedForecast = weatherData[selectedDayIndex].hourlyForecasts[selectedForecastIndex] val selectedForecast = weatherData[selectedDayIndex].hourlyForecasts[selectedForecastIndex]
val imperialUnits = LauncherPreferences.instance.imperialUnits val imperialUnits = LauncherPreferences.instance.imperialUnits
@ -441,3 +446,25 @@ private fun weatherIconById(id: Int): WeatherIcon {
else -> WeatherIcon.None else -> WeatherIcon.None
} }
} }
@Composable
fun NoData() {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(imageVector = Icons.Rounded.LightMode,
contentDescription = "",
modifier = Modifier
.padding(24.dp)
.size(32.dp),
tint = MaterialTheme.colorScheme.secondary
)
Text(
text = stringResource(id = R.string.weather_no_data),
style = MaterialTheme.typography.bodySmall
)
}
}