Redesign places result

This commit is contained in:
MM20 2024-05-05 22:35:57 +02:00
parent a382c40c20
commit 2cda2437f3
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
13 changed files with 507 additions and 199 deletions

View File

@ -2,72 +2,75 @@ package de.mm20.launcher2.ui.launcher.search.location
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.widget.Toast import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.EaseOutBack import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateValueAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn import androidx.compose.animation.expandIn
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkOut import androidx.compose.animation.shrinkOut
import androidx.compose.animation.slideIn
import androidx.compose.animation.slideOut
import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material.icons.rounded.ArrowUpward import androidx.compose.material.icons.automirrored.rounded.NavigateNext
import androidx.compose.material.icons.rounded.BugReport import androidx.compose.material.icons.rounded.BugReport
import androidx.compose.material.icons.rounded.FiberManualRecord
import androidx.compose.material.icons.rounded.Language
import androidx.compose.material.icons.rounded.Map import androidx.compose.material.icons.rounded.Map
import androidx.compose.material.icons.rounded.Navigation
import androidx.compose.material.icons.rounded.Phone import androidx.compose.material.icons.rounded.Phone
import androidx.compose.material.icons.rounded.Star import androidx.compose.material.icons.rounded.Star
import androidx.compose.material.icons.rounded.StarOutline import androidx.compose.material.icons.rounded.StarOutline
import androidx.compose.material.icons.rounded.TravelExplore import androidx.compose.material3.AssistChip
import androidx.compose.material.icons.twotone.CloudOff import androidx.compose.material3.AssistChipDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.roundToIntRect import androidx.compose.ui.unit.roundToIntRect
import androidx.compose.ui.unit.times
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import de.mm20.launcher2.i18n.R import de.mm20.launcher2.i18n.R
import de.mm20.launcher2.ktx.tryStartActivity import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.Location import de.mm20.launcher2.search.Location
import de.mm20.launcher2.ui.animation.animateHorizontalAlignmentAsState import de.mm20.launcher2.search.OpeningHours
import de.mm20.launcher2.ui.animation.animateTextStyleAsState import de.mm20.launcher2.search.OpeningSchedule
import de.mm20.launcher2.ui.component.DefaultToolbarAction import de.mm20.launcher2.ui.component.DefaultToolbarAction
import de.mm20.launcher2.ui.component.ShapedLauncherIcon import de.mm20.launcher2.ui.component.ShapedLauncherIcon
import de.mm20.launcher2.ui.component.Toolbar import de.mm20.launcher2.ui.component.Toolbar
import de.mm20.launcher2.ui.component.ToolbarAction import de.mm20.launcher2.ui.component.ToolbarAction
import de.mm20.launcher2.ui.ktx.DegreesConverter
import de.mm20.launcher2.ui.ktx.metersToLocalizedString import de.mm20.launcher2.ui.ktx.metersToLocalizedString
import de.mm20.launcher2.ui.ktx.toPixels import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.launcher.search.common.SearchableItemVM import de.mm20.launcher2.ui.launcher.search.common.SearchableItemVM
@ -76,14 +79,11 @@ import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled
import de.mm20.launcher2.ui.locals.LocalGridSettings import de.mm20.launcher2.ui.locals.LocalGridSettings
import de.mm20.launcher2.ui.modifier.scale import de.mm20.launcher2.ui.modifier.scale
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch
import java.time.DayOfWeek
import java.time.Duration
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.time.format.TextStyle import java.time.format.TextStyle
import java.util.Locale
import kotlin.math.pow import kotlin.math.pow
@Composable @Composable
@ -117,13 +117,442 @@ fun LocationItem(
} else emptyFlow() } else emptyFlow()
}.collectAsStateWithLifecycle(null) }.collectAsStateWithLifecycle(null)
val icon by viewModel.icon.collectAsStateWithLifecycle()
val badge by viewModel.badge.collectAsStateWithLifecycle(null)
val imperialUnits by viewModel.imperialUnits.collectAsState() val imperialUnits by viewModel.imperialUnits.collectAsState()
val isUpToDate by viewModel.isUpToDate.collectAsState() val isUpToDate by viewModel.isUpToDate.collectAsState()
val distance = userLocation?.distanceTo(location.toAndroidLocation()) val distance = userLocation?.distanceTo(location.toAndroidLocation())
Row(modifier = modifier) { SharedTransitionLayout(
modifier = modifier,
) {
AnimatedContent(showDetails) { showDetails ->
if (!showDetails) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
ShapedLauncherIcon(
modifier = Modifier
.animateEnterExit(
enter = slideIn { IntOffset(-it.width, 0) } + fadeIn(),
exit = slideOut { IntOffset(-it.width, 0) } + fadeOut(),
)
.padding(8.dp),
size = 48.dp,
icon = { icon },
badge = { badge },
)
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 8.dp),
verticalArrangement = androidx.compose.foundation.layout.Arrangement.Center,
) {
Text(
text = location.labelOverride ?: location.label,
style = MaterialTheme.typography.titleSmall,
modifier = Modifier
.sharedBounds(
rememberSharedContentState("label"),
this@AnimatedContent
)
)
val category = location.category
val formattedDistance = distance?.metersToLocalizedString(
context, imperialUnits
)
if (category != null || formattedDistance != null) {
Text(
when {
category != null && formattedDistance != null -> "${category}${formattedDistance}"
category != null -> category.toString()
formattedDistance != null -> formattedDistance
else -> ""
},
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier
.sharedElement(
rememberSharedContentState("sublabel"),
this@AnimatedContent
)
)
}
}
Box(
modifier = Modifier
.animateEnterExit(
enter = slideIn { IntOffset(it.width, 0) } + fadeIn(),
exit = slideOut { IntOffset(it.width, 0) } + fadeOut(),
)
.padding(end = 8.dp)
.size(48.dp)
.background(
MaterialTheme.colorScheme.secondaryContainer,
MaterialTheme.shapes.small
),
contentAlignment = Alignment.Center
) {
Icon(
if (targetHeading != null) Icons.Rounded.Navigation else Icons.Rounded.FiberManualRecord,
null,
modifier = Modifier
.size(20.dp)
.rotate(targetHeading ?: 0f),
tint = MaterialTheme.colorScheme.onSecondaryContainer,
)
}
}
} else {
val showMap by viewModel.showMap.collectAsState()
Column {
if (showMap) {
val tileServerUrl by viewModel.mapTileServerUrl.collectAsState()
val shape = MaterialTheme.shapes.small
val applyTheming by viewModel.applyMapTheming.collectAsState()
val showPositionOnMap by viewModel.showPositionOnMap.collectAsState()
MapTiles(
modifier = Modifier
.animateEnterExit(
enter = expandVertically(),
exit = shrinkOut(),
)
.padding(12.dp)
.align(Alignment.CenterHorizontally)
.fillMaxWidth()
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, shape)
.clip(MaterialTheme.shapes.small)
.clickable {
viewModel.launch(context)
},
tileServerUrl = tileServerUrl,
location = location,
maxZoomLevel = 19,
tiles = IntSize(3, 2),
applyTheming = applyTheming,
userLocation = {
if (showPositionOnMap) userLocation?.let {
UserLocation(
it.latitude,
it.longitude,
heading = userHeading,
)
} else null
},
)
}
Row(
modifier = Modifier.padding(horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Column {
Text(
text = location.labelOverride ?: location.label,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.sharedBounds(
rememberSharedContentState("label"),
this@AnimatedContent
)
)
val category = location.category
val formattedDistance = distance?.metersToLocalizedString(
context, imperialUnits
)
if (category != null || formattedDistance != null) {
Text(
when {
category != null && formattedDistance != null -> "${category}${formattedDistance}"
category != null -> category.toString()
formattedDistance != null -> formattedDistance
else -> ""
},
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier
.sharedElement(
rememberSharedContentState("sublabel"),
this@AnimatedContent
)
)
}
}
}
val openingSchedule = location.openingSchedule
if (openingSchedule != null && (openingSchedule.isTwentyFourSeven || openingSchedule.openingHours.isNotEmpty())) {
var showOpeningSchedule by remember(openingSchedule) {
mutableStateOf(false)
}
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp, top = 12.dp),
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.colorScheme.surfaceContainer,
onClick = {
if (!openingSchedule.isTwentyFourSeven) {
showOpeningSchedule = true
}
}
) {
AnimatedContent(showOpeningSchedule) { showSchedule ->
if (!showSchedule) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
if (openingSchedule.isTwentyFourSeven) {
Text(
text = stringResource(R.string.location_open_24_7),
style = MaterialTheme.typography.labelMedium,
)
} else {
val text = remember(openingSchedule) {
val currentOpeningTime =
openingSchedule.getCurrentOpeningHours()
val timeFormat =
DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return@remember if (currentOpeningTime != null) {
val isSameDay =
currentOpeningTime.dayOfWeek == LocalDateTime.now().dayOfWeek
val formattedTime =
timeFormat.format(currentOpeningTime.startTime + currentOpeningTime.duration)
val closingTime = if (isSameDay) {
context.getString(
R.string.location_closes,
formattedTime
)
} else {
val dow = currentOpeningTime.dayOfWeek.getDisplayName(
TextStyle.SHORT,
Locale.getDefault()
)
context.getString(
R.string.location_closes_other_day,
dow,
formattedTime
)
}
"${context.getString(R.string.location_open)}$closingTime"
} else {
val nextOpeningTime =
openingSchedule.getNextOpeningHours()
val isSameDay = nextOpeningTime.dayOfWeek == LocalDateTime.now().dayOfWeek
val formattedTime = timeFormat.format(nextOpeningTime.startTime)
val openingTime = if (isSameDay) {
context.getString(
R.string.location_opens,
formattedTime
)
} else {
val dow = nextOpeningTime.dayOfWeek.getDisplayName(
TextStyle.SHORT,
Locale.getDefault()
)
context.getString(
R.string.location_opens_other_day,
dow,
formattedTime
)
}
"${context.getString(R.string.location_closed)}$openingTime"
}
}
Text(text = text, style = MaterialTheme.typography.labelMedium, modifier = Modifier.weight(1f))
Icon(Icons.AutoMirrored.Rounded.NavigateNext, null)
}
}
} else {
Column(
modifier = Modifier.padding(vertical = 6.dp)
) {
val groups = remember(openingSchedule) {
openingSchedule.openingHours
.groupBy { it.dayOfWeek }.entries
.sortedBy { it.key }
}
for (group in groups) {
Row(
modifier = Modifier.padding(
vertical = 2.dp,
horizontal = 12.dp
),
) {
Text(
modifier = Modifier.weight(1f),
text = group.key.getDisplayName(
TextStyle.FULL,
Locale.getDefault()
),
style = MaterialTheme.typography.bodySmall,
)
val times = remember(group.value) {
val dateFormatter =
DateTimeFormatter.ofLocalizedTime(
FormatStyle.SHORT
)
group.value.sortedBy { it.startTime }
.joinToString(separator = ", ") {
"${it.startTime.format(dateFormatter)}${
it.startTime.plus(
it.duration
).format(dateFormatter)
}"
}
}
Text(
text = times,
style = MaterialTheme.typography.labelMedium,
)
}
}
}
}
}
}
}
Row(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.padding(start = 12.dp, top = 8.dp)
) {
AssistChip(
modifier = Modifier.padding(end = 12.dp),
onClick = {
context.tryStartActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("google.navigation:q=${location.latitude},${location.longitude}")
),
)
},
label = { Text(stringResource(R.string.menu_navigation)) },
leadingIcon = {
Icon(
Icons.Rounded.Navigation, null,
modifier = Modifier.size(AssistChipDefaults.IconSize)
)
}
)
location.phoneNumber?.let {
AssistChip(
modifier = Modifier.padding(end = 12.dp),
onClick = {
context.tryStartActivity(
Intent(
Intent.ACTION_DIAL, Uri.parse("tel:$it")
)
)
},
label = { Text(stringResource(R.string.menu_dial)) },
leadingIcon = {
Icon(
Icons.Rounded.Phone, null,
modifier = Modifier.size(AssistChipDefaults.IconSize)
)
}
)
}
location.websiteUrl?.let {
AssistChip(
modifier = Modifier.padding(end = 12.dp),
onClick = {
context.tryStartActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(it)
)
)
},
label = { Text(stringResource(R.string.menu_website)) },
leadingIcon = {
Icon(
Icons.Rounded.Language, null,
modifier = Modifier.size(AssistChipDefaults.IconSize)
)
}
)
}
}
val toolbarActions = mutableListOf<ToolbarAction>()
if (LocalFavoritesEnabled.current) {
val isPinned by viewModel.isPinned.collectAsState(false)
val favAction = if (isPinned) {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_unpin),
icon = Icons.Rounded.Star,
action = {
viewModel.unpin()
}
)
} else {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_pin),
icon = Icons.Rounded.StarOutline,
action = {
viewModel.pin()
})
}
toolbarActions.add(favAction)
}
if (!showMap) {
toolbarActions += DefaultToolbarAction(
label = stringResource(id = R.string.menu_map),
icon = Icons.Rounded.Map
) {
viewModel.launch(context)
}
}
location.fixMeUrl?.let {
toolbarActions += DefaultToolbarAction(
label = stringResource(id = R.string.menu_bugreport),
icon = Icons.Rounded.BugReport,
) {
context.tryStartActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(location.fixMeUrl)
)
)
}
}
Toolbar(
modifier = Modifier.fillMaxWidth(),
leftActions = listOf(DefaultToolbarAction(
label = stringResource(id = R.string.menu_back),
icon = Icons.AutoMirrored.Rounded.ArrowBack
) {
onBack()
}),
rightActions = toolbarActions,
)
}
}
}
}
/*Row(modifier = modifier) {
Column { Column {
Row( Row(
modifier = Modifier modifier = Modifier
@ -136,8 +565,6 @@ fun LocationItem(
modifier = Modifier.padding(start = 8.dp), modifier = Modifier.padding(start = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
val icon by viewModel.icon.collectAsStateWithLifecycle()
val badge by viewModel.badge.collectAsState(null)
Box( Box(
modifier = Modifier modifier = Modifier
.size(52.dp) .size(52.dp)
@ -283,56 +710,20 @@ fun LocationItem(
} }
} }
val showMap by viewModel.showMap.collectAsState()
if (showMap) {
val zoomLevel = 19
val nTiles = 4
val tileServerUrl by viewModel.mapTileServerUrl.collectAsState() HorizontalDivider()
val shape = MaterialTheme.shapes.small
val applyTheming by viewModel.applyMapTheming.collectAsState()
val showPositionOnMap by viewModel.showPositionOnMap.collectAsState()
HorizontalDivider() val address = buildAddress(location.street, location.houseNumber)
if (address != null) {
MapTiles( Text(
modifier = Modifier modifier = Modifier
.padding(top = 16.dp, bottom = 8.dp)
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.fillMaxWidth(.9125f) .padding(bottom = 8.dp),
.border(1.dp, MaterialTheme.colorScheme.outline, shape) text = address,
.clip(shape) style = MaterialTheme.typography.labelSmall,
.clickable { color = MaterialTheme.colorScheme.secondary
viewModel.launch(context)
},
tileServerUrl = tileServerUrl,
location = location,
maxZoomLevel = zoomLevel,
tiles = IntSize(3, 2),
applyTheming = applyTheming,
userLocation = {
if (showPositionOnMap) userLocation?.let {
UserLocation(
it.latitude,
it.longitude,
heading = userHeading,
)
} else null
},
) )
val address = buildAddress(location.street, location.houseNumber)
if (address != null) {
Text(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(bottom = 8.dp),
text = address,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.secondary
)
}
} }
HorizontalDivider(Modifier.padding(top = 8.dp)) HorizontalDivider(Modifier.padding(top = 8.dp))
@ -378,97 +769,10 @@ fun LocationItem(
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
) )
} }
val toolbarActions = mutableListOf<ToolbarAction>()
if (LocalFavoritesEnabled.current) {
val isPinned by viewModel.isPinned.collectAsState(false)
val favAction = if (isPinned) {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_unpin),
icon = Icons.Rounded.Star,
action = {
viewModel.unpin()
}
)
} else {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_pin),
icon = Icons.Rounded.StarOutline,
action = {
viewModel.pin()
})
}
toolbarActions.add(favAction)
}
if (!showMap) {
toolbarActions += DefaultToolbarAction(
label = stringResource(id = R.string.menu_map),
icon = Icons.Rounded.Map
) {
viewModel.launch(context)
}
}
location.phoneNumber?.let {
toolbarActions += DefaultToolbarAction(
label = stringResource(id = R.string.menu_dial),
icon = Icons.Rounded.Phone
) {
viewModel.viewModelScope.launch {
context.tryStartActivity(
Intent(
Intent.ACTION_DIAL, Uri.parse("tel:$it")
)
)
}
}
}
location.websiteUrl?.let {
toolbarActions += DefaultToolbarAction(
label = stringResource(id = R.string.menu_website),
icon = Icons.Rounded.TravelExplore
) {
viewModel.viewModelScope.launch {
context.tryStartActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(it)
)
)
}
}
}
location.fixMeUrl?.let {
toolbarActions += DefaultToolbarAction(
label = stringResource(id = R.string.menu_bugreport),
icon = Icons.Rounded.BugReport,
) {
context.tryStartActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(location.fixMeUrl)
)
)
}
}
Toolbar(
modifier = Modifier.fillMaxWidth(),
leftActions = listOf(DefaultToolbarAction(
label = stringResource(id = R.string.menu_back),
icon = Icons.AutoMirrored.Rounded.ArrowBack
) {
onBack()
}),
rightActions = toolbarActions,
)
} }
} }
} }
} }*/
} }
@Composable @Composable
@ -521,3 +825,25 @@ private fun buildAddress(
} }
return if (summary.isEmpty()) null else summary.toString() return if (summary.isEmpty()) null else summary.toString()
} }
private fun OpeningSchedule.getCurrentOpeningHours(): OpeningHours? {
return openingHours.find { it.isOpen }
}
private fun OpeningSchedule.getNextOpeningHours(): OpeningHours {
val now = LocalDateTime.now()
val sortedSchedule = this
.openingHours
.sortedWith { a, b ->
if (a.dayOfWeek == b.dayOfWeek) {
a.startTime.compareTo(b.startTime)
} else {
a.dayOfWeek.compareTo(b.dayOfWeek)
}
}
return sortedSchedule
.find {
now.dayOfWeek < it.dayOfWeek || now.dayOfWeek == it.dayOfWeek && now.toLocalTime() < it.startTime
} ?: sortedSchedule.first()
}

View File

@ -23,9 +23,11 @@ import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Navigation import androidx.compose.material.icons.rounded.Navigation
import androidx.compose.material.icons.rounded.Place import androidx.compose.material.icons.rounded.Place
@ -185,31 +187,6 @@ fun MapTiles(
), ),
label = "poiLocAnimation" label = "poiLocAnimation"
) )
val textMeasurer = rememberTextMeasurer()
val osmAttributionTextStyle = MaterialTheme.typography.labelSmall
val osmAttributionTextColor = MaterialTheme.colorScheme.onSurface
val osmAttributionSurface =
MaterialTheme.colorScheme.surfaceContainerHigh.copy(alpha = .5f)
val (yLocation, xLocation) = remember(location, zoom) {
getTileCoordinates(
latitude = location.latitude,
longitude = location.longitude,
zoom
)
}
val userOffset = remember(userLocation, zoom) {
if (userLocation != null) {
getTileCoordinates(
latitude = userLocation.lat,
longitude = userLocation.lon,
zoom
)
} else {
Offset.Unspecified
}
}
val tileSize = minWidth / tiles.width.toFloat() val tileSize = minWidth / tiles.width.toFloat()
val locationTileCoordinates = val locationTileCoordinates =
getTileCoordinates(location.latitude, location.longitude, zoom) getTileCoordinates(location.latitude, location.longitude, zoom)
@ -275,6 +252,18 @@ fun MapTiles(
.border(2.dp, MaterialTheme.colorScheme.onTertiary, CircleShape) .border(2.dp, MaterialTheme.colorScheme.onTertiary, CircleShape)
.shadow(1.dp, CircleShape) .shadow(1.dp, CircleShape)
) )
if (osmAttribution != null) {
Text(
text = osmAttribution,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier
.align(Alignment.BottomEnd)
.background(MaterialTheme.colorScheme.surfaceContainerHigh.copy(alpha = .5f))
.padding(top = 2.dp, bottom = 2.dp, start = 4.dp, end = 4.dp)
)
}
} }
} }
} }

View File

@ -666,7 +666,6 @@
<string name="missing_permission_plugins">Es requereix permís de connector per utilitzar connectors.</string> <string name="missing_permission_plugins">Es requereix permís de connector per utilitzar connectors.</string>
<string name="preference_plugin_badges_summary">Indiqueu amb quin connector s\'ha creat un resultat de cerca</string> <string name="preference_plugin_badges_summary">Indiqueu amb quin connector s\'ha creat un resultat de cerca</string>
<string name="preference_mdy_color_source_wallpaper">Colors del Fons de Pantalla</string> <string name="preference_mdy_color_source_wallpaper">Colors del Fons de Pantalla</string>
<string name="location_open_next_day">Estarà disponible %1$s</string>
<string name="missing_permission_location_search">Doneu permís a la ubicació per cercar ubicacions properes.</string> <string name="missing_permission_location_search">Doneu permís a la ubicació per cercar ubicacions properes.</string>
<string name="preference_mdy_color_source">Font de Colors dinàmics</string> <string name="preference_mdy_color_source">Font de Colors dinàmics</string>
<string name="preference_mdy_color_source_system">Colors del Sistema</string> <string name="preference_mdy_color_source_system">Colors del Sistema</string>

View File

@ -688,7 +688,6 @@
<string name="preference_search_locations_radius">Vzdálenost vyhledávání</string> <string name="preference_search_locations_radius">Vzdálenost vyhledávání</string>
<string name="preference_search_locations_summary">Vyhledat v místní oblasti obchody a další místa</string> <string name="preference_search_locations_summary">Vyhledat v místní oblasti obchody a další místa</string>
<string name="preference_search_locations_show_position_on_map_summary">Zobrazit vlastní lokaci na mapě</string> <string name="preference_search_locations_show_position_on_map_summary">Zobrazit vlastní lokaci na mapě</string>
<string name="location_open_next_day">Otevírá v %1$s</string>
<string name="preference_search_locations_show_map_summary">Zobrazit náhled mapy pro místa</string> <string name="preference_search_locations_show_map_summary">Zobrazit náhled mapy pro místa</string>
<string name="preference_search_locations_theme_map">Téma mapy</string> <string name="preference_search_locations_theme_map">Téma mapy</string>
<string name="preference_search_locations_theme_map_summary">Použít barevné schéma launcheru jako téma mapy</string> <string name="preference_search_locations_theme_map_summary">Použít barevné schéma launcheru jako téma mapy</string>

View File

@ -678,7 +678,6 @@
<string name="default_websearch_3_url">https://play.google.com/store/search?q=${1}</string> <string name="default_websearch_3_url">https://play.google.com/store/search?q=${1}</string>
<string name="shortcut_unavailable_description">Denne genvej er utilgængelig, fordi %1$s ikke er din standard launcher</string> <string name="shortcut_unavailable_description">Denne genvej er utilgængelig, fordi %1$s ikke er din standard launcher</string>
<string name="default_websearch_2_url">https://www.youtube.com/results?search_query=${1}</string> <string name="default_websearch_2_url">https://www.youtube.com/results?search_query=${1}</string>
<string name="location_open_next_day">Åbner på %1$s</string>
<string name="location_open">Åben</string> <string name="location_open">Åben</string>
<string name="location_closed">Lukket</string> <string name="location_closed">Lukket</string>
<string name="location_open_until">Åben indtil %1$s</string> <string name="location_open_until">Åben indtil %1$s</string>

View File

@ -681,7 +681,6 @@
<string name="preference_mdy_color_source">Quelle für dynamische Farben</string> <string name="preference_mdy_color_source">Quelle für dynamische Farben</string>
<string name="preference_search_locations_show_position_on_map_summary">Den eigenen Standort auf der Karte zeigen</string> <string name="preference_search_locations_show_position_on_map_summary">Den eigenen Standort auf der Karte zeigen</string>
<string name="location_closed">Geschlossen</string> <string name="location_closed">Geschlossen</string>
<string name="location_open_next_day">Öffnet am %1$s</string>
<string name="preference_search_location_custom_overpass_url">Overpass-URL</string> <string name="preference_search_location_custom_overpass_url">Overpass-URL</string>
<string name="preference_search_locations">Orte</string> <string name="preference_search_locations">Orte</string>
<string name="location_open">Geöffnet</string> <string name="location_open">Geöffnet</string>

View File

@ -703,7 +703,6 @@
<string name="clock_style_analog">Manecillas</string> <string name="clock_style_analog">Manecillas</string>
<string name="clock_style_empty">Sin reloj</string> <string name="clock_style_empty">Sin reloj</string>
<string name="clock_variant_standard">Estándar</string> <string name="clock_variant_standard">Estándar</string>
<string name="location_open_next_day">Abre el %1$s</string>
<string name="preference_search_locations_show_map">Mapa</string> <string name="preference_search_locations_show_map">Mapa</string>
<string name="preference_search_locations_show_map_summary">Mostrar una vista previa de mapa para los lugares</string> <string name="preference_search_locations_show_map_summary">Mostrar una vista previa de mapa para los lugares</string>
<string name="preference_search_locations_theme_map">Tema del mapa</string> <string name="preference_search_locations_theme_map">Tema del mapa</string>

View File

@ -688,7 +688,6 @@
<string name="preference_search_locations_hide_uncategorized">Kategorizálatlan helyek elrejtése</string> <string name="preference_search_locations_hide_uncategorized">Kategorizálatlan helyek elrejtése</string>
<string name="preference_search_locations_hide_uncategorized_summary">Csak jól meghatározott kategóriák, például kávézók, vagy éttermek eredményeit jeleníti meg</string> <string name="preference_search_locations_hide_uncategorized_summary">Csak jól meghatározott kategóriák, például kávézók, vagy éttermek eredményeit jeleníti meg</string>
<string name="preference_search_locations_show_map">Térkép</string> <string name="preference_search_locations_show_map">Térkép</string>
<string name="location_open_next_day">Kinyit ekkor: %1$s</string>
<string name="preference_search_locations_theme_map">Térkép színséma</string> <string name="preference_search_locations_theme_map">Térkép színséma</string>
<string name="preference_search_locations_theme_map_summary">Az indító színsémájának alkalmazása a térképre</string> <string name="preference_search_locations_theme_map_summary">Az indító színsémájának alkalmazása a térképre</string>
<string name="preference_search_locations_show_position_on_map">Saját helyszín megjelenítése</string> <string name="preference_search_locations_show_position_on_map">Saját helyszín megjelenítése</string>

View File

@ -687,7 +687,6 @@
<string name="preference_search_location_custom_overpass_url">URL Overpass</string> <string name="preference_search_location_custom_overpass_url">URL Overpass</string>
<string name="preference_search_bar_separate_work_profile">App per profili di lavoro separate</string> <string name="preference_search_bar_separate_work_profile">App per profili di lavoro separate</string>
<string name="preference_search_bar_separate_work_profile_summary">Mostra app del profilo di lavoro in una scheda separata</string> <string name="preference_search_bar_separate_work_profile_summary">Mostra app del profilo di lavoro in una scheda separata</string>
<string name="location_open_next_day">Apertura %1$s</string>
<string name="preference_search_locations_hide_uncategorized">Nascondi luoghi senza categoria</string> <string name="preference_search_locations_hide_uncategorized">Nascondi luoghi senza categoria</string>
<string name="preference_clock_widget_show_seconds">Mostra secondi</string> <string name="preference_clock_widget_show_seconds">Mostra secondi</string>
<string name="widget_use_theme_colors">Usa colore del tema</string> <string name="widget_use_theme_colors">Usa colore del tema</string>

View File

@ -347,7 +347,6 @@
<string name="location_closed">Fechado</string> <string name="location_closed">Fechado</string>
<string name="location_open_until">Aberto até %1$s</string> <string name="location_open_until">Aberto até %1$s</string>
<string name="location_open_next">Abre em %1$s</string> <string name="location_open_next">Abre em %1$s</string>
<string name="location_open_next_day">Abre no/na %1$s</string>
<string name="menu_map">Ver no mapa</string> <string name="menu_map">Ver no mapa</string>
<string name="menu_website">Abrir página web</string> <string name="menu_website">Abrir página web</string>
</resources> </resources>

View File

@ -691,7 +691,6 @@
<string name="missing_permission_location_search">Для поиска мест поблизости требуется разрешение на доступ к местоположению.</string> <string name="missing_permission_location_search">Для поиска мест поблизости требуется разрешение на доступ к местоположению.</string>
<string name="location_open_until">Закроется в %1$s</string> <string name="location_open_until">Закроется в %1$s</string>
<string name="location_open_next">Откроется в %1$s</string> <string name="location_open_next">Откроется в %1$s</string>
<string name="location_open_next_day">Откроется в %1$s</string>
<string name="menu_map">Показать на карте</string> <string name="menu_map">Показать на карте</string>
<string name="preference_search_locations">Места</string> <string name="preference_search_locations">Места</string>
<string name="preference_search_locations_radius">Радиус поиска</string> <string name="preference_search_locations_radius">Радиус поиска</string>

View File

@ -195,7 +195,6 @@
<string name="location_closed">Kapalı</string> <string name="location_closed">Kapalı</string>
<string name="location_open_until">Saat %1$s\'e kadar açık</string> <string name="location_open_until">Saat %1$s\'e kadar açık</string>
<string name="location_open_next">%1$s Sonra açılacaktır</string> <string name="location_open_next">%1$s Sonra açılacaktır</string>
<string name="location_open_next_day">%1$s Günü açılacaktır</string>
<string name="menu_map">Haritada göster</string> <string name="menu_map">Haritada göster</string>
<string name="menu_website">Websiteyi aç</string> <string name="menu_website">Websiteyi aç</string>
<string name="calendar_widget_pinned_events">Yakında</string> <string name="calendar_widget_pinned_events">Yakında</string>

View File

@ -2,12 +2,14 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:locale="en"> <resources xmlns:tools="http://schemas.android.com/tools" tools:locale="en">
<string name="location_open">Open</string> <string name="location_open">Open</string>
<string name="location_closed">Closed</string> <string name="location_closed">Closed</string>
<!-- Shown when a location search result is open until TIMESTAMP (hours:minutes) of the same day --> <!-- Time when a place next closes (on the same day). $1%s: time (hh:mm) -->
<string name="location_open_until">Open until %1$s</string> <string name="location_closes">closes at %1$s</string>
<!-- Shown when a location search result opens in TIMESPAN on the same day --> <!-- Time when a place next closes (on a different day). $1%s: day of week, %2$s: time (hh:mm) -->
<string name="location_open_next">Opens in %1$s</string> <string name="location_closes_other_day">closes %1$s, %2$s</string>
<!-- Shown when a location search result opens next DAY-OF-WEEK --> <!-- Time when a place next opens (on the same day). $1%s: time (hh:mm) -->
<string name="location_open_next_day">Opens on %1$s</string> <string name="location_opens">opens at %1$s</string>
<!-- Time when a place next opens (on a different day). $1%s: day of week, %2$s: time (hh:mm) -->
<string name="location_opens_other_day">opens %1$s, %2$s</string>
<!-- Name of the app --> <!-- Name of the app -->
<!-- Share something. Can be anything, an app, a website, a file, … --> <!-- Share something. Can be anything, an app, a website, a file, … -->
<string name="menu_share">Share</string> <string name="menu_share">Share</string>
@ -893,6 +895,7 @@
<string name="preference_search_locations_show_position_on_map">Show own position</string> <string name="preference_search_locations_show_position_on_map">Show own position</string>
<string name="preference_search_location_custom_tile_server_url">Tileserver URL</string> <string name="preference_search_location_custom_tile_server_url">Tileserver URL</string>
<string name="menu_dial">Call</string> <string name="menu_dial">Call</string>
<string name="menu_navigation">Navigation</string>
<string name="menu_bugreport">Bug Report</string> <string name="menu_bugreport">Bug Report</string>
<string name="location_open_24_7">Open 24/7</string> <string name="location_open_24_7">Open 24/7</string>
<string name="plugin_state_error">This plugin isn\'t working correctly</string> <string name="plugin_state_error">This plugin isn\'t working correctly</string>