mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 00:35:26 +02:00
Added Time zone converter
Hidden as "tools experiment" Missing: - Clean data - Translations - UI/UX clean up - Relative time change functionality - Custom user time zone functionality closes #59 this is a squashed commit
This commit is contained in:
parent
bba2f24c7c
commit
caf025778e
@ -72,11 +72,19 @@ internal fun UnittoApp(uiPrefs: UIPreferences) {
|
|||||||
// Navigation drawer stuff
|
// Navigation drawer stuff
|
||||||
val drawerState = rememberUnittoDrawerState()
|
val drawerState = rememberUnittoDrawerState()
|
||||||
val drawerScope = rememberCoroutineScope()
|
val drawerScope = rememberCoroutineScope()
|
||||||
val mainTabs = listOf(
|
val mainTabs by remember(uiPrefs.enableToolsExperiment) {
|
||||||
DrawerItems.Calculator,
|
derivedStateOf {
|
||||||
DrawerItems.Converter,
|
if (uiPrefs.enableToolsExperiment) {
|
||||||
DrawerItems.DateDifference
|
listOf(
|
||||||
)
|
DrawerItems.Calculator, DrawerItems.Converter, DrawerItems.DateDifference, DrawerItems.TimeZones
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
listOf(
|
||||||
|
DrawerItems.Calculator, DrawerItems.Converter, DrawerItems.DateDifference,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
val additionalTabs = listOf(DrawerItems.Settings)
|
val additionalTabs = listOf(DrawerItems.Settings)
|
||||||
|
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
@ -129,7 +137,7 @@ internal fun UnittoApp(uiPrefs: UIPreferences) {
|
|||||||
UnittoNavigation(
|
UnittoNavigation(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
themmoController = it,
|
themmoController = it,
|
||||||
startDestination = uiPrefs.startingScreen,
|
startDestination = TopLevelDestinations.TimeZone.route,
|
||||||
openDrawer = { drawerScope.launch { drawerState.open() } }
|
openDrawer = { drawerScope.launch { drawerState.open() } }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,8 @@ internal fun UnittoNavigation(
|
|||||||
|
|
||||||
timeZoneScreen(
|
timeZoneScreen(
|
||||||
navigateToMenu = openDrawer,
|
navigateToMenu = openDrawer,
|
||||||
navigateToSettings = navController::navigateToSettings
|
navigateToSettings = navController::navigateToSettings,
|
||||||
|
navController = navController,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,8 @@ sealed class TopLevelDestinations(
|
|||||||
)
|
)
|
||||||
|
|
||||||
object TimeZone : TopLevelDestinations(
|
object TimeZone : TopLevelDestinations(
|
||||||
route = "time_zone_route",
|
route = "time_zone_graph",
|
||||||
name = R.string.time_zone
|
name = R.string.time_zone_screen
|
||||||
)
|
)
|
||||||
|
|
||||||
object Settings : TopLevelDestinations(
|
object Settings : TopLevelDestinations(
|
||||||
@ -54,5 +54,6 @@ val TOP_LEVEL_DESTINATIONS: Map<TopLevelDestinations, Int> by lazy {
|
|||||||
mapOf(
|
mapOf(
|
||||||
TopLevelDestinations.Converter to R.string.unit_converter,
|
TopLevelDestinations.Converter to R.string.unit_converter,
|
||||||
TopLevelDestinations.Calculator to R.string.calculator,
|
TopLevelDestinations.Calculator to R.string.calculator,
|
||||||
|
TopLevelDestinations.DateDifference to R.string.date_difference,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
|
testImplementation(libs.org.robolectric)
|
||||||
testImplementation(libs.androidx.compose.ui.test.junit4)
|
testImplementation(libs.androidx.compose.ui.test.junit4)
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.core.ui.common
|
package com.sadellie.unitto.core.ui.common
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -44,6 +45,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.Layout
|
import androidx.compose.ui.layout.Layout
|
||||||
import androidx.compose.ui.layout.Placeable
|
import androidx.compose.ui.layout.Placeable
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
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.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
@ -59,23 +61,25 @@ import kotlin.math.max
|
|||||||
@Composable
|
@Composable
|
||||||
fun TimePickerDialog(
|
fun TimePickerDialog(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
localDateTime: LocalDateTime,
|
hour: Int,
|
||||||
|
minute: Int,
|
||||||
confirmLabel: String = stringResource(R.string.ok_label),
|
confirmLabel: String = stringResource(R.string.ok_label),
|
||||||
dismissLabel: String = stringResource(R.string.cancel_label),
|
dismissLabel: String = stringResource(R.string.cancel_label),
|
||||||
onDismiss: () -> Unit = {},
|
onDismiss: () -> Unit = {},
|
||||||
onConfirm: (LocalDateTime) -> Unit,
|
onConfirm: (hour: Int, minute: Int) -> Unit,
|
||||||
vertical: Boolean
|
|
||||||
) {
|
) {
|
||||||
|
val isVertical = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||||
|
|
||||||
val pickerState = rememberTimePickerState(
|
val pickerState = rememberTimePickerState(
|
||||||
localDateTime.hour,
|
initialHour = hour,
|
||||||
localDateTime.minute,
|
initialMinute = minute,
|
||||||
DateFormat.is24HourFormat(LocalContext.current)
|
is24Hour = DateFormat.is24HourFormat(LocalContext.current)
|
||||||
)
|
)
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
modifier = modifier.wrapContentHeight(),
|
modifier = modifier.wrapContentHeight(),
|
||||||
properties = DialogProperties(usePlatformDefaultWidth = vertical)
|
properties = DialogProperties(usePlatformDefaultWidth = isVertical)
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@ -97,7 +101,7 @@ fun TimePickerDialog(
|
|||||||
TimePicker(
|
TimePicker(
|
||||||
state = pickerState,
|
state = pickerState,
|
||||||
modifier = Modifier.padding(top = 20.dp),
|
modifier = Modifier.padding(top = 20.dp),
|
||||||
layoutType = if (vertical) TimePickerLayoutType.Vertical else TimePickerLayoutType.Horizontal
|
layoutType = if (isVertical) TimePickerLayoutType.Vertical else TimePickerLayoutType.Horizontal
|
||||||
)
|
)
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
@ -110,13 +114,7 @@ fun TimePickerDialog(
|
|||||||
Text(text = dismissLabel)
|
Text(text = dismissLabel)
|
||||||
}
|
}
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = { onConfirm(pickerState.hour, pickerState.minute) }
|
||||||
onConfirm(
|
|
||||||
localDateTime
|
|
||||||
.withHour(pickerState.hour)
|
|
||||||
.withMinute(pickerState.minute)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
Text(text = confirmLabel)
|
Text(text = confirmLabel)
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,11 @@ package com.sadellie.unitto.core.ui.common
|
|||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
|
import androidx.compose.material3.FabPosition
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.TopAppBarColors
|
import androidx.compose.material3.TopAppBarColors
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
||||||
@ -35,6 +37,8 @@ import androidx.compose.ui.Modifier
|
|||||||
* @param navigationIcon See [CenterAlignedTopAppBar]
|
* @param navigationIcon See [CenterAlignedTopAppBar]
|
||||||
* @param actions See [CenterAlignedTopAppBar]
|
* @param actions See [CenterAlignedTopAppBar]
|
||||||
* @param colors See [CenterAlignedTopAppBar]
|
* @param colors See [CenterAlignedTopAppBar]
|
||||||
|
* @param floatingActionButton See [Scaffold]
|
||||||
|
* @param scrollBehavior See [CenterAlignedTopAppBar]
|
||||||
* @param content See [Scaffold]
|
* @param content See [Scaffold]
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
@ -44,6 +48,9 @@ fun UnittoScreenWithTopBar(
|
|||||||
navigationIcon: @Composable () -> Unit,
|
navigationIcon: @Composable () -> Unit,
|
||||||
actions: @Composable RowScope.() -> Unit = {},
|
actions: @Composable RowScope.() -> Unit = {},
|
||||||
colors: TopAppBarColors = TopAppBarDefaults.topAppBarColors(),
|
colors: TopAppBarColors = TopAppBarDefaults.topAppBarColors(),
|
||||||
|
floatingActionButton: @Composable () -> Unit = {},
|
||||||
|
floatingActionButtonPosition: FabPosition = FabPosition.End,
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||||
content: @Composable (PaddingValues) -> Unit
|
content: @Composable (PaddingValues) -> Unit
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@ -53,9 +60,12 @@ fun UnittoScreenWithTopBar(
|
|||||||
title = title,
|
title = title,
|
||||||
navigationIcon = navigationIcon,
|
navigationIcon = navigationIcon,
|
||||||
actions = actions,
|
actions = actions,
|
||||||
colors = colors
|
colors = colors,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
floatingActionButton = floatingActionButton,
|
||||||
|
floatingActionButtonPosition = floatingActionButtonPosition,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.core.ui.datetime
|
||||||
|
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
// FIXME Duplicate from date difference
|
||||||
|
internal val time24Formatter by lazy { DateTimeFormatter.ofPattern("HH:mm") }
|
||||||
|
internal val time12Formatter by lazy { DateTimeFormatter.ofPattern("hh:mm a") }
|
||||||
|
internal val dayMonthYear by lazy { DateTimeFormatter.ofPattern("d MMM y") }
|
||||||
|
internal val zoneFormatPattern by lazy { DateTimeFormatter.ofPattern("O") }
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.core.ui.datetime
|
||||||
|
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LocalDateTime.formatLocal(): String {
|
||||||
|
return if (DateFormat.is24HourFormat(LocalContext.current)) format24()
|
||||||
|
else format12()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats [LocalDateTime] into string that looks like
|
||||||
|
*
|
||||||
|
* 23:58
|
||||||
|
*
|
||||||
|
* @return Formatted string.
|
||||||
|
*/
|
||||||
|
fun LocalDateTime.format24(): String = this.format(time24Formatter)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats [LocalDateTime] into string that looks like
|
||||||
|
*
|
||||||
|
* 11:58 am
|
||||||
|
*
|
||||||
|
* @return Formatted string.
|
||||||
|
*/
|
||||||
|
fun LocalDateTime.format12(): String = this.format(time12Formatter)
|
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.core.ui.datetime
|
||||||
|
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import com.sadellie.unitto.core.base.R
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ZonedDateTime.formatLocal(): String {
|
||||||
|
return if (DateFormat.is24HourFormat(LocalContext.current)) format24()
|
||||||
|
else format12()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats [ZonedDateTime] into string that looks like
|
||||||
|
*
|
||||||
|
* 23:58
|
||||||
|
*
|
||||||
|
* @return Formatted string.
|
||||||
|
*/
|
||||||
|
fun ZonedDateTime.format24(): String = this.format(time24Formatter)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats [ZonedDateTime] into string that looks like
|
||||||
|
*
|
||||||
|
* 11:58 am
|
||||||
|
*
|
||||||
|
* @return Formatted string.
|
||||||
|
*/
|
||||||
|
fun ZonedDateTime.format12(): String = this.format(time12Formatter)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats [ZonedDateTime] into string that looks like
|
||||||
|
*
|
||||||
|
* 21 Jul 2023
|
||||||
|
*
|
||||||
|
* @return Formatted string.
|
||||||
|
*/
|
||||||
|
fun ZonedDateTime.formatDayMonthYear(): String = this.format(dayMonthYear)
|
||||||
|
|
||||||
|
fun ZonedDateTime.formatTimeZoneOffset(): String = this.format(zoneFormatPattern)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format offset string. Examples:
|
||||||
|
*
|
||||||
|
* 0
|
||||||
|
*
|
||||||
|
* +8
|
||||||
|
*
|
||||||
|
* +8, tomorrow
|
||||||
|
*
|
||||||
|
* -8, yesterday
|
||||||
|
*
|
||||||
|
* @receiver [ZonedDateTime] Time with offset.
|
||||||
|
* @param currentTime Time without offset.
|
||||||
|
* @return Formatted string.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ZonedDateTime.formatOffset(
|
||||||
|
currentTime: ZonedDateTime
|
||||||
|
): String? {
|
||||||
|
|
||||||
|
val offsetFixed = ChronoUnit.SECONDS.between(currentTime, this)
|
||||||
|
|
||||||
|
if (offsetFixed == 0L) return null
|
||||||
|
|
||||||
|
var resultBuffer = ""
|
||||||
|
val absoluteOffset = offsetFixed.absoluteValue
|
||||||
|
|
||||||
|
// Add a positive/negative prefix symbol
|
||||||
|
when {
|
||||||
|
offsetFixed > 0 -> resultBuffer += "+"
|
||||||
|
offsetFixed < 0 -> resultBuffer += "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatted hours and minutes
|
||||||
|
val hour = absoluteOffset / 3600
|
||||||
|
val minute = absoluteOffset % 3600 / 60
|
||||||
|
|
||||||
|
if (hour != 0L) {
|
||||||
|
resultBuffer += "${hour}${stringResource(R.string.hour_short)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Very ugly
|
||||||
|
if (minute != 0L) {
|
||||||
|
if (hour != 0L) resultBuffer += " "
|
||||||
|
resultBuffer += "${minute}${stringResource(R.string.minute_short)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Day after time string
|
||||||
|
val diff = this.dayOfYear - currentTime.dayOfYear
|
||||||
|
when {
|
||||||
|
diff > 0 -> resultBuffer += ", ${stringResource(R.string.tomorrow).lowercase()}"
|
||||||
|
diff < 0 -> resultBuffer += ", ${stringResource(R.string.yesterday).lowercase()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultBuffer
|
||||||
|
}
|
@ -21,10 +21,12 @@ package com.sadellie.unitto.core.ui.model
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Calculate
|
import androidx.compose.material.icons.filled.Calculate
|
||||||
import androidx.compose.material.icons.filled.Event
|
import androidx.compose.material.icons.filled.Event
|
||||||
|
import androidx.compose.material.icons.filled.Schedule
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material.icons.filled.SwapHoriz
|
import androidx.compose.material.icons.filled.SwapHoriz
|
||||||
import androidx.compose.material.icons.outlined.Calculate
|
import androidx.compose.material.icons.outlined.Calculate
|
||||||
import androidx.compose.material.icons.outlined.Event
|
import androidx.compose.material.icons.outlined.Event
|
||||||
|
import androidx.compose.material.icons.outlined.Schedule
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
import androidx.compose.material.icons.outlined.SwapHoriz
|
import androidx.compose.material.icons.outlined.SwapHoriz
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
@ -53,6 +55,12 @@ sealed class DrawerItems(
|
|||||||
defaultIcon = Icons.Outlined.Event
|
defaultIcon = Icons.Outlined.Event
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object TimeZones : DrawerItems(
|
||||||
|
destination = TopLevelDestinations.TimeZone,
|
||||||
|
selectedIcon = Icons.Filled.Schedule,
|
||||||
|
defaultIcon = Icons.Outlined.Schedule
|
||||||
|
)
|
||||||
|
|
||||||
object Settings : DrawerItems(
|
object Settings : DrawerItems(
|
||||||
destination = TopLevelDestinations.Settings,
|
destination = TopLevelDestinations.Settings,
|
||||||
selectedIcon = Icons.Filled.Settings,
|
selectedIcon = Icons.Filled.Settings,
|
||||||
|
@ -27,14 +27,14 @@ import androidx.compose.ui.unit.em
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.sadellie.unitto.core.base.R
|
import com.sadellie.unitto.core.base.R
|
||||||
|
|
||||||
private val Montserrat = FontFamily(
|
val Montserrat = FontFamily(
|
||||||
Font(R.font.montserrat_light, weight = FontWeight.Light),
|
Font(R.font.montserrat_light, weight = FontWeight.Light),
|
||||||
Font(R.font.montserrat_regular, weight = FontWeight.Normal),
|
Font(R.font.montserrat_regular, weight = FontWeight.Normal),
|
||||||
Font(R.font.montserrat_medium, weight = FontWeight.Medium),
|
Font(R.font.montserrat_medium, weight = FontWeight.Medium),
|
||||||
Font(R.font.montserrat_semibold, weight = FontWeight.SemiBold),
|
Font(R.font.montserrat_semibold, weight = FontWeight.SemiBold),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val Lato = FontFamily(
|
val Lato = FontFamily(
|
||||||
Font(R.font.lato_regular)
|
Font(R.font.lato_regular)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.core.ui
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.junit4.createComposeRule
|
||||||
|
import com.sadellie.unitto.core.ui.datetime.formatOffset
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
class ZonedDateTimeUtilsTest {
|
||||||
|
|
||||||
|
@get: Rule
|
||||||
|
val composeTestRule = createComposeRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `no difference`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val formatted = currentTime.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals(null, formatted)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show positive hour`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.plusSeconds(7200) // + 2h = 14:00
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("+2h", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show positive hour minute`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.plusSeconds(9000) // + 2h 30m = 14:30
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("+2h 30m", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show positive hour minute day`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.plusSeconds(50400) // + 14h = 02:00 tomorrow
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("+14h, tomorrow", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show positive minute`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.plusSeconds(1800) // + 30m = 12:30
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("+30m", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show positive minute day`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(23)
|
||||||
|
.withMinute(45)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.plusSeconds(1800) // + 30m = 00:15 tomorrow
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("+30m, tomorrow", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show negative hour`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.minusSeconds(7200) // - 2h = 10:00
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("-2h", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show negative hour minute`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.minusSeconds(9000) // - 2h 30m = 09:30 tomorrow
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("-2h 30m", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show negative hour minute day`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.minusSeconds(50400) // - 14h = 22:00 yesterday
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("-14h, yesterday", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show negative minute`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(12)
|
||||||
|
.withMinute(0)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.minusSeconds(1800) // - 30m = 11:30
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("-30m", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show negative minute day`() = composeTestRule.setContent {
|
||||||
|
val currentTime = ZonedDateTime.now()
|
||||||
|
.withHour(0)
|
||||||
|
.withMinute(15)
|
||||||
|
|
||||||
|
val offset = currentTime
|
||||||
|
.minusSeconds(1800) // - 30m = 23:45 yesterday
|
||||||
|
.formatOffset(currentTime)
|
||||||
|
|
||||||
|
assertEquals("-30m, yesterday", offset)
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"identityHash": "d5dca9e0346c3400b7ff5b31e85c7827",
|
"identityHash": "ab71572ff5556256d1f042af36243f6f",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "units",
|
"tableName": "units",
|
||||||
@ -34,10 +34,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"unitId"
|
"unitId"
|
||||||
],
|
]
|
||||||
"autoGenerate": false
|
|
||||||
},
|
},
|
||||||
"indices": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
@ -72,10 +72,42 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"entityId"
|
"entityId"
|
||||||
],
|
]
|
||||||
"autoGenerate": true
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "time_zones",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "position",
|
||||||
|
"columnName": "position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "label",
|
||||||
|
"columnName": "label",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"indices": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
@ -84,7 +116,7 @@
|
|||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd5dca9e0346c3400b7ff5b31e85c7827')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ab71572ff5556256d1f042af36243f6f')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 3,
|
||||||
|
"identityHash": "ab71572ff5556256d1f042af36243f6f",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "units",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`unitId` TEXT NOT NULL, `is_favorite` INTEGER, `paired_unit_id` TEXT, `frequency` INTEGER, PRIMARY KEY(`unitId`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "unitId",
|
||||||
|
"columnName": "unitId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isFavorite",
|
||||||
|
"columnName": "is_favorite",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pairedUnitId",
|
||||||
|
"columnName": "paired_unit_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "frequency",
|
||||||
|
"columnName": "frequency",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"unitId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "calculator_history",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`entityId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `expression` TEXT NOT NULL, `result` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "entityId",
|
||||||
|
"columnName": "entityId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "expression",
|
||||||
|
"columnName": "expression",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "result",
|
||||||
|
"columnName": "result",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"entityId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "time_zones",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "position",
|
||||||
|
"columnName": "position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "label",
|
||||||
|
"columnName": "label",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ab71572ff5556256d1f042af36243f6f')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.data.database
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface TimeZoneDao {
|
||||||
|
@Query("SELECT * FROM time_zones ORDER BY position ASC")
|
||||||
|
fun getAll(): Flow<List<TimeZoneEntity>>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insert(vararg timeZoneEntity: TimeZoneEntity)
|
||||||
|
|
||||||
|
@Query("UPDATE time_zones SET position = ( SELECT SUM(position) FROM time_zones WHERE id IN (:fromId, :toId) ) - position WHERE id IN (:fromId, :toId)")
|
||||||
|
suspend fun swap(fromId: String, toId: String)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
suspend fun remove(timeZoneEntity: TimeZoneEntity)
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.data.database
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "time_zones")
|
||||||
|
class TimeZoneEntity(
|
||||||
|
@PrimaryKey val id: String,
|
||||||
|
@ColumnInfo(name = "position") val position: Int,
|
||||||
|
@ColumnInfo(name = "label") val label: String = "",
|
||||||
|
)
|
@ -23,17 +23,20 @@ import androidx.room.Database
|
|||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
version = 2,
|
version = 3,
|
||||||
exportSchema = true,
|
exportSchema = true,
|
||||||
entities = [
|
entities = [
|
||||||
UnitsEntity::class,
|
UnitsEntity::class,
|
||||||
CalculatorHistoryEntity::class
|
CalculatorHistoryEntity::class,
|
||||||
|
TimeZoneEntity::class
|
||||||
],
|
],
|
||||||
autoMigrations = [
|
autoMigrations = [
|
||||||
AutoMigration (from = 1, to = 2)
|
AutoMigration (from = 1, to = 2),
|
||||||
|
AutoMigration (from = 2, to = 3),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
abstract class UnittoDatabase : RoomDatabase() {
|
abstract class UnittoDatabase : RoomDatabase() {
|
||||||
abstract fun unitsDao(): UnitsDao
|
abstract fun unitsDao(): UnitsDao
|
||||||
abstract fun calculatorHistoryDao(): CalculatorHistoryDao
|
abstract fun calculatorHistoryDao(): CalculatorHistoryDao
|
||||||
|
abstract fun timeZoneDao(): TimeZoneDao
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,17 @@ class UnittoDatabaseModule {
|
|||||||
return unittoDatabase.calculatorHistoryDao()
|
return unittoDatabase.calculatorHistoryDao()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells Hilt to use this method to get [TimeZoneDao]
|
||||||
|
*
|
||||||
|
* @param unittoDatabase Database for which we need DAO
|
||||||
|
* @return Singleton of [TimeZoneDao]
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
fun provideTimeZoneDao(unittoDatabase: UnittoDatabase): TimeZoneDao {
|
||||||
|
return unittoDatabase.timeZoneDao()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells Hilt to use this method to get [UnittoDatabase]
|
* Tells Hilt to use this method to get [UnittoDatabase]
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.data.model
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
data class UnittoTimeZone(
|
||||||
|
val id: String,
|
||||||
|
// For beta only, will change to StringRes later
|
||||||
|
val nameRes: String,
|
||||||
|
val position: Int = 0,
|
||||||
|
val offsetSeconds: Long = 0,
|
||||||
|
val code: String = "CODE",
|
||||||
|
) {
|
||||||
|
fun offsetFrom(currentTime: ZonedDateTime): ZonedDateTime {
|
||||||
|
val offsetSeconds = currentTime.offset.totalSeconds.toLong()
|
||||||
|
val currentTimeWithoutOffset = currentTime.minusSeconds(offsetSeconds)
|
||||||
|
|
||||||
|
return currentTimeWithoutOffset.plusSeconds(this.offsetSeconds)
|
||||||
|
}
|
||||||
|
}
|
1
data/timezone/.gitignore
vendored
Normal file
1
data/timezone/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
33
data/timezone/build.gradle.kts
Normal file
33
data/timezone/build.gradle.kts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("unitto.library")
|
||||||
|
id("unitto.android.hilt")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.sadellie.unitto.data.timezone"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(mapOf("path" to ":core:base")))
|
||||||
|
implementation(project(mapOf("path" to ":data:common")))
|
||||||
|
implementation(project(mapOf("path" to ":data:model")))
|
||||||
|
implementation(project(mapOf("path" to ":data:database")))
|
||||||
|
}
|
0
data/timezone/consumer-rules.pro
Normal file
0
data/timezone/consumer-rules.pro
Normal file
22
data/timezone/src/main/AndroidManifest.xml
Normal file
22
data/timezone/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Unitto is a unit converter for Android
|
||||||
|
~ Copyright (c) 2023 Elshan Agaev
|
||||||
|
~
|
||||||
|
~ This program is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by
|
||||||
|
~ the Free Software Foundation, either version 3 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
~ GNU General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest>
|
||||||
|
|
||||||
|
</manifest>
|
@ -0,0 +1,490 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.data.timezone
|
||||||
|
|
||||||
|
import com.sadellie.unitto.data.common.lev
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class TimeZonesRepository @Inject constructor() {
|
||||||
|
private val allTimeZones: HashMap<String, UnittoTimeZone> = hashMapOf(
|
||||||
|
"alfa_time_zone" to UnittoTimeZone(id = "alfa_time_zone", nameRes = "Alfa Time Zone", offsetSeconds = 3600),
|
||||||
|
"australian_central_daylight_time" to UnittoTimeZone(id = "australian_central_daylight_time", nameRes = "Australian Central Daylight Time", offsetSeconds = 37800),
|
||||||
|
"australian_central_standard_time" to UnittoTimeZone(id = "australian_central_standard_time", nameRes = "Australian Central Standard Time", offsetSeconds = 34200),
|
||||||
|
"acre_time" to UnittoTimeZone(id = "acre_time", nameRes = "Acre Time", offsetSeconds = -18000),
|
||||||
|
"australian_central_western_standard_time" to UnittoTimeZone(id = "australian_central_western_standard_time", nameRes = "Australian Central Western Standard Time", offsetSeconds = 31500),
|
||||||
|
"arabia_daylight_time" to UnittoTimeZone(id = "arabia_daylight_time", nameRes = "Arabia Daylight Time", offsetSeconds = 14400),
|
||||||
|
"atlantic_daylight_time" to UnittoTimeZone(id = "atlantic_daylight_time", nameRes = "Atlantic Daylight Time", offsetSeconds = -10800),
|
||||||
|
"heure_avanc_e_de_latlantique_french" to UnittoTimeZone(id = "heure_avanc_e_de_latlantique_french", nameRes = "Heure Avanc-e de l'Atlantique (French)", offsetSeconds = -10800),
|
||||||
|
"australian_eastern_daylight_time" to UnittoTimeZone(id = "australian_eastern_daylight_time", nameRes = "Australian Eastern Daylight Time", offsetSeconds = 39600),
|
||||||
|
"eastern_daylight_time" to UnittoTimeZone(id = "eastern_daylight_time", nameRes = "Eastern Daylight Time", offsetSeconds = 39600),
|
||||||
|
"eastern_daylight_saving_time" to UnittoTimeZone(id = "eastern_daylight_saving_time", nameRes = "Eastern Daylight Saving Time", offsetSeconds = 39600),
|
||||||
|
"australian_eastern_standard_time" to UnittoTimeZone(id = "australian_eastern_standard_time", nameRes = "Australian Eastern Standard Time", offsetSeconds = 36000),
|
||||||
|
"eastern_standard_time" to UnittoTimeZone(id = "eastern_standard_time", nameRes = "Eastern Standard Time", offsetSeconds = 36000),
|
||||||
|
"australian_eastern_time" to UnittoTimeZone(id = "australian_eastern_time", nameRes = "Australian Eastern Time", offsetSeconds = 36000),
|
||||||
|
"afghanistan_time" to UnittoTimeZone(id = "afghanistan_time", nameRes = "Afghanistan Time", offsetSeconds = 16200),
|
||||||
|
"alaska_daylight_time" to UnittoTimeZone(id = "alaska_daylight_time", nameRes = "Alaska Daylight Time", offsetSeconds = -28800),
|
||||||
|
"alaska_standard_time" to UnittoTimeZone(id = "alaska_standard_time", nameRes = "Alaska Standard Time", offsetSeconds = -32400),
|
||||||
|
"alma_ata_time" to UnittoTimeZone(id = "alma_ata_time", nameRes = "Alma-Ata Time", offsetSeconds = 21600),
|
||||||
|
"amazon_summer_time" to UnittoTimeZone(id = "amazon_summer_time", nameRes = "Amazon Summer Time", offsetSeconds = -10800),
|
||||||
|
"armenia_daylight_time" to UnittoTimeZone(id = "armenia_daylight_time", nameRes = "Armenia Daylight Time", offsetSeconds = 18000),
|
||||||
|
"amazon_time" to UnittoTimeZone(id = "amazon_time", nameRes = "Amazon Time", offsetSeconds = -14400),
|
||||||
|
"armenia_time" to UnittoTimeZone(id = "armenia_time", nameRes = "Armenia Time", offsetSeconds = 14400),
|
||||||
|
"anadyr_summer_time" to UnittoTimeZone(id = "anadyr_summer_time", nameRes = "Anadyr Summer Time", offsetSeconds = 43200),
|
||||||
|
"anadyr_time" to UnittoTimeZone(id = "anadyr_time", nameRes = "Anadyr Time", offsetSeconds = 43200),
|
||||||
|
"aqtobe_time" to UnittoTimeZone(id = "aqtobe_time", nameRes = "Aqtobe Time", offsetSeconds = 18000),
|
||||||
|
"argentina_time" to UnittoTimeZone(id = "argentina_time", nameRes = "Argentina Time", offsetSeconds = -10800),
|
||||||
|
"arabic_standard_time" to UnittoTimeZone(id = "arabic_standard_time", nameRes = "Arabic Standard Time", offsetSeconds = 10800),
|
||||||
|
"atlantic_standard_time" to UnittoTimeZone(id = "atlantic_standard_time", nameRes = "Atlantic Standard Time", offsetSeconds = -14400),
|
||||||
|
"tiempo_est_ndar_del_atl_ntico_spanish" to UnittoTimeZone(id = "tiempo_est_ndar_del_atl_ntico_spanish", nameRes = "Tiempo Est-ndar del Atl-ntico (Spanish)", offsetSeconds = -14400),
|
||||||
|
"heure_normale_de_latlantique_french" to UnittoTimeZone(id = "heure_normale_de_latlantique_french", nameRes = "Heure Normale de l'Atlantique (French)", offsetSeconds = -14400),
|
||||||
|
"australian_western_daylight_time" to UnittoTimeZone(id = "australian_western_daylight_time", nameRes = "Australian Western Daylight Time", offsetSeconds = 32400),
|
||||||
|
"western_daylight_time" to UnittoTimeZone(id = "western_daylight_time", nameRes = "Western Daylight Time", offsetSeconds = 32400),
|
||||||
|
"western_summer_time" to UnittoTimeZone(id = "western_summer_time", nameRes = "Western Summer Time", offsetSeconds = 32400),
|
||||||
|
"australian_western_standard_time" to UnittoTimeZone(id = "australian_western_standard_time", nameRes = "Australian Western Standard Time", offsetSeconds = 28800),
|
||||||
|
"western_standard_time" to UnittoTimeZone(id = "western_standard_time", nameRes = "Western Standard Time", offsetSeconds = 28800),
|
||||||
|
"western_australia_time" to UnittoTimeZone(id = "western_australia_time", nameRes = "Western Australia Time", offsetSeconds = 28800),
|
||||||
|
"azores_summer_time" to UnittoTimeZone(id = "azores_summer_time", nameRes = "Azores Summer Time", offsetSeconds = 0),
|
||||||
|
"azores_daylight_time" to UnittoTimeZone(id = "azores_daylight_time", nameRes = "Azores Daylight Time", offsetSeconds = 0),
|
||||||
|
"azores_time" to UnittoTimeZone(id = "azores_time", nameRes = "Azores Time", offsetSeconds = -3600),
|
||||||
|
"azores_standard_time" to UnittoTimeZone(id = "azores_standard_time", nameRes = "Azores Standard Time", offsetSeconds = -3600),
|
||||||
|
"azerbaijan_summer_time" to UnittoTimeZone(id = "azerbaijan_summer_time", nameRes = "Azerbaijan Summer Time", offsetSeconds = 18000),
|
||||||
|
"azerbaijan_time" to UnittoTimeZone(id = "azerbaijan_time", nameRes = "Azerbaijan Time", offsetSeconds = 14400),
|
||||||
|
"anywhere_on_earth" to UnittoTimeZone(id = "anywhere_on_earth", nameRes = "Anywhere on Earth", offsetSeconds = -43200),
|
||||||
|
"bravo_time_zone" to UnittoTimeZone(id = "bravo_time_zone", nameRes = "Bravo Time Zone", offsetSeconds = 7200),
|
||||||
|
"brunei_darussalam_time" to UnittoTimeZone(id = "brunei_darussalam_time", nameRes = "Brunei Darussalam Time", offsetSeconds = 28800),
|
||||||
|
"brunei_time" to UnittoTimeZone(id = "brunei_time", nameRes = "Brunei Time", offsetSeconds = 28800),
|
||||||
|
"bolivia_time" to UnittoTimeZone(id = "bolivia_time", nameRes = "Bolivia Time", offsetSeconds = -14400),
|
||||||
|
"brasilia_summer_time" to UnittoTimeZone(id = "brasilia_summer_time", nameRes = "Brasilia Summer Time", offsetSeconds = -7200),
|
||||||
|
"brazil_summer_time" to UnittoTimeZone(id = "brazil_summer_time", nameRes = "Brazil Summer Time", offsetSeconds = -7200),
|
||||||
|
"brazilian_summer_time" to UnittoTimeZone(id = "brazilian_summer_time", nameRes = "Brazilian Summer Time", offsetSeconds = -7200),
|
||||||
|
"bras_lia_time" to UnittoTimeZone(id = "bras_lia_time", nameRes = "Bras-lia Time", offsetSeconds = -10800),
|
||||||
|
"brazil_time" to UnittoTimeZone(id = "brazil_time", nameRes = "Brazil Time", offsetSeconds = -10800),
|
||||||
|
"brazilian_time" to UnittoTimeZone(id = "brazilian_time", nameRes = "Brazilian Time", offsetSeconds = -10800),
|
||||||
|
"bangladesh_standard_time" to UnittoTimeZone(id = "bangladesh_standard_time", nameRes = "Bangladesh Standard Time", offsetSeconds = 21600),
|
||||||
|
"bougainville_standard_time" to UnittoTimeZone(id = "bougainville_standard_time", nameRes = "Bougainville Standard Time", offsetSeconds = 39600),
|
||||||
|
"british_summer_time" to UnittoTimeZone(id = "british_summer_time", nameRes = "British Summer Time", offsetSeconds = 3600),
|
||||||
|
"british_daylight_time" to UnittoTimeZone(id = "british_daylight_time", nameRes = "British Daylight Time", offsetSeconds = 3600),
|
||||||
|
"british_daylight_saving_time" to UnittoTimeZone(id = "british_daylight_saving_time", nameRes = "British Daylight Saving Time", offsetSeconds = 3600),
|
||||||
|
"bhutan_time" to UnittoTimeZone(id = "bhutan_time", nameRes = "Bhutan Time", offsetSeconds = 21600),
|
||||||
|
"charlie_time_zone" to UnittoTimeZone(id = "charlie_time_zone", nameRes = "Charlie Time Zone", offsetSeconds = 10800),
|
||||||
|
"casey_time" to UnittoTimeZone(id = "casey_time", nameRes = "Casey Time", offsetSeconds = 28800),
|
||||||
|
"central_africa_time" to UnittoTimeZone(id = "central_africa_time", nameRes = "Central Africa Time", offsetSeconds = 7200),
|
||||||
|
"cocos_islands_time" to UnittoTimeZone(id = "cocos_islands_time", nameRes = "Cocos Islands Time", offsetSeconds = 23400),
|
||||||
|
"central_daylight_time" to UnittoTimeZone(id = "central_daylight_time", nameRes = "Central Daylight Time", offsetSeconds = -18000),
|
||||||
|
"central_daylight_saving_time" to UnittoTimeZone(id = "central_daylight_saving_time", nameRes = "Central Daylight Saving Time", offsetSeconds = -18000),
|
||||||
|
"north_american_central_daylight_time" to UnittoTimeZone(id = "north_american_central_daylight_time", nameRes = "North American Central Daylight Time", offsetSeconds = -18000),
|
||||||
|
"heure_avanc_e_du_centre_french" to UnittoTimeZone(id = "heure_avanc_e_du_centre_french", nameRes = "Heure Avanc-e du Centre (French)", offsetSeconds = -18000),
|
||||||
|
"cuba_daylight_time" to UnittoTimeZone(id = "cuba_daylight_time", nameRes = "Cuba Daylight Time", offsetSeconds = -14400),
|
||||||
|
"central_european_summer_time" to UnittoTimeZone(id = "central_european_summer_time", nameRes = "Central European Summer Time", offsetSeconds = 7200),
|
||||||
|
"central_european_daylight_time" to UnittoTimeZone(id = "central_european_daylight_time", nameRes = "Central European Daylight Time", offsetSeconds = 7200),
|
||||||
|
"european_central_summer_time" to UnittoTimeZone(id = "european_central_summer_time", nameRes = "European Central Summer Time", offsetSeconds = 7200),
|
||||||
|
"mitteleurop_ische_sommerzeit_german" to UnittoTimeZone(id = "mitteleurop_ische_sommerzeit_german", nameRes = "Mitteleurop-ische Sommerzeit (German)", offsetSeconds = 7200),
|
||||||
|
"central_european_time" to UnittoTimeZone(id = "central_european_time", nameRes = "Central European Time", offsetSeconds = 3600),
|
||||||
|
"european_central_time" to UnittoTimeZone(id = "european_central_time", nameRes = "European Central Time", offsetSeconds = 3600),
|
||||||
|
"central_europe_time" to UnittoTimeZone(id = "central_europe_time", nameRes = "Central Europe Time", offsetSeconds = 3600),
|
||||||
|
"mitteleurop_ische_zeit_german" to UnittoTimeZone(id = "mitteleurop_ische_zeit_german", nameRes = "Mitteleurop-ische Zeit (German)", offsetSeconds = 3600),
|
||||||
|
"chatham_island_daylight_time" to UnittoTimeZone(id = "chatham_island_daylight_time", nameRes = "Chatham Island Daylight Time", offsetSeconds = 49500),
|
||||||
|
"chatham_daylight_time" to UnittoTimeZone(id = "chatham_daylight_time", nameRes = "Chatham Daylight Time", offsetSeconds = 49500),
|
||||||
|
"chatham_island_standard_time" to UnittoTimeZone(id = "chatham_island_standard_time", nameRes = "Chatham Island Standard Time", offsetSeconds = 45900),
|
||||||
|
"choibalsan_summer_time" to UnittoTimeZone(id = "choibalsan_summer_time", nameRes = "Choibalsan Summer Time", offsetSeconds = 32400),
|
||||||
|
"choibalsan_daylight_time" to UnittoTimeZone(id = "choibalsan_daylight_time", nameRes = "Choibalsan Daylight Time", offsetSeconds = 32400),
|
||||||
|
"choibalsan_daylight_saving_time" to UnittoTimeZone(id = "choibalsan_daylight_saving_time", nameRes = "Choibalsan Daylight Saving Time", offsetSeconds = 32400),
|
||||||
|
"choibalsan_time" to UnittoTimeZone(id = "choibalsan_time", nameRes = "Choibalsan Time", offsetSeconds = 28800),
|
||||||
|
"chuuk_time" to UnittoTimeZone(id = "chuuk_time", nameRes = "Chuuk Time", offsetSeconds = 36000),
|
||||||
|
"cayman_islands_daylight_saving_time" to UnittoTimeZone(id = "cayman_islands_daylight_saving_time", nameRes = "Cayman Islands Daylight Saving Time", offsetSeconds = -14400),
|
||||||
|
"cayman_islands_standard_time" to UnittoTimeZone(id = "cayman_islands_standard_time", nameRes = "Cayman Islands Standard Time", offsetSeconds = -18000),
|
||||||
|
"cayman_islands_time" to UnittoTimeZone(id = "cayman_islands_time", nameRes = "Cayman Islands Time", offsetSeconds = -18000),
|
||||||
|
"cook_island_time" to UnittoTimeZone(id = "cook_island_time", nameRes = "Cook Island Time", offsetSeconds = -36000),
|
||||||
|
"chile_summer_time" to UnittoTimeZone(id = "chile_summer_time", nameRes = "Chile Summer Time", offsetSeconds = -10800),
|
||||||
|
"chile_daylight_time" to UnittoTimeZone(id = "chile_daylight_time", nameRes = "Chile Daylight Time", offsetSeconds = -10800),
|
||||||
|
"chile_standard_time" to UnittoTimeZone(id = "chile_standard_time", nameRes = "Chile Standard Time", offsetSeconds = -14400),
|
||||||
|
"chile_time" to UnittoTimeZone(id = "chile_time", nameRes = "Chile Time", offsetSeconds = -14400),
|
||||||
|
"chile_standard_time" to UnittoTimeZone(id = "chile_standard_time", nameRes = "Chile Standard Time", offsetSeconds = -14400),
|
||||||
|
"colombia_time" to UnittoTimeZone(id = "colombia_time", nameRes = "Colombia Time", offsetSeconds = -18000),
|
||||||
|
"central_standard_time" to UnittoTimeZone(id = "central_standard_time", nameRes = "Central Standard Time", offsetSeconds = -21600),
|
||||||
|
"central_time" to UnittoTimeZone(id = "central_time", nameRes = "Central Time", offsetSeconds = -21600),
|
||||||
|
"north_american_central_standard_time" to UnittoTimeZone(id = "north_american_central_standard_time", nameRes = "North American Central Standard Time", offsetSeconds = -21600),
|
||||||
|
"tiempo_central_est_ndar_spanish" to UnittoTimeZone(id = "tiempo_central_est_ndar_spanish", nameRes = "Tiempo Central Est-ndar (Spanish)", offsetSeconds = -21600),
|
||||||
|
"heure_normale_du_centre_french" to UnittoTimeZone(id = "heure_normale_du_centre_french", nameRes = "Heure Normale du Centre (French)", offsetSeconds = -21600),
|
||||||
|
"china_standard_time" to UnittoTimeZone(id = "china_standard_time", nameRes = "China Standard Time", offsetSeconds = 28800),
|
||||||
|
"cuba_standard_time" to UnittoTimeZone(id = "cuba_standard_time", nameRes = "Cuba Standard Time", offsetSeconds = -18000),
|
||||||
|
"cape_verde_time" to UnittoTimeZone(id = "cape_verde_time", nameRes = "Cape Verde Time", offsetSeconds = -3600),
|
||||||
|
"christmas_island_time" to UnittoTimeZone(id = "christmas_island_time", nameRes = "Christmas Island Time", offsetSeconds = 25200),
|
||||||
|
"chamorro_standard_time" to UnittoTimeZone(id = "chamorro_standard_time", nameRes = "Chamorro Standard Time", offsetSeconds = 36000),
|
||||||
|
"guam_standard_time" to UnittoTimeZone(id = "guam_standard_time", nameRes = "Guam Standard Time", offsetSeconds = 36000),
|
||||||
|
"delta_time_zone" to UnittoTimeZone(id = "delta_time_zone", nameRes = "Delta Time Zone", offsetSeconds = 14400),
|
||||||
|
"davis_time" to UnittoTimeZone(id = "davis_time", nameRes = "Davis Time", offsetSeconds = 25200),
|
||||||
|
"dumont_durville_time" to UnittoTimeZone(id = "dumont_durville_time", nameRes = "Dumont-d'Urville Time", offsetSeconds = 36000),
|
||||||
|
"echo_time_zone" to UnittoTimeZone(id = "echo_time_zone", nameRes = "Echo Time Zone", offsetSeconds = 18000),
|
||||||
|
"easter_island_summer_time" to UnittoTimeZone(id = "easter_island_summer_time", nameRes = "Easter Island Summer Time", offsetSeconds = -18000),
|
||||||
|
"easter_island_daylight_time" to UnittoTimeZone(id = "easter_island_daylight_time", nameRes = "Easter Island Daylight Time", offsetSeconds = -18000),
|
||||||
|
"easter_island_standard_time" to UnittoTimeZone(id = "easter_island_standard_time", nameRes = "Easter Island Standard Time", offsetSeconds = -21600),
|
||||||
|
"eastern_africa_time" to UnittoTimeZone(id = "eastern_africa_time", nameRes = "Eastern Africa Time", offsetSeconds = 10800),
|
||||||
|
"east_africa_time" to UnittoTimeZone(id = "east_africa_time", nameRes = "East Africa Time", offsetSeconds = 10800),
|
||||||
|
"ecuador_time" to UnittoTimeZone(id = "ecuador_time", nameRes = "Ecuador Time", offsetSeconds = -18000),
|
||||||
|
"eastern_daylight_time" to UnittoTimeZone(id = "eastern_daylight_time", nameRes = "Eastern Daylight Time", offsetSeconds = -14400),
|
||||||
|
"eastern_daylight_savings_time" to UnittoTimeZone(id = "eastern_daylight_savings_time", nameRes = "Eastern Daylight Savings Time", offsetSeconds = -14400),
|
||||||
|
"north_american_eastern_daylight_time" to UnittoTimeZone(id = "north_american_eastern_daylight_time", nameRes = "North American Eastern Daylight Time", offsetSeconds = -14400),
|
||||||
|
"heure_avanc_e_de_lest_french" to UnittoTimeZone(id = "heure_avanc_e_de_lest_french", nameRes = "Heure Avanc-e de l'Est (French)", offsetSeconds = -14400),
|
||||||
|
"tiempo_de_verano_del_este_spanish" to UnittoTimeZone(id = "tiempo_de_verano_del_este_spanish", nameRes = "Tiempo de verano del Este (Spanish)", offsetSeconds = -14400),
|
||||||
|
"eastern_european_summer_time" to UnittoTimeZone(id = "eastern_european_summer_time", nameRes = "Eastern European Summer Time", offsetSeconds = 10800),
|
||||||
|
"eastern_european_daylight_time" to UnittoTimeZone(id = "eastern_european_daylight_time", nameRes = "Eastern European Daylight Time", offsetSeconds = 10800),
|
||||||
|
"osteurop_ische_sommerzeit_german" to UnittoTimeZone(id = "osteurop_ische_sommerzeit_german", nameRes = "Osteurop-ische Sommerzeit (German)", offsetSeconds = 10800),
|
||||||
|
"eastern_european_time" to UnittoTimeZone(id = "eastern_european_time", nameRes = "Eastern European Time", offsetSeconds = 7200),
|
||||||
|
"osteurop_ische_zeit_german" to UnittoTimeZone(id = "osteurop_ische_zeit_german", nameRes = "Osteurop-ische Zeit (German)", offsetSeconds = 7200),
|
||||||
|
"eastern_greenland_summer_time" to UnittoTimeZone(id = "eastern_greenland_summer_time", nameRes = "Eastern Greenland Summer Time", offsetSeconds = 0),
|
||||||
|
"east_greenland_summer_time" to UnittoTimeZone(id = "east_greenland_summer_time", nameRes = "East Greenland Summer Time", offsetSeconds = 0),
|
||||||
|
"east_greenland_time" to UnittoTimeZone(id = "east_greenland_time", nameRes = "East Greenland Time", offsetSeconds = -3600),
|
||||||
|
"eastern_greenland_time" to UnittoTimeZone(id = "eastern_greenland_time", nameRes = "Eastern Greenland Time", offsetSeconds = -3600),
|
||||||
|
"eastern_standard_time" to UnittoTimeZone(id = "eastern_standard_time", nameRes = "Eastern Standard Time", offsetSeconds = -18000),
|
||||||
|
"eastern_time_" to UnittoTimeZone(id = "eastern_time_", nameRes = "Eastern Time ", offsetSeconds = -18000),
|
||||||
|
"north_american_eastern_standard_time" to UnittoTimeZone(id = "north_american_eastern_standard_time", nameRes = "North American Eastern Standard Time", offsetSeconds = -18000),
|
||||||
|
"tiempo_del_este_spanish" to UnittoTimeZone(id = "tiempo_del_este_spanish", nameRes = "Tiempo del Este (Spanish)", offsetSeconds = -18000),
|
||||||
|
"heure_normale_de_lest_french" to UnittoTimeZone(id = "heure_normale_de_lest_french", nameRes = "Heure Normale de l'Est (French)", offsetSeconds = -18000),
|
||||||
|
"foxtrot_time_zone" to UnittoTimeZone(id = "foxtrot_time_zone", nameRes = "Foxtrot Time Zone", offsetSeconds = 21600),
|
||||||
|
"further_eastern_european_time" to UnittoTimeZone(id = "further_eastern_european_time", nameRes = "Further-Eastern European Time", offsetSeconds = 10800),
|
||||||
|
"fiji_summer_time" to UnittoTimeZone(id = "fiji_summer_time", nameRes = "Fiji Summer Time", offsetSeconds = 46800),
|
||||||
|
"fiji_daylight_time" to UnittoTimeZone(id = "fiji_daylight_time", nameRes = "Fiji Daylight Time", offsetSeconds = 46800),
|
||||||
|
"fiji_time" to UnittoTimeZone(id = "fiji_time", nameRes = "Fiji Time", offsetSeconds = 43200),
|
||||||
|
"falkland_islands_summer_time" to UnittoTimeZone(id = "falkland_islands_summer_time", nameRes = "Falkland Islands Summer Time", offsetSeconds = -10800),
|
||||||
|
"falkland_island_daylight_time" to UnittoTimeZone(id = "falkland_island_daylight_time", nameRes = "Falkland Island Daylight Time", offsetSeconds = -10800),
|
||||||
|
"falkland_island_time" to UnittoTimeZone(id = "falkland_island_time", nameRes = "Falkland Island Time", offsetSeconds = -14400),
|
||||||
|
"falkland_island_standard_time" to UnittoTimeZone(id = "falkland_island_standard_time", nameRes = "Falkland Island Standard Time", offsetSeconds = -14400),
|
||||||
|
"fernando_de_noronha_time" to UnittoTimeZone(id = "fernando_de_noronha_time", nameRes = "Fernando de Noronha Time", offsetSeconds = -7200),
|
||||||
|
"golf_time_zone" to UnittoTimeZone(id = "golf_time_zone", nameRes = "Golf Time Zone", offsetSeconds = 25200),
|
||||||
|
"galapagos_time" to UnittoTimeZone(id = "galapagos_time", nameRes = "Galapagos Time", offsetSeconds = -21600),
|
||||||
|
"gambier_time" to UnittoTimeZone(id = "gambier_time", nameRes = "Gambier Time", offsetSeconds = -32400),
|
||||||
|
"gambier_islands_time" to UnittoTimeZone(id = "gambier_islands_time", nameRes = "Gambier Islands Time", offsetSeconds = -32400),
|
||||||
|
"georgia_standard_time" to UnittoTimeZone(id = "georgia_standard_time", nameRes = "Georgia Standard Time", offsetSeconds = 14400),
|
||||||
|
"french_guiana_time" to UnittoTimeZone(id = "french_guiana_time", nameRes = "French Guiana Time", offsetSeconds = -10800),
|
||||||
|
"gilbert_island_time" to UnittoTimeZone(id = "gilbert_island_time", nameRes = "Gilbert Island Time", offsetSeconds = 43200),
|
||||||
|
"greenwich_mean_time" to UnittoTimeZone(id = "greenwich_mean_time", nameRes = "Greenwich Mean Time", offsetSeconds = 0),
|
||||||
|
"coordinated_universal_time" to UnittoTimeZone(id = "coordinated_universal_time", nameRes = "Coordinated Universal Time", offsetSeconds = 0),
|
||||||
|
"greenwich_time" to UnittoTimeZone(id = "greenwich_time", nameRes = "Greenwich Time", offsetSeconds = 0),
|
||||||
|
"gulf_standard_time" to UnittoTimeZone(id = "gulf_standard_time", nameRes = "Gulf Standard Time", offsetSeconds = 14400),
|
||||||
|
"south_georgia_time" to UnittoTimeZone(id = "south_georgia_time", nameRes = "South Georgia Time", offsetSeconds = -7200),
|
||||||
|
"guyana_time" to UnittoTimeZone(id = "guyana_time", nameRes = "Guyana Time", offsetSeconds = -14400),
|
||||||
|
"hotel_time_zone" to UnittoTimeZone(id = "hotel_time_zone", nameRes = "Hotel Time Zone", offsetSeconds = 28800),
|
||||||
|
"hawaii_aleutian_daylight_time" to UnittoTimeZone(id = "hawaii_aleutian_daylight_time", nameRes = "Hawaii-Aleutian Daylight Time", offsetSeconds = -32400),
|
||||||
|
"hawaii_daylight_time" to UnittoTimeZone(id = "hawaii_daylight_time", nameRes = "Hawaii Daylight Time", offsetSeconds = -32400),
|
||||||
|
"hong_kong_time" to UnittoTimeZone(id = "hong_kong_time", nameRes = "Hong Kong Time", offsetSeconds = 28800),
|
||||||
|
"hovd_summer_time" to UnittoTimeZone(id = "hovd_summer_time", nameRes = "Hovd Summer Time", offsetSeconds = 28800),
|
||||||
|
"hovd_daylight_time" to UnittoTimeZone(id = "hovd_daylight_time", nameRes = "Hovd Daylight Time", offsetSeconds = 28800),
|
||||||
|
"hovd_daylight_saving_time" to UnittoTimeZone(id = "hovd_daylight_saving_time", nameRes = "Hovd Daylight Saving Time", offsetSeconds = 28800),
|
||||||
|
"hovd_time" to UnittoTimeZone(id = "hovd_time", nameRes = "Hovd Time", offsetSeconds = 25200),
|
||||||
|
"hawaii_standard_time" to UnittoTimeZone(id = "hawaii_standard_time", nameRes = "Hawaii Standard Time", offsetSeconds = -36000),
|
||||||
|
"hawaii_aleutian_standard_time" to UnittoTimeZone(id = "hawaii_aleutian_standard_time", nameRes = "Hawaii-Aleutian Standard Time", offsetSeconds = -36000),
|
||||||
|
"india_time_zone" to UnittoTimeZone(id = "india_time_zone", nameRes = "India Time Zone", offsetSeconds = 32400),
|
||||||
|
"indochina_time" to UnittoTimeZone(id = "indochina_time", nameRes = "Indochina Time", offsetSeconds = 25200),
|
||||||
|
"israel_daylight_time" to UnittoTimeZone(id = "israel_daylight_time", nameRes = "Israel Daylight Time", offsetSeconds = 10800),
|
||||||
|
"indian_chagos_time" to UnittoTimeZone(id = "indian_chagos_time", nameRes = "Indian Chagos Time", offsetSeconds = 21600),
|
||||||
|
"iran_daylight_time" to UnittoTimeZone(id = "iran_daylight_time", nameRes = "Iran Daylight Time", offsetSeconds = 16200),
|
||||||
|
"iran_summer_time" to UnittoTimeZone(id = "iran_summer_time", nameRes = "Iran Summer Time", offsetSeconds = 16200),
|
||||||
|
"iran_daylight_time" to UnittoTimeZone(id = "iran_daylight_time", nameRes = "Iran Daylight Time", offsetSeconds = 16200),
|
||||||
|
"irkutsk_summer_time" to UnittoTimeZone(id = "irkutsk_summer_time", nameRes = "Irkutsk Summer Time", offsetSeconds = 32400),
|
||||||
|
"irkutsk_time" to UnittoTimeZone(id = "irkutsk_time", nameRes = "Irkutsk Time", offsetSeconds = 28800),
|
||||||
|
"iran_standard_time" to UnittoTimeZone(id = "iran_standard_time", nameRes = "Iran Standard Time", offsetSeconds = 12600),
|
||||||
|
"iran_time" to UnittoTimeZone(id = "iran_time", nameRes = "Iran Time", offsetSeconds = 12600),
|
||||||
|
"india_standard_time" to UnittoTimeZone(id = "india_standard_time", nameRes = "India Standard Time", offsetSeconds = 19800),
|
||||||
|
"india_time" to UnittoTimeZone(id = "india_time", nameRes = "India Time", offsetSeconds = 19800),
|
||||||
|
"indian_standard_time" to UnittoTimeZone(id = "indian_standard_time", nameRes = "Indian Standard Time", offsetSeconds = 19800),
|
||||||
|
"irish_standard_time" to UnittoTimeZone(id = "irish_standard_time", nameRes = "Irish Standard Time", offsetSeconds = 3600),
|
||||||
|
"irish_summer_time" to UnittoTimeZone(id = "irish_summer_time", nameRes = "Irish Summer Time", offsetSeconds = 3600),
|
||||||
|
"israel_standard_time" to UnittoTimeZone(id = "israel_standard_time", nameRes = "Israel Standard Time", offsetSeconds = 7200),
|
||||||
|
"japan_standard_time" to UnittoTimeZone(id = "japan_standard_time", nameRes = "Japan Standard Time", offsetSeconds = 32400),
|
||||||
|
"kilo_time_zone" to UnittoTimeZone(id = "kilo_time_zone", nameRes = "Kilo Time Zone", offsetSeconds = 36000),
|
||||||
|
"kyrgyzstan_time" to UnittoTimeZone(id = "kyrgyzstan_time", nameRes = "Kyrgyzstan Time", offsetSeconds = 21600),
|
||||||
|
"kosrae_time" to UnittoTimeZone(id = "kosrae_time", nameRes = "Kosrae Time", offsetSeconds = 39600),
|
||||||
|
"krasnoyarsk_summer_time" to UnittoTimeZone(id = "krasnoyarsk_summer_time", nameRes = "Krasnoyarsk Summer Time", offsetSeconds = 28800),
|
||||||
|
"krasnoyarsk_time" to UnittoTimeZone(id = "krasnoyarsk_time", nameRes = "Krasnoyarsk Time", offsetSeconds = 25200),
|
||||||
|
"korea_standard_time" to UnittoTimeZone(id = "korea_standard_time", nameRes = "Korea Standard Time", offsetSeconds = 32400),
|
||||||
|
"korean_standard_time" to UnittoTimeZone(id = "korean_standard_time", nameRes = "Korean Standard Time", offsetSeconds = 32400),
|
||||||
|
"korea_time" to UnittoTimeZone(id = "korea_time", nameRes = "Korea Time", offsetSeconds = 32400),
|
||||||
|
"kuybyshev_time" to UnittoTimeZone(id = "kuybyshev_time", nameRes = "Kuybyshev Time", offsetSeconds = 14400),
|
||||||
|
"samara_summer_time" to UnittoTimeZone(id = "samara_summer_time", nameRes = "Samara Summer Time", offsetSeconds = 14400),
|
||||||
|
"lima_time_zone" to UnittoTimeZone(id = "lima_time_zone", nameRes = "Lima Time Zone", offsetSeconds = 39600),
|
||||||
|
"lord_howe_daylight_time" to UnittoTimeZone(id = "lord_howe_daylight_time", nameRes = "Lord Howe Daylight Time", offsetSeconds = 39600),
|
||||||
|
"lord_howe_standard_time" to UnittoTimeZone(id = "lord_howe_standard_time", nameRes = "Lord Howe Standard Time", offsetSeconds = 37800),
|
||||||
|
"line_islands_time" to UnittoTimeZone(id = "line_islands_time", nameRes = "Line Islands Time", offsetSeconds = 50400),
|
||||||
|
"mike_time_zone" to UnittoTimeZone(id = "mike_time_zone", nameRes = "Mike Time Zone", offsetSeconds = 43200),
|
||||||
|
"magadan_summer_time" to UnittoTimeZone(id = "magadan_summer_time", nameRes = "Magadan Summer Time", offsetSeconds = 43200),
|
||||||
|
"magadan_island_summer_time" to UnittoTimeZone(id = "magadan_island_summer_time", nameRes = "Magadan Island Summer Time", offsetSeconds = 43200),
|
||||||
|
"magadan_time" to UnittoTimeZone(id = "magadan_time", nameRes = "Magadan Time", offsetSeconds = 39600),
|
||||||
|
"magadan_island_time" to UnittoTimeZone(id = "magadan_island_time", nameRes = "Magadan Island Time", offsetSeconds = 39600),
|
||||||
|
"marquesas_time" to UnittoTimeZone(id = "marquesas_time", nameRes = "Marquesas Time", offsetSeconds = -34200),
|
||||||
|
"mawson_time" to UnittoTimeZone(id = "mawson_time", nameRes = "Mawson Time", offsetSeconds = 18000),
|
||||||
|
"mountain_daylight_time" to UnittoTimeZone(id = "mountain_daylight_time", nameRes = "Mountain Daylight Time", offsetSeconds = -21600),
|
||||||
|
"mountain_daylight_saving_time" to UnittoTimeZone(id = "mountain_daylight_saving_time", nameRes = "Mountain Daylight Saving Time", offsetSeconds = -21600),
|
||||||
|
"north_american_mountain_daylight_time" to UnittoTimeZone(id = "north_american_mountain_daylight_time", nameRes = "North American Mountain Daylight Time", offsetSeconds = -21600),
|
||||||
|
"heure_avanc_e_des_rocheuses_french" to UnittoTimeZone(id = "heure_avanc_e_des_rocheuses_french", nameRes = "Heure Avanc-e des Rocheuses (French)", offsetSeconds = -21600),
|
||||||
|
"marshall_islands_time" to UnittoTimeZone(id = "marshall_islands_time", nameRes = "Marshall Islands Time", offsetSeconds = 43200),
|
||||||
|
"myanmar_time" to UnittoTimeZone(id = "myanmar_time", nameRes = "Myanmar Time", offsetSeconds = 23400),
|
||||||
|
"moscow_daylight_time" to UnittoTimeZone(id = "moscow_daylight_time", nameRes = "Moscow Daylight Time", offsetSeconds = 14400),
|
||||||
|
"moscow_summer_time" to UnittoTimeZone(id = "moscow_summer_time", nameRes = "Moscow Summer Time", offsetSeconds = 14400),
|
||||||
|
"moscow_standard_time" to UnittoTimeZone(id = "moscow_standard_time", nameRes = "Moscow Standard Time", offsetSeconds = 10800),
|
||||||
|
"moscow_time" to UnittoTimeZone(id = "moscow_time", nameRes = "Moscow Time", offsetSeconds = 10800),
|
||||||
|
"mountain_standard_time" to UnittoTimeZone(id = "mountain_standard_time", nameRes = "Mountain Standard Time", offsetSeconds = -25200),
|
||||||
|
"mountain_time" to UnittoTimeZone(id = "mountain_time", nameRes = "Mountain Time", offsetSeconds = -25200),
|
||||||
|
"north_american_mountain_standard_time" to UnittoTimeZone(id = "north_american_mountain_standard_time", nameRes = "North American Mountain Standard Time", offsetSeconds = -25200),
|
||||||
|
"heure_normale_des_rocheuses_french" to UnittoTimeZone(id = "heure_normale_des_rocheuses_french", nameRes = "Heure Normale des Rocheuses (French)", offsetSeconds = -25200),
|
||||||
|
"mauritius_time" to UnittoTimeZone(id = "mauritius_time", nameRes = "Mauritius Time", offsetSeconds = 14400),
|
||||||
|
"maldives_time" to UnittoTimeZone(id = "maldives_time", nameRes = "Maldives Time", offsetSeconds = 18000),
|
||||||
|
"malaysia_time" to UnittoTimeZone(id = "malaysia_time", nameRes = "Malaysia Time", offsetSeconds = 28800),
|
||||||
|
"malaysian_standard_time" to UnittoTimeZone(id = "malaysian_standard_time", nameRes = "Malaysian Standard Time", offsetSeconds = 28800),
|
||||||
|
"november_time_zone" to UnittoTimeZone(id = "november_time_zone", nameRes = "November Time Zone", offsetSeconds = -3600),
|
||||||
|
"new_caledonia_time" to UnittoTimeZone(id = "new_caledonia_time", nameRes = "New Caledonia Time", offsetSeconds = 39600),
|
||||||
|
"newfoundland_daylight_time" to UnittoTimeZone(id = "newfoundland_daylight_time", nameRes = "Newfoundland Daylight Time", offsetSeconds = -9000),
|
||||||
|
"heure_avanc_e_de_terre_neuve_french" to UnittoTimeZone(id = "heure_avanc_e_de_terre_neuve_french", nameRes = "Heure Avanc-e de Terre-Neuve (French)", offsetSeconds = -9000),
|
||||||
|
"norfolk_daylight_time" to UnittoTimeZone(id = "norfolk_daylight_time", nameRes = "Norfolk Daylight Time", offsetSeconds = 43200),
|
||||||
|
"norfolk_island_daylight_time" to UnittoTimeZone(id = "norfolk_island_daylight_time", nameRes = "Norfolk Island Daylight Time", offsetSeconds = 43200),
|
||||||
|
"norfolk_time" to UnittoTimeZone(id = "norfolk_time", nameRes = "Norfolk Time", offsetSeconds = 39600),
|
||||||
|
"norfolk_island_time" to UnittoTimeZone(id = "norfolk_island_time", nameRes = "Norfolk Island Time", offsetSeconds = 39600),
|
||||||
|
"novosibirsk_summer_time" to UnittoTimeZone(id = "novosibirsk_summer_time", nameRes = "Novosibirsk Summer Time", offsetSeconds = 25200),
|
||||||
|
"omsk_summer_time" to UnittoTimeZone(id = "omsk_summer_time", nameRes = "Omsk Summer Time", offsetSeconds = 25200),
|
||||||
|
"novosibirsk_time" to UnittoTimeZone(id = "novosibirsk_time", nameRes = "Novosibirsk Time", offsetSeconds = 25200),
|
||||||
|
"omsk_standard_time" to UnittoTimeZone(id = "omsk_standard_time", nameRes = "Omsk Standard Time", offsetSeconds = 25200),
|
||||||
|
"nepal_time" to UnittoTimeZone(id = "nepal_time", nameRes = "Nepal Time", offsetSeconds = 20700),
|
||||||
|
"nauru_time" to UnittoTimeZone(id = "nauru_time", nameRes = "Nauru Time", offsetSeconds = 43200),
|
||||||
|
"newfoundland_standard_time" to UnittoTimeZone(id = "newfoundland_standard_time", nameRes = "Newfoundland Standard Time", offsetSeconds = -12600),
|
||||||
|
"heure_normale_de_terre_neuve_french" to UnittoTimeZone(id = "heure_normale_de_terre_neuve_french", nameRes = "Heure Normale de Terre-Neuve (French)", offsetSeconds = -12600),
|
||||||
|
"niue_time" to UnittoTimeZone(id = "niue_time", nameRes = "Niue Time", offsetSeconds = -39600),
|
||||||
|
"new_zealand_daylight_time" to UnittoTimeZone(id = "new_zealand_daylight_time", nameRes = "New Zealand Daylight Time", offsetSeconds = 46800),
|
||||||
|
"new_zealand_standard_time" to UnittoTimeZone(id = "new_zealand_standard_time", nameRes = "New Zealand Standard Time", offsetSeconds = 43200),
|
||||||
|
"oscar_time_zone" to UnittoTimeZone(id = "oscar_time_zone", nameRes = "Oscar Time Zone", offsetSeconds = -7200),
|
||||||
|
"omsk_summer_time" to UnittoTimeZone(id = "omsk_summer_time", nameRes = "Omsk Summer Time", offsetSeconds = 25200),
|
||||||
|
"novosibirsk_summer_time" to UnittoTimeZone(id = "novosibirsk_summer_time", nameRes = "Novosibirsk Summer Time", offsetSeconds = 25200),
|
||||||
|
"omsk_standard_time" to UnittoTimeZone(id = "omsk_standard_time", nameRes = "Omsk Standard Time", offsetSeconds = 21600),
|
||||||
|
"omsk_time" to UnittoTimeZone(id = "omsk_time", nameRes = "Omsk Time", offsetSeconds = 21600),
|
||||||
|
"novosibirsk_time" to UnittoTimeZone(id = "novosibirsk_time", nameRes = "Novosibirsk Time", offsetSeconds = 21600),
|
||||||
|
"oral_time" to UnittoTimeZone(id = "oral_time", nameRes = "Oral Time", offsetSeconds = 18000),
|
||||||
|
"papa_time_zone" to UnittoTimeZone(id = "papa_time_zone", nameRes = "Papa Time Zone", offsetSeconds = -10800),
|
||||||
|
"pacific_daylight_time" to UnittoTimeZone(id = "pacific_daylight_time", nameRes = "Pacific Daylight Time", offsetSeconds = -25200),
|
||||||
|
"pacific_daylight_saving_time" to UnittoTimeZone(id = "pacific_daylight_saving_time", nameRes = "Pacific Daylight Saving Time", offsetSeconds = -25200),
|
||||||
|
"north_american_pacific_daylight_time" to UnittoTimeZone(id = "north_american_pacific_daylight_time", nameRes = "North American Pacific Daylight Time", offsetSeconds = -25200),
|
||||||
|
"heure_avanc_e_du_pacifique_french" to UnittoTimeZone(id = "heure_avanc_e_du_pacifique_french", nameRes = "Heure Avanc-e du Pacifique (French)", offsetSeconds = -25200),
|
||||||
|
"peru_time" to UnittoTimeZone(id = "peru_time", nameRes = "Peru Time", offsetSeconds = -18000),
|
||||||
|
"kamchatka_summer_time" to UnittoTimeZone(id = "kamchatka_summer_time", nameRes = "Kamchatka Summer Time", offsetSeconds = 43200),
|
||||||
|
"kamchatka_time" to UnittoTimeZone(id = "kamchatka_time", nameRes = "Kamchatka Time", offsetSeconds = 43200),
|
||||||
|
"petropavlovsk_kamchatski_time" to UnittoTimeZone(id = "petropavlovsk_kamchatski_time", nameRes = "Petropavlovsk-Kamchatski Time", offsetSeconds = 43200),
|
||||||
|
"papua_new_guinea_time" to UnittoTimeZone(id = "papua_new_guinea_time", nameRes = "Papua New Guinea Time", offsetSeconds = 36000),
|
||||||
|
"phoenix_island_time" to UnittoTimeZone(id = "phoenix_island_time", nameRes = "Phoenix Island Time", offsetSeconds = 46800),
|
||||||
|
"philippine_time" to UnittoTimeZone(id = "philippine_time", nameRes = "Philippine Time", offsetSeconds = 28800),
|
||||||
|
"philippine_standard_time" to UnittoTimeZone(id = "philippine_standard_time", nameRes = "Philippine Standard Time", offsetSeconds = 28800),
|
||||||
|
"pakistan_standard_time" to UnittoTimeZone(id = "pakistan_standard_time", nameRes = "Pakistan Standard Time", offsetSeconds = 18000),
|
||||||
|
"pakistan_time" to UnittoTimeZone(id = "pakistan_time", nameRes = "Pakistan Time", offsetSeconds = 18000),
|
||||||
|
"pierre_&_miquelon_daylight_time" to UnittoTimeZone(id = "pierre_&_miquelon_daylight_time", nameRes = "Pierre & Miquelon Daylight Time", offsetSeconds = -7200),
|
||||||
|
"pierre_&_miquelon_standard_time" to UnittoTimeZone(id = "pierre_&_miquelon_standard_time", nameRes = "Pierre & Miquelon Standard Time", offsetSeconds = -10800),
|
||||||
|
"pohnpei_standard_time" to UnittoTimeZone(id = "pohnpei_standard_time", nameRes = "Pohnpei Standard Time", offsetSeconds = 39600),
|
||||||
|
"pacific_standard_time" to UnittoTimeZone(id = "pacific_standard_time", nameRes = "Pacific Standard Time", offsetSeconds = -28800),
|
||||||
|
"pacific_time" to UnittoTimeZone(id = "pacific_time", nameRes = "Pacific Time", offsetSeconds = -28800),
|
||||||
|
"north_american_pacific_standard_time" to UnittoTimeZone(id = "north_american_pacific_standard_time", nameRes = "North American Pacific Standard Time", offsetSeconds = -28800),
|
||||||
|
"tiempo_del_pac_fico_spanish" to UnittoTimeZone(id = "tiempo_del_pac_fico_spanish", nameRes = "Tiempo del Pac-fico (Spanish)", offsetSeconds = -28800),
|
||||||
|
"heure_normale_du_pacifique_french" to UnittoTimeZone(id = "heure_normale_du_pacifique_french", nameRes = "Heure Normale du Pacifique (French)", offsetSeconds = -28800),
|
||||||
|
"pitcairn_standard_time" to UnittoTimeZone(id = "pitcairn_standard_time", nameRes = "Pitcairn Standard Time", offsetSeconds = -28800),
|
||||||
|
"palau_time" to UnittoTimeZone(id = "palau_time", nameRes = "Palau Time", offsetSeconds = 32400),
|
||||||
|
"paraguay_summer_time" to UnittoTimeZone(id = "paraguay_summer_time", nameRes = "Paraguay Summer Time", offsetSeconds = -10800),
|
||||||
|
"paraguay_time" to UnittoTimeZone(id = "paraguay_time", nameRes = "Paraguay Time", offsetSeconds = -14400),
|
||||||
|
"pyongyang_time" to UnittoTimeZone(id = "pyongyang_time", nameRes = "Pyongyang Time", offsetSeconds = 30600),
|
||||||
|
"pyongyang_standard_time" to UnittoTimeZone(id = "pyongyang_standard_time", nameRes = "Pyongyang Standard Time", offsetSeconds = 30600),
|
||||||
|
"quebec_time_zone" to UnittoTimeZone(id = "quebec_time_zone", nameRes = "Quebec Time Zone", offsetSeconds = -14400),
|
||||||
|
"qyzylorda_time" to UnittoTimeZone(id = "qyzylorda_time", nameRes = "Qyzylorda Time", offsetSeconds = 21600),
|
||||||
|
"romeo_time_zone" to UnittoTimeZone(id = "romeo_time_zone", nameRes = "Romeo Time Zone", offsetSeconds = -18000),
|
||||||
|
"reunion_time" to UnittoTimeZone(id = "reunion_time", nameRes = "Reunion Time", offsetSeconds = 14400),
|
||||||
|
"rothera_time" to UnittoTimeZone(id = "rothera_time", nameRes = "Rothera Time", offsetSeconds = -10800),
|
||||||
|
"sierra_time_zone" to UnittoTimeZone(id = "sierra_time_zone", nameRes = "Sierra Time Zone", offsetSeconds = -21600),
|
||||||
|
"sakhalin_time" to UnittoTimeZone(id = "sakhalin_time", nameRes = "Sakhalin Time", offsetSeconds = 39600),
|
||||||
|
"samara_time" to UnittoTimeZone(id = "samara_time", nameRes = "Samara Time", offsetSeconds = 14400),
|
||||||
|
"samara_standard_time" to UnittoTimeZone(id = "samara_standard_time", nameRes = "Samara Standard Time", offsetSeconds = 14400),
|
||||||
|
"south_africa_standard_time" to UnittoTimeZone(id = "south_africa_standard_time", nameRes = "South Africa Standard Time", offsetSeconds = 7200),
|
||||||
|
"south_african_standard_time" to UnittoTimeZone(id = "south_african_standard_time", nameRes = "South African Standard Time", offsetSeconds = 7200),
|
||||||
|
"solomon_islands_time" to UnittoTimeZone(id = "solomon_islands_time", nameRes = "Solomon Islands Time", offsetSeconds = 39600),
|
||||||
|
"solomon_island_time" to UnittoTimeZone(id = "solomon_island_time", nameRes = "Solomon Island Time", offsetSeconds = 39600),
|
||||||
|
"seychelles_time" to UnittoTimeZone(id = "seychelles_time", nameRes = "Seychelles Time", offsetSeconds = 14400),
|
||||||
|
"singapore_time" to UnittoTimeZone(id = "singapore_time", nameRes = "Singapore Time", offsetSeconds = 28800),
|
||||||
|
"singapore_standard_time" to UnittoTimeZone(id = "singapore_standard_time", nameRes = "Singapore Standard Time", offsetSeconds = 28800),
|
||||||
|
"srednekolymsk_time" to UnittoTimeZone(id = "srednekolymsk_time", nameRes = "Srednekolymsk Time", offsetSeconds = 39600),
|
||||||
|
"suriname_time" to UnittoTimeZone(id = "suriname_time", nameRes = "Suriname Time", offsetSeconds = -10800),
|
||||||
|
"samoa_standard_time" to UnittoTimeZone(id = "samoa_standard_time", nameRes = "Samoa Standard Time", offsetSeconds = -39600),
|
||||||
|
"syowa_time" to UnittoTimeZone(id = "syowa_time", nameRes = "Syowa Time", offsetSeconds = 10800),
|
||||||
|
"tango_time_zone" to UnittoTimeZone(id = "tango_time_zone", nameRes = "Tango Time Zone", offsetSeconds = -25200),
|
||||||
|
"tahiti_time" to UnittoTimeZone(id = "tahiti_time", nameRes = "Tahiti Time", offsetSeconds = -36000),
|
||||||
|
"french_southern_and_antarctic_time" to UnittoTimeZone(id = "french_southern_and_antarctic_time", nameRes = "French Southern and Antarctic Time", offsetSeconds = 18000),
|
||||||
|
"kerguelen_islands_time" to UnittoTimeZone(id = "kerguelen_islands_time", nameRes = "Kerguelen (Islands) Time", offsetSeconds = 18000),
|
||||||
|
"tajikistan_time" to UnittoTimeZone(id = "tajikistan_time", nameRes = "Tajikistan Time", offsetSeconds = 18000),
|
||||||
|
"tokelau_time" to UnittoTimeZone(id = "tokelau_time", nameRes = "Tokelau Time", offsetSeconds = 46800),
|
||||||
|
"east_timor_time" to UnittoTimeZone(id = "east_timor_time", nameRes = "East Timor Time", offsetSeconds = 32400),
|
||||||
|
"turkmenistan_time" to UnittoTimeZone(id = "turkmenistan_time", nameRes = "Turkmenistan Time", offsetSeconds = 18000),
|
||||||
|
"tonga_summer_time" to UnittoTimeZone(id = "tonga_summer_time", nameRes = "Tonga Summer Time", offsetSeconds = 50400),
|
||||||
|
"tonga_time" to UnittoTimeZone(id = "tonga_time", nameRes = "Tonga Time", offsetSeconds = 46800),
|
||||||
|
"turkey_time" to UnittoTimeZone(id = "turkey_time", nameRes = "Turkey Time", offsetSeconds = 10800),
|
||||||
|
"tuvalu_time" to UnittoTimeZone(id = "tuvalu_time", nameRes = "Tuvalu Time", offsetSeconds = 43200),
|
||||||
|
"uniform_time_zone" to UnittoTimeZone(id = "uniform_time_zone", nameRes = "Uniform Time Zone", offsetSeconds = -28800),
|
||||||
|
"ulaanbaatar_summer_time" to UnittoTimeZone(id = "ulaanbaatar_summer_time", nameRes = "Ulaanbaatar Summer Time", offsetSeconds = 32400),
|
||||||
|
"ulan_bator_summer_time" to UnittoTimeZone(id = "ulan_bator_summer_time", nameRes = "Ulan Bator Summer Time", offsetSeconds = 32400),
|
||||||
|
"ulaanbaatar_time" to UnittoTimeZone(id = "ulaanbaatar_time", nameRes = "Ulaanbaatar Time", offsetSeconds = 28800),
|
||||||
|
"ulan_bator_time" to UnittoTimeZone(id = "ulan_bator_time", nameRes = "Ulan Bator Time", offsetSeconds = 28800),
|
||||||
|
"coordinated_universal_time" to UnittoTimeZone(id = "coordinated_universal_time", nameRes = "Coordinated Universal Time", offsetSeconds = 0),
|
||||||
|
"uruguay_summer_time" to UnittoTimeZone(id = "uruguay_summer_time", nameRes = "Uruguay Summer Time", offsetSeconds = -7200),
|
||||||
|
"uruguay_time" to UnittoTimeZone(id = "uruguay_time", nameRes = "Uruguay Time", offsetSeconds = -10800),
|
||||||
|
"uzbekistan_time" to UnittoTimeZone(id = "uzbekistan_time", nameRes = "Uzbekistan Time", offsetSeconds = 18000),
|
||||||
|
"victor_time_zone" to UnittoTimeZone(id = "victor_time_zone", nameRes = "Victor Time Zone", offsetSeconds = -32400),
|
||||||
|
"venezuelan_standard_time" to UnittoTimeZone(id = "venezuelan_standard_time", nameRes = "Venezuelan Standard Time", offsetSeconds = -14400),
|
||||||
|
"hora_legal_de_venezuela_spanish" to UnittoTimeZone(id = "hora_legal_de_venezuela_spanish", nameRes = "Hora Legal de Venezuela (Spanish)", offsetSeconds = -14400),
|
||||||
|
"vladivostok_summer_time" to UnittoTimeZone(id = "vladivostok_summer_time", nameRes = "Vladivostok Summer Time", offsetSeconds = 39600),
|
||||||
|
"vladivostok_time" to UnittoTimeZone(id = "vladivostok_time", nameRes = "Vladivostok Time", offsetSeconds = 36000),
|
||||||
|
"vostok_time" to UnittoTimeZone(id = "vostok_time", nameRes = "Vostok Time", offsetSeconds = 21600),
|
||||||
|
"vanuatu_time" to UnittoTimeZone(id = "vanuatu_time", nameRes = "Vanuatu Time", offsetSeconds = 39600),
|
||||||
|
"efate_time" to UnittoTimeZone(id = "efate_time", nameRes = "Efate Time", offsetSeconds = 39600),
|
||||||
|
"whiskey_time_zone" to UnittoTimeZone(id = "whiskey_time_zone", nameRes = "Whiskey Time Zone", offsetSeconds = -36000),
|
||||||
|
"wake_time" to UnittoTimeZone(id = "wake_time", nameRes = "Wake Time", offsetSeconds = 43200),
|
||||||
|
"western_argentine_summer_time" to UnittoTimeZone(id = "western_argentine_summer_time", nameRes = "Western Argentine Summer Time", offsetSeconds = -10800),
|
||||||
|
"west_africa_summer_time" to UnittoTimeZone(id = "west_africa_summer_time", nameRes = "West Africa Summer Time", offsetSeconds = 7200),
|
||||||
|
"west_africa_time" to UnittoTimeZone(id = "west_africa_time", nameRes = "West Africa Time", offsetSeconds = 3600),
|
||||||
|
"western_european_summer_time" to UnittoTimeZone(id = "western_european_summer_time", nameRes = "Western European Summer Time", offsetSeconds = 3600),
|
||||||
|
"western_european_daylight_time" to UnittoTimeZone(id = "western_european_daylight_time", nameRes = "Western European Daylight Time", offsetSeconds = 3600),
|
||||||
|
"westeurop_ische_sommerzeit_german" to UnittoTimeZone(id = "westeurop_ische_sommerzeit_german", nameRes = "Westeurop-ische Sommerzeit (German)", offsetSeconds = 3600),
|
||||||
|
"western_european_time" to UnittoTimeZone(id = "western_european_time", nameRes = "Western European Time", offsetSeconds = 0),
|
||||||
|
"greenwich_mean_time" to UnittoTimeZone(id = "greenwich_mean_time", nameRes = "Greenwich Mean Time", offsetSeconds = 0),
|
||||||
|
"westeurop_ische_zeit_german" to UnittoTimeZone(id = "westeurop_ische_zeit_german", nameRes = "Westeurop-ische Zeit (German)", offsetSeconds = 0),
|
||||||
|
"wallis_and_futuna_time" to UnittoTimeZone(id = "wallis_and_futuna_time", nameRes = "Wallis and Futuna Time", offsetSeconds = 43200),
|
||||||
|
"western_greenland_summer_time" to UnittoTimeZone(id = "western_greenland_summer_time", nameRes = "Western Greenland Summer Time", offsetSeconds = -7200),
|
||||||
|
"west_greenland_summer_time" to UnittoTimeZone(id = "west_greenland_summer_time", nameRes = "West Greenland Summer Time", offsetSeconds = -7200),
|
||||||
|
"west_greenland_time" to UnittoTimeZone(id = "west_greenland_time", nameRes = "West Greenland Time", offsetSeconds = -10800),
|
||||||
|
"western_greenland_time" to UnittoTimeZone(id = "western_greenland_time", nameRes = "Western Greenland Time", offsetSeconds = -10800),
|
||||||
|
"western_indonesian_time" to UnittoTimeZone(id = "western_indonesian_time", nameRes = "Western Indonesian Time", offsetSeconds = 25200),
|
||||||
|
"waktu_indonesia_barat" to UnittoTimeZone(id = "waktu_indonesia_barat", nameRes = "Waktu Indonesia Barat", offsetSeconds = 25200),
|
||||||
|
"eastern_indonesian_time" to UnittoTimeZone(id = "eastern_indonesian_time", nameRes = "Eastern Indonesian Time", offsetSeconds = 32400),
|
||||||
|
"waktu_indonesia_timur" to UnittoTimeZone(id = "waktu_indonesia_timur", nameRes = "Waktu Indonesia Timur", offsetSeconds = 32400),
|
||||||
|
"central_indonesian_time" to UnittoTimeZone(id = "central_indonesian_time", nameRes = "Central Indonesian Time", offsetSeconds = 28800),
|
||||||
|
"waktu_indonesia_tengah" to UnittoTimeZone(id = "waktu_indonesia_tengah", nameRes = "Waktu Indonesia Tengah", offsetSeconds = 28800),
|
||||||
|
"west_samoa_time" to UnittoTimeZone(id = "west_samoa_time", nameRes = "West Samoa Time", offsetSeconds = 46800),
|
||||||
|
"samoa_time" to UnittoTimeZone(id = "samoa_time", nameRes = "Samoa Time", offsetSeconds = 46800),
|
||||||
|
"western_sahara_summer_time" to UnittoTimeZone(id = "western_sahara_summer_time", nameRes = "Western Sahara Summer Time", offsetSeconds = 3600),
|
||||||
|
"western_sahara_standard_time" to UnittoTimeZone(id = "western_sahara_standard_time", nameRes = "Western Sahara Standard Time", offsetSeconds = 0),
|
||||||
|
"x_ray_time_zone" to UnittoTimeZone(id = "x_ray_time_zone", nameRes = "X-ray Time Zone", offsetSeconds = -39600),
|
||||||
|
"yankee_time_zone" to UnittoTimeZone(id = "yankee_time_zone", nameRes = "Yankee Time Zone", offsetSeconds = -43200),
|
||||||
|
"yakutsk_summer_time" to UnittoTimeZone(id = "yakutsk_summer_time", nameRes = "Yakutsk Summer Time", offsetSeconds = 36000),
|
||||||
|
"yakutsk_time" to UnittoTimeZone(id = "yakutsk_time", nameRes = "Yakutsk Time", offsetSeconds = 32400),
|
||||||
|
"yap_time" to UnittoTimeZone(id = "yap_time", nameRes = "Yap Time", offsetSeconds = 36000),
|
||||||
|
"yekaterinburg_summer_time" to UnittoTimeZone(id = "yekaterinburg_summer_time", nameRes = "Yekaterinburg Summer Time", offsetSeconds = 21600),
|
||||||
|
"yekaterinburg_time" to UnittoTimeZone(id = "yekaterinburg_time", nameRes = "Yekaterinburg Time", offsetSeconds = 18000),
|
||||||
|
"zulu_time_zone" to UnittoTimeZone(id = "zulu_time_zone", nameRes = "Zulu Time Zone", offsetSeconds = 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
val favoriteTimeZones: MutableStateFlow<List<UnittoTimeZone>> = MutableStateFlow(emptyList())
|
||||||
|
|
||||||
|
// UNCOMMENT FOR RELEASE
|
||||||
|
// val favoriteTimeZones: Flow<List<UnittoTimeZone>> = dao
|
||||||
|
// .getAll()
|
||||||
|
// .map { list ->
|
||||||
|
// val favorites = mutableListOf<UnittoTimeZone>()
|
||||||
|
// list.forEach { entity ->
|
||||||
|
// val foundTimeZone = allTimeZones[entity.id] ?: return@forEach
|
||||||
|
// val mapped = foundTimeZone.copy(
|
||||||
|
// position = entity.position
|
||||||
|
// )
|
||||||
|
// favorites.add(mapped)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// favorites
|
||||||
|
// }
|
||||||
|
|
||||||
|
suspend fun swapTimeZones(from: String, to: String) = withContext(Dispatchers.IO) {
|
||||||
|
// UNCOMMENT FOR RELEASE
|
||||||
|
// dao.swap(from, to)
|
||||||
|
|
||||||
|
favoriteTimeZones.update {
|
||||||
|
val fromIndex = it.indexOfFirst { it.id == from }
|
||||||
|
val toIndex = it.indexOfFirst { it.id == to }
|
||||||
|
|
||||||
|
it
|
||||||
|
.toMutableList()
|
||||||
|
.apply {
|
||||||
|
add(toIndex, removeAt(fromIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun delete(timeZone: UnittoTimeZone) = withContext(Dispatchers.IO) {
|
||||||
|
// UNCOMMENT FOR RELEASE
|
||||||
|
// // Only PrimaryKey is needed
|
||||||
|
// dao.remove(TimeZoneEntity(id = timeZone.id, position = 0))
|
||||||
|
|
||||||
|
favoriteTimeZones.update { it.minus(timeZone) }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun filterAllTimeZones(searchQuery: String): List<UnittoTimeZone> =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val query = searchQuery.trim().lowercase()
|
||||||
|
val threshold: Int = query.length / 2
|
||||||
|
val timeZonesWithDist = mutableListOf<Pair<UnittoTimeZone, Int>>()
|
||||||
|
|
||||||
|
allTimeZones.values.forEach { timeZone ->
|
||||||
|
val timeZoneName = timeZone.nameRes
|
||||||
|
|
||||||
|
if (timeZone.code.lowercase() == query) {
|
||||||
|
timeZonesWithDist.add(timeZone to 1)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
// not zero, so that lev can have that
|
||||||
|
timeZoneName.startsWith(query) -> {
|
||||||
|
timeZonesWithDist.add(timeZone to 1)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
timeZoneName.contains(query) -> {
|
||||||
|
timeZonesWithDist.add(timeZone to 2)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val levDist = timeZoneName
|
||||||
|
.substring(0, minOf(query.length, timeZoneName.length))
|
||||||
|
.lev(query)
|
||||||
|
|
||||||
|
if (levDist < threshold) {
|
||||||
|
timeZonesWithDist.add(timeZone to levDist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withContext timeZonesWithDist.sortedBy { it.second }.map { it.first }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addToFavorites(timeZone: UnittoTimeZone) {
|
||||||
|
// UNCOMMENT FOR RELEASE
|
||||||
|
// dao.insert(
|
||||||
|
// TimeZoneEntity(
|
||||||
|
// id = timeZone.id,
|
||||||
|
// position = System.currentTimeMillis().toInt()
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
favoriteTimeZones.update { it.plus(timeZone) }
|
||||||
|
}
|
||||||
|
}
|
@ -92,6 +92,7 @@ data class UIPreferences(
|
|||||||
val customColor: Color = Color.Unspecified,
|
val customColor: Color = Color.Unspecified,
|
||||||
val monetMode: MonetMode = MonetMode.TONAL_SPOT,
|
val monetMode: MonetMode = MonetMode.TONAL_SPOT,
|
||||||
val startingScreen: String = TopLevelDestinations.Converter.route,
|
val startingScreen: String = TopLevelDestinations.Converter.route,
|
||||||
|
val enableToolsExperiment: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class MainPreferences(
|
data class MainPreferences(
|
||||||
@ -153,6 +154,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
|||||||
val monetMode: MonetMode = preferences[PrefsKeys.MONET_MODE]?.let { MonetMode.valueOf(it) }
|
val monetMode: MonetMode = preferences[PrefsKeys.MONET_MODE]?.let { MonetMode.valueOf(it) }
|
||||||
?: MonetMode.TONAL_SPOT
|
?: MonetMode.TONAL_SPOT
|
||||||
val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Converter.route
|
val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Converter.route
|
||||||
|
val enableToolsExperiment: Boolean = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false
|
||||||
|
|
||||||
UIPreferences(
|
UIPreferences(
|
||||||
themingMode = themingMode,
|
themingMode = themingMode,
|
||||||
@ -160,7 +162,8 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
|||||||
enableAmoledTheme = enableAmoledTheme,
|
enableAmoledTheme = enableAmoledTheme,
|
||||||
customColor = customColor,
|
customColor = customColor,
|
||||||
monetMode = monetMode,
|
monetMode = monetMode,
|
||||||
startingScreen = startingScreen
|
startingScreen = startingScreen,
|
||||||
|
enableToolsExperiment = enableToolsExperiment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +231,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
|||||||
latestRightSideUnit = main.latestRightSideUnit,
|
latestRightSideUnit = main.latestRightSideUnit,
|
||||||
shownUnitGroups = main.shownUnitGroups,
|
shownUnitGroups = main.shownUnitGroups,
|
||||||
enableVibrations = main.enableVibrations,
|
enableVibrations = main.enableVibrations,
|
||||||
enableToolsExperiment = false,
|
enableToolsExperiment = ui.enableToolsExperiment,
|
||||||
startingScreen = ui.startingScreen,
|
startingScreen = ui.startingScreen,
|
||||||
radianMode = main.radianMode,
|
radianMode = main.radianMode,
|
||||||
unitConverterFavoritesOnly = main.unitConverterFavoritesOnly,
|
unitConverterFavoritesOnly = main.unitConverterFavoritesOnly,
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.feature.datedifference
|
package com.sadellie.unitto.feature.datedifference
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
@ -34,7 +33,6 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@ -62,8 +60,10 @@ internal fun DateDifferenceRoute(
|
|||||||
navigateToMenu = navigateToMenu,
|
navigateToMenu = navigateToMenu,
|
||||||
navigateToSettings = navigateToSettings,
|
navigateToSettings = navigateToSettings,
|
||||||
uiState = uiState.value,
|
uiState = uiState.value,
|
||||||
updateStart = viewModel::setStartTime,
|
setStartTime = viewModel::setStartTime,
|
||||||
updateEnd = viewModel::setEndTime
|
setEndTime = viewModel::setEndTime,
|
||||||
|
setStartDate = viewModel::setStartDate,
|
||||||
|
setEndDate = viewModel::setEndDate,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,12 +72,13 @@ internal fun DateDifferenceRoute(
|
|||||||
internal fun DateDifferenceScreen(
|
internal fun DateDifferenceScreen(
|
||||||
navigateToMenu: () -> Unit,
|
navigateToMenu: () -> Unit,
|
||||||
navigateToSettings: () -> Unit,
|
navigateToSettings: () -> Unit,
|
||||||
updateStart: (LocalDateTime) -> Unit,
|
setStartTime: (Int, Int) -> Unit,
|
||||||
updateEnd: (LocalDateTime) -> Unit,
|
setEndTime: (Int, Int) -> Unit,
|
||||||
|
setStartDate: (LocalDateTime) -> Unit,
|
||||||
|
setEndDate: (LocalDateTime) -> Unit,
|
||||||
uiState: UIState,
|
uiState: UIState,
|
||||||
) {
|
) {
|
||||||
var dialogState by remember { mutableStateOf(DialogState.NONE) }
|
var dialogState by remember { mutableStateOf(DialogState.NONE) }
|
||||||
val isVertical = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
|
||||||
|
|
||||||
UnittoScreenWithTopBar(
|
UnittoScreenWithTopBar(
|
||||||
title = { Text(stringResource(R.string.date_difference)) },
|
title = { Text(stringResource(R.string.date_difference)) },
|
||||||
@ -138,26 +139,26 @@ internal fun DateDifferenceScreen(
|
|||||||
when (dialogState) {
|
when (dialogState) {
|
||||||
DialogState.FROM -> {
|
DialogState.FROM -> {
|
||||||
TimePickerDialog(
|
TimePickerDialog(
|
||||||
localDateTime = uiState.start,
|
hour = uiState.start.hour,
|
||||||
|
minute = uiState.start.minute,
|
||||||
onDismiss = ::resetDialog,
|
onDismiss = ::resetDialog,
|
||||||
onConfirm = {
|
onConfirm = { hour, minute ->
|
||||||
updateStart(it)
|
setStartTime(hour, minute)
|
||||||
dialogState = DialogState.FROM_DATE
|
dialogState = DialogState.FROM_DATE
|
||||||
},
|
},
|
||||||
confirmLabel = stringResource(R.string.next_label),
|
confirmLabel = stringResource(R.string.next_label),
|
||||||
vertical = isVertical,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogState.FROM_TIME -> {
|
DialogState.FROM_TIME -> {
|
||||||
TimePickerDialog(
|
TimePickerDialog(
|
||||||
localDateTime = uiState.start,
|
hour = uiState.start.hour,
|
||||||
|
minute = uiState.start.minute,
|
||||||
onDismiss = ::resetDialog,
|
onDismiss = ::resetDialog,
|
||||||
onConfirm = {
|
onConfirm = { hour, minute ->
|
||||||
updateStart(it)
|
setStartTime(hour, minute)
|
||||||
resetDialog()
|
resetDialog()
|
||||||
},
|
},
|
||||||
vertical = isVertical,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +167,7 @@ internal fun DateDifferenceScreen(
|
|||||||
localDateTime = uiState.start,
|
localDateTime = uiState.start,
|
||||||
onDismiss = ::resetDialog,
|
onDismiss = ::resetDialog,
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
updateStart(it)
|
setStartDate(it)
|
||||||
resetDialog()
|
resetDialog()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -174,26 +175,26 @@ internal fun DateDifferenceScreen(
|
|||||||
|
|
||||||
DialogState.TO -> {
|
DialogState.TO -> {
|
||||||
TimePickerDialog(
|
TimePickerDialog(
|
||||||
localDateTime = uiState.end,
|
hour = uiState.end.hour,
|
||||||
|
minute = uiState.end.minute,
|
||||||
onDismiss = ::resetDialog,
|
onDismiss = ::resetDialog,
|
||||||
onConfirm = {
|
onConfirm = { hour, minute ->
|
||||||
updateEnd(it)
|
setEndTime(hour, minute)
|
||||||
dialogState = DialogState.TO_DATE
|
dialogState = DialogState.TO_DATE
|
||||||
},
|
},
|
||||||
confirmLabel = stringResource(R.string.next_label),
|
confirmLabel = stringResource(R.string.next_label),
|
||||||
vertical = isVertical,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogState.TO_TIME -> {
|
DialogState.TO_TIME -> {
|
||||||
TimePickerDialog(
|
TimePickerDialog(
|
||||||
localDateTime = uiState.end,
|
hour = uiState.end.hour,
|
||||||
|
minute = uiState.end.minute,
|
||||||
onDismiss = ::resetDialog,
|
onDismiss = ::resetDialog,
|
||||||
onConfirm = {
|
onConfirm = { hour, minute ->
|
||||||
updateEnd(it)
|
setEndTime(hour, minute)
|
||||||
resetDialog()
|
resetDialog()
|
||||||
},
|
},
|
||||||
vertical = isVertical,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +203,7 @@ internal fun DateDifferenceScreen(
|
|||||||
localDateTime = uiState.end,
|
localDateTime = uiState.end,
|
||||||
onDismiss = ::resetDialog,
|
onDismiss = ::resetDialog,
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
updateEnd(it)
|
setEndDate(it)
|
||||||
resetDialog()
|
resetDialog()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -222,10 +223,12 @@ private fun DateDifferenceScreenPreview() {
|
|||||||
DateDifferenceScreen(
|
DateDifferenceScreen(
|
||||||
navigateToMenu = {},
|
navigateToMenu = {},
|
||||||
navigateToSettings = {},
|
navigateToSettings = {},
|
||||||
updateStart = {},
|
setStartTime = { _, _ -> },
|
||||||
updateEnd = {},
|
setEndTime = { _, _ -> },
|
||||||
uiState = UIState(
|
uiState = UIState(
|
||||||
result = DateDifference.Default(4, 1, 2, 3, 4)
|
result = DateDifference.Default(4, 1, 2, 3, 4)
|
||||||
)
|
),
|
||||||
|
setStartDate = {},
|
||||||
|
setEndDate = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -46,9 +46,13 @@ internal class DateDifferenceViewModel @Inject constructor() : ViewModel() {
|
|||||||
viewModelScope, SharingStarted.WhileSubscribed(5000L), UIState()
|
viewModelScope, SharingStarted.WhileSubscribed(5000L), UIState()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun setStartTime(newTime: LocalDateTime) = _start.update { newTime }
|
fun setStartTime(hour: Int, minute: Int) = _start.update { it.withHour(hour).withMinute(minute) }
|
||||||
|
|
||||||
fun setEndTime(newTime: LocalDateTime) = _end.update { newTime }
|
fun setEndTime(hour: Int, minute: Int) = _end.update { it.withHour(hour).withMinute(minute) }
|
||||||
|
|
||||||
|
fun setStartDate(dateTime: LocalDateTime) = _start.update { dateTime }
|
||||||
|
|
||||||
|
fun setEndDate(dateTime: LocalDateTime) = _end.update { dateTime }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch(Dispatchers.Default) {
|
viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
@ -30,9 +30,11 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
implementation(libs.com.github.sadellie.themmo)
|
implementation(libs.com.github.sadellie.themmo)
|
||||||
|
implementation(libs.org.burnoutcrew.composereorderable)
|
||||||
|
|
||||||
implementation(project(mapOf("path" to ":data:common")))
|
implementation(project(mapOf("path" to ":data:common")))
|
||||||
implementation(project(mapOf("path" to ":data:userprefs")))
|
implementation(project(mapOf("path" to ":data:userprefs")))
|
||||||
implementation(project(mapOf("path" to ":data:database")))
|
implementation(project(mapOf("path" to ":data:database")))
|
||||||
|
implementation(project(mapOf("path" to ":data:timezone")))
|
||||||
implementation(project(mapOf("path" to ":data:model")))
|
implementation(project(mapOf("path" to ":data:model")))
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.timezone
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateColorAsState
|
||||||
|
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material3.Divider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.sadellie.unitto.core.base.R
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import com.sadellie.unitto.timezone.components.SelectableTimeZone
|
||||||
|
import com.sadellie.unitto.timezone.components.UnittoSearchBar
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun AddTimeZoneRoute(
|
||||||
|
viewModel: AddTimeZoneViewModel = hiltViewModel(),
|
||||||
|
navigateUp: () -> Unit,
|
||||||
|
userTime: ZonedDateTime? = null
|
||||||
|
) {
|
||||||
|
val uiState = viewModel.addTimeZoneUIState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
if (userTime == null) {
|
||||||
|
while (isActive) {
|
||||||
|
viewModel.setTime(ZonedDateTime.now())
|
||||||
|
delay(1000)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
viewModel.setTime(userTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTimeZoneScreen(
|
||||||
|
uiState = uiState.value,
|
||||||
|
navigateUp = navigateUp,
|
||||||
|
onQueryChange = viewModel::onQueryChange,
|
||||||
|
addToFavorites = viewModel::addToFavorites,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AddTimeZoneScreen(
|
||||||
|
uiState: AddTimeZoneUIState,
|
||||||
|
navigateUp: () -> Unit,
|
||||||
|
onQueryChange: (String) -> Unit,
|
||||||
|
addToFavorites: (UnittoTimeZone) -> Unit,
|
||||||
|
) {
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(
|
||||||
|
rememberTopAppBarState()
|
||||||
|
)
|
||||||
|
val elevatedColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
||||||
|
val needToTint by remember {
|
||||||
|
derivedStateOf { scrollBehavior.state.overlappedFraction > 0.01f }
|
||||||
|
}
|
||||||
|
|
||||||
|
val searchBarBackground = animateColorAsState(
|
||||||
|
targetValue = if (needToTint) elevatedColor else MaterialTheme.colorScheme.surface,
|
||||||
|
animationSpec = tween(durationMillis = 500, easing = LinearOutSlowInEasing),
|
||||||
|
label = "Search bar background"
|
||||||
|
)
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
|
UnittoSearchBar(
|
||||||
|
modifier = Modifier,
|
||||||
|
query = uiState.query,
|
||||||
|
onQueryChange = onQueryChange,
|
||||||
|
navigateUp = navigateUp,
|
||||||
|
title = stringResource(R.string.add_time_zone_title),
|
||||||
|
placeholder = stringResource(R.string.search_text_field_placeholder),
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
|
containerColor = searchBarBackground.value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { paddingValues ->
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.padding(paddingValues),
|
||||||
|
state = listState
|
||||||
|
) {
|
||||||
|
items(uiState.list) {
|
||||||
|
SelectableTimeZone(
|
||||||
|
timeZone = it,
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable { addToFavorites(it); navigateUp() }
|
||||||
|
.fillMaxWidth(),
|
||||||
|
currentTime = uiState.userTime
|
||||||
|
)
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun PreviewAddTimeZoneScreen() {
|
||||||
|
AddTimeZoneScreen(
|
||||||
|
navigateUp = {},
|
||||||
|
uiState = AddTimeZoneUIState(
|
||||||
|
list = List(50) {
|
||||||
|
UnittoTimeZone(
|
||||||
|
id = "timezone $it",
|
||||||
|
nameRes = "Time zone $it",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
onQueryChange = {},
|
||||||
|
addToFavorites = {},
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.timezone
|
||||||
|
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
data class AddTimeZoneUIState(
|
||||||
|
val query: String = "",
|
||||||
|
val list: List<UnittoTimeZone> = emptyList(),
|
||||||
|
val userTime: ZonedDateTime? = null,
|
||||||
|
)
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.timezone
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import com.sadellie.unitto.data.timezone.TimeZonesRepository
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class AddTimeZoneViewModel @Inject constructor(
|
||||||
|
private val timezonesRepository: TimeZonesRepository,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _userTime = MutableStateFlow<ZonedDateTime?>(ZonedDateTime.now())
|
||||||
|
private val _query = MutableStateFlow("")
|
||||||
|
|
||||||
|
private val _filteredTimeZones = MutableStateFlow(emptyList<UnittoTimeZone>())
|
||||||
|
val addTimeZoneUIState = combine(
|
||||||
|
_query,
|
||||||
|
_filteredTimeZones,
|
||||||
|
_userTime,
|
||||||
|
) { query, filteredTimeZone, userTime ->
|
||||||
|
return@combine AddTimeZoneUIState(
|
||||||
|
query = query,
|
||||||
|
list = filteredTimeZone,
|
||||||
|
userTime = userTime,
|
||||||
|
)
|
||||||
|
}.stateIn(
|
||||||
|
viewModelScope, SharingStarted.WhileSubscribed(5000), AddTimeZoneUIState()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun onQueryChange(query: String) {
|
||||||
|
_query.update { query }
|
||||||
|
filterTimeZones(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterTimeZones(query: String = "") = viewModelScope.launch {
|
||||||
|
_filteredTimeZones.update {
|
||||||
|
timezonesRepository.filterAllTimeZones(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addToFavorites(timeZone: UnittoTimeZone) = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
timezonesRepository.addToFavorites(timeZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTime(time: ZonedDateTime) = viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
_userTime.update { time }
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
// TODO Maybe only when actually needed?
|
||||||
|
filterTimeZones()
|
||||||
|
}
|
||||||
|
}
|
@ -18,10 +18,364 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.timezone
|
package com.sadellie.unitto.timezone
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.SizeTransform
|
||||||
|
import androidx.compose.animation.animateColor
|
||||||
|
import androidx.compose.animation.core.animateDp
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.core.updateTransition
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.scaleIn
|
||||||
|
import androidx.compose.animation.scaleOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.animation.togetherWith
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.AnchoredDraggableState
|
||||||
|
import androidx.compose.foundation.gestures.DraggableAnchors
|
||||||
|
import androidx.compose.foundation.gestures.animateTo
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.outlined.History
|
||||||
|
import androidx.compose.material3.FabPosition
|
||||||
|
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LargeFloatingActionButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.sadellie.unitto.core.base.R
|
||||||
|
import com.sadellie.unitto.core.ui.common.MenuButton
|
||||||
|
import com.sadellie.unitto.core.ui.common.SettingsButton
|
||||||
|
import com.sadellie.unitto.core.ui.common.TimePickerDialog
|
||||||
|
import com.sadellie.unitto.core.ui.common.UnittoScreenWithTopBar
|
||||||
|
import com.sadellie.unitto.core.ui.common.squashable
|
||||||
|
import com.sadellie.unitto.core.ui.datetime.formatDayMonthYear
|
||||||
|
import com.sadellie.unitto.core.ui.datetime.formatLocal
|
||||||
|
import com.sadellie.unitto.core.ui.datetime.formatTimeZoneOffset
|
||||||
|
import com.sadellie.unitto.core.ui.theme.AppTypography
|
||||||
|
import com.sadellie.unitto.core.ui.theme.DarkThemeColors
|
||||||
|
import com.sadellie.unitto.core.ui.theme.LightThemeColors
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import com.sadellie.unitto.timezone.components.TimeZoneListItem
|
||||||
|
import io.github.sadellie.themmo.Themmo
|
||||||
|
import io.github.sadellie.themmo.rememberThemmoController
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.burnoutcrew.reorderable.ReorderableItem
|
||||||
|
import org.burnoutcrew.reorderable.detectReorderAfterLongPress
|
||||||
|
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
|
||||||
|
import org.burnoutcrew.reorderable.reorderable
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun TimeZoneRoute() {}
|
internal fun TimeZoneRoute(
|
||||||
|
viewModel: TimeZoneViewModel = hiltViewModel(),
|
||||||
|
navigateToMenu: () -> Unit,
|
||||||
|
navigateToSettings: () -> Unit,
|
||||||
|
navigateToAddTimeZone: (ZonedDateTime?) -> Unit,
|
||||||
|
) {
|
||||||
|
val uiState = viewModel.timeZoneUIState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
TimeZoneScreen(
|
||||||
|
uiState = uiState.value,
|
||||||
|
navigateToMenu = navigateToMenu,
|
||||||
|
navigateToSettings = navigateToSettings,
|
||||||
|
navigateToAddTimeZone = {
|
||||||
|
navigateToAddTimeZone(
|
||||||
|
if (uiState.value.updateTime) null
|
||||||
|
else uiState.value.userTime
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onDragEnd = viewModel::onDragEnd,
|
||||||
|
onDelete = viewModel::onDelete,
|
||||||
|
setSelectedTime = viewModel::setCustomTime,
|
||||||
|
setCurrentTime = viewModel::setCurrentTime,
|
||||||
|
resetTime = viewModel::resetTime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TimeZoneScreen() {}
|
private fun TimeZoneScreen(
|
||||||
|
uiState: TimeZoneUIState,
|
||||||
|
navigateToMenu: () -> Unit,
|
||||||
|
navigateToSettings: () -> Unit,
|
||||||
|
navigateToAddTimeZone: () -> Unit,
|
||||||
|
onDragEnd: (String, String) -> Unit,
|
||||||
|
onDelete: (UnittoTimeZone) -> Unit,
|
||||||
|
setSelectedTime: (ZonedDateTime) -> Unit,
|
||||||
|
setCurrentTime: () -> Unit,
|
||||||
|
resetTime: () -> Unit,
|
||||||
|
) {
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
|
LaunchedEffect(uiState.updateTime) {
|
||||||
|
while (uiState.updateTime and isActive) {
|
||||||
|
setCurrentTime()
|
||||||
|
delay(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val copiedList = rememberUpdatedState(newValue = uiState.list) as MutableState
|
||||||
|
val state = rememberReorderableLazyListState(
|
||||||
|
onMove = onMove@{ from, to ->
|
||||||
|
// -1 because we use fake item. It fixes animation for the first item in list
|
||||||
|
copiedList.value = copiedList.value
|
||||||
|
.toMutableList()
|
||||||
|
.apply {
|
||||||
|
add(to.index - 1, removeAt(from.index - 1))
|
||||||
|
}
|
||||||
|
onDragEnd(from.key as String, to.key as String)
|
||||||
|
},
|
||||||
|
canDragOver = { draggedOver, _ ->
|
||||||
|
// Don't allow dragging over fake item
|
||||||
|
draggedOver.index > 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// TODO Unswipe on dragging
|
||||||
|
var swiped by remember<MutableState<UnittoTimeZone?>> { mutableStateOf(null) }
|
||||||
|
var showTimeSelector by rememberSaveable { mutableStateOf(false) }
|
||||||
|
val maxDrag = -with(LocalDensity.current) { 80.dp.toPx() }
|
||||||
|
|
||||||
|
UnittoScreenWithTopBar(
|
||||||
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
title = { Text(stringResource(R.string.time_zone_screen)) },
|
||||||
|
navigationIcon = { MenuButton(navigateToMenu) },
|
||||||
|
actions = { SettingsButton(navigateToSettings) },
|
||||||
|
floatingActionButton = {
|
||||||
|
LargeFloatingActionButton(navigateToAddTimeZone) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Add,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(FloatingActionButtonDefaults.LargeIconSize),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
floatingActionButtonPosition = FabPosition.Center,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
|
||||||
|
) { paddingValues ->
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
state = state.listState,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.reorderable(state)
|
||||||
|
.detectReorderAfterLongPress(state),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
contentPadding = PaddingValues(bottom = 124.dp)
|
||||||
|
) {
|
||||||
|
// This is a fake item. First item in list can not animated, so we do this magic fuckery
|
||||||
|
item {
|
||||||
|
UserTimeZone(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
userTime = uiState.userTime,
|
||||||
|
onClick = { showTimeSelector = true },
|
||||||
|
onResetClick = resetTime,
|
||||||
|
showReset = !uiState.updateTime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
items(copiedList.value, { it.id }) { item ->
|
||||||
|
ReorderableItem(state, key = item.id) { isDragging ->
|
||||||
|
val transition = updateTransition(isDragging, label = "draggedTransition")
|
||||||
|
val background by transition.animateColor(label = "background") {
|
||||||
|
if (it) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
}
|
||||||
|
val itemPadding by transition.animateDp(label = "itemPadding") {
|
||||||
|
if (it) 32.dp else 16.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val draggableState = rememberDraggableTimeZone(maxDrag) { swiped = item }
|
||||||
|
|
||||||
|
LaunchedEffect(swiped) {
|
||||||
|
if (swiped != item) scope.launch {
|
||||||
|
draggableState.animateTo(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeZoneListItem(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = itemPadding),
|
||||||
|
timeZone = item,
|
||||||
|
currentTime = uiState.userTime,
|
||||||
|
onDelete = onDelete,
|
||||||
|
color = background,
|
||||||
|
onSwipe = {},
|
||||||
|
draggableState = draggableState,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showTimeSelector) {
|
||||||
|
TimePickerDialog(
|
||||||
|
hour = uiState.userTime.hour,
|
||||||
|
minute = uiState.userTime.minute,
|
||||||
|
onConfirm = { hour, minute ->
|
||||||
|
setSelectedTime(
|
||||||
|
uiState.userTime
|
||||||
|
.withHour(hour)
|
||||||
|
.withMinute(minute)
|
||||||
|
)
|
||||||
|
showTimeSelector = false
|
||||||
|
},
|
||||||
|
onDismiss = { showTimeSelector = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun UserTimeZone(
|
||||||
|
modifier: Modifier,
|
||||||
|
userTime: ZonedDateTime,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onResetClick: () -> Unit,
|
||||||
|
showReset: Boolean,
|
||||||
|
) {
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.squashable(
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onResetClick,
|
||||||
|
cornerRadiusRange = 8.dp..32.dp,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
)
|
||||||
|
.background(MaterialTheme.colorScheme.tertiaryContainer)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
) {
|
||||||
|
Column(Modifier.weight(1f)) {
|
||||||
|
Text(
|
||||||
|
text = userTime.formatTimeZoneOffset(),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onTertiaryContainer
|
||||||
|
)
|
||||||
|
// TODO Swipe to increase, touch to set
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = userTime.formatLocal(),
|
||||||
|
label = "user time change",
|
||||||
|
transitionSpec = {
|
||||||
|
slideInVertically { height -> height } + fadeIn() togetherWith
|
||||||
|
slideOutVertically { height -> -height } + fadeOut() using
|
||||||
|
SizeTransform()
|
||||||
|
}
|
||||||
|
) { time ->
|
||||||
|
Text(
|
||||||
|
text = time,
|
||||||
|
style = MaterialTheme.typography.displayLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onTertiaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = userTime.formatDayMonthYear(),
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onTertiaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = showReset,
|
||||||
|
enter = scaleIn() + fadeIn(),
|
||||||
|
exit = scaleOut() + fadeOut(),
|
||||||
|
) {
|
||||||
|
IconButton(onResetClick) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.History,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onTertiaryContainer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun rememberDraggableTimeZone(
|
||||||
|
maxDrag: Float,
|
||||||
|
onSwipe: () -> Unit,
|
||||||
|
) = remember {
|
||||||
|
AnchoredDraggableState(
|
||||||
|
initialValue = false,
|
||||||
|
anchors = DraggableAnchors {
|
||||||
|
false at 0f
|
||||||
|
true at maxDrag
|
||||||
|
},
|
||||||
|
positionalThreshold = { it * 0.5f },
|
||||||
|
velocityThreshold = { maxDrag },
|
||||||
|
animationSpec = tween(),
|
||||||
|
confirmValueChange = {
|
||||||
|
onSwipe()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun PreviewTimeZoneScreen() {
|
||||||
|
Themmo(
|
||||||
|
themmoController = rememberThemmoController(
|
||||||
|
lightColorScheme = LightThemeColors,
|
||||||
|
darkColorScheme = DarkThemeColors,
|
||||||
|
),
|
||||||
|
typography = AppTypography,
|
||||||
|
) {
|
||||||
|
TimeZoneScreen(
|
||||||
|
uiState = TimeZoneUIState(
|
||||||
|
list = List(50) {
|
||||||
|
UnittoTimeZone(
|
||||||
|
id = "timezone $it",
|
||||||
|
nameRes = "Time zone $it",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
navigateToMenu = {},
|
||||||
|
navigateToSettings = {},
|
||||||
|
navigateToAddTimeZone = {},
|
||||||
|
onDragEnd = { _, _ -> },
|
||||||
|
onDelete = {},
|
||||||
|
setSelectedTime = {},
|
||||||
|
setCurrentTime = {},
|
||||||
|
resetTime = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.timezone
|
||||||
|
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
data class TimeZoneUIState(
|
||||||
|
val list: List<UnittoTimeZone> = emptyList(),
|
||||||
|
val userTime: ZonedDateTime = ZonedDateTime.now(),
|
||||||
|
val updateTime: Boolean = true,
|
||||||
|
)
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.timezone
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import com.sadellie.unitto.data.timezone.TimeZonesRepository
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class TimeZoneViewModel @Inject constructor(
|
||||||
|
private val timezonesRepository: TimeZonesRepository
|
||||||
|
): ViewModel() {
|
||||||
|
|
||||||
|
private val _userTime = MutableStateFlow(ZonedDateTime.now())
|
||||||
|
private val _updateTime = MutableStateFlow(true)
|
||||||
|
|
||||||
|
val timeZoneUIState = combine(
|
||||||
|
_userTime,
|
||||||
|
_updateTime,
|
||||||
|
timezonesRepository.favoriteTimeZones
|
||||||
|
) { userTime, updateTime, favorites ->
|
||||||
|
return@combine TimeZoneUIState(
|
||||||
|
list = favorites,
|
||||||
|
userTime = userTime,
|
||||||
|
updateTime = updateTime
|
||||||
|
)
|
||||||
|
}.stateIn(
|
||||||
|
viewModelScope, SharingStarted.WhileSubscribed(5000), TimeZoneUIState()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun onDragEnd(from: String, to: String) = viewModelScope.launch {
|
||||||
|
timezonesRepository.swapTimeZones(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDelete(timeZone: UnittoTimeZone) = viewModelScope.launch {
|
||||||
|
timezonesRepository.delete(timeZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCustomTime(time: ZonedDateTime) = viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
_updateTime.update { false }
|
||||||
|
_userTime.update { time }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetTime() = viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
_updateTime.update { true }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCurrentTime() = viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
_userTime.update { ZonedDateTime.now() }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.timezone.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.core.ui.datetime.formatLocal
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SelectableTimeZone(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
timeZone: UnittoTimeZone,
|
||||||
|
currentTime: ZonedDateTime?,
|
||||||
|
) {
|
||||||
|
val offsetTime: ZonedDateTime? by remember(currentTime) {
|
||||||
|
mutableStateOf(currentTime?.let { timeZone.offsetFrom(it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
ListItem(
|
||||||
|
modifier = modifier,
|
||||||
|
headlineContent = {
|
||||||
|
Text(text = timeZone.nameRes)
|
||||||
|
},
|
||||||
|
supportingContent = {
|
||||||
|
Text(text = timeZone.id)
|
||||||
|
},
|
||||||
|
trailingContent = {
|
||||||
|
Text(text = offsetTime?.formatLocal() ?: "", style = MaterialTheme.typography.headlineSmall)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF)
|
||||||
|
@Composable
|
||||||
|
fun PreviewSelectableTimeZone() {
|
||||||
|
SelectableTimeZone(
|
||||||
|
timeZone = UnittoTimeZone(
|
||||||
|
id = "text",
|
||||||
|
nameRes = "Time zone"
|
||||||
|
),
|
||||||
|
modifier = Modifier.width(440.dp),
|
||||||
|
currentTime = ZonedDateTime.now(),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.timezone.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.SizeTransform
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.togetherWith
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.gestures.AnchoredDraggableState
|
||||||
|
import androidx.compose.foundation.gestures.DraggableAnchors
|
||||||
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
|
import androidx.compose.foundation.gestures.anchoredDraggable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.core.ui.datetime.formatLocal
|
||||||
|
import com.sadellie.unitto.core.ui.datetime.formatOffset
|
||||||
|
import com.sadellie.unitto.data.model.UnittoTimeZone
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TimeZoneListItem(
|
||||||
|
modifier: Modifier,
|
||||||
|
timeZone: UnittoTimeZone,
|
||||||
|
currentTime: ZonedDateTime,
|
||||||
|
onClick: () -> Unit = {},
|
||||||
|
onDelete: (UnittoTimeZone) -> Unit = {},
|
||||||
|
color: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
onSwipe: (UnittoTimeZone) -> Unit = {},
|
||||||
|
draggableState: AnchoredDraggableState<Boolean>,
|
||||||
|
) {
|
||||||
|
val offsetTime by remember(currentTime) { mutableStateOf(timeZone.offsetFrom(currentTime)) }
|
||||||
|
val offsetTimeFormatted = offsetTime.formatOffset(currentTime)
|
||||||
|
|
||||||
|
// TODO Animate deleting
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.heightIn(72.dp)
|
||||||
|
.clip(RoundedCornerShape(25))
|
||||||
|
.anchoredDraggable(
|
||||||
|
state = draggableState,
|
||||||
|
orientation = Orientation.Horizontal,
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
// TODO Reveal animation
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable { onDelete(timeZone) }
|
||||||
|
.background(MaterialTheme.colorScheme.errorContainer)
|
||||||
|
.matchParentSize()
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterEnd)
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.size(32.dp),
|
||||||
|
imageVector = Icons.Outlined.Delete,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onErrorContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.offset {
|
||||||
|
IntOffset(
|
||||||
|
y = 0,
|
||||||
|
x = draggableState
|
||||||
|
.requireOffset()
|
||||||
|
.roundToInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.clickable {}
|
||||||
|
.background(color)
|
||||||
|
.matchParentSize()
|
||||||
|
.padding(12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Column(Modifier.weight(1f)) {
|
||||||
|
// TODO Name
|
||||||
|
Text(
|
||||||
|
text = timeZone.nameRes,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = offsetTimeFormatted != null,
|
||||||
|
label = "Nullable offset"
|
||||||
|
) {
|
||||||
|
Text(offsetTimeFormatted ?: "", style = MaterialTheme.typography.bodyMedium)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
// Time
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = offsetTime.formatLocal(),
|
||||||
|
label = "Time change",
|
||||||
|
transitionSpec = {
|
||||||
|
fadeIn() togetherWith fadeOut() using (SizeTransform(clip = false))
|
||||||
|
}
|
||||||
|
) { time ->
|
||||||
|
Text(time, style = MaterialTheme.typography.headlineMedium)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF)
|
||||||
|
@Composable
|
||||||
|
fun PreviewTimeZoneListItem() {
|
||||||
|
|
||||||
|
val maxDrag = -with(LocalDensity.current) { 80.dp.toPx() }
|
||||||
|
val draggableState = remember {
|
||||||
|
AnchoredDraggableState(
|
||||||
|
initialValue = false,
|
||||||
|
anchors = DraggableAnchors {
|
||||||
|
false at 0f
|
||||||
|
true at maxDrag
|
||||||
|
},
|
||||||
|
positionalThreshold = { 0f },
|
||||||
|
velocityThreshold = { 0f },
|
||||||
|
animationSpec = tween(),
|
||||||
|
confirmValueChange = { true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeZoneListItem(
|
||||||
|
modifier = Modifier,
|
||||||
|
timeZone = UnittoTimeZone(
|
||||||
|
id = "timezone",
|
||||||
|
offsetSeconds = -10800,
|
||||||
|
nameRes = "Time zone"
|
||||||
|
),
|
||||||
|
currentTime = ZonedDateTime.now(),
|
||||||
|
draggableState = draggableState
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.timezone.components
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.Crossfade
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActionScope
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material.icons.outlined.Clear
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarColors
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
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.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import com.sadellie.unitto.core.base.R
|
||||||
|
import com.sadellie.unitto.core.ui.common.NavigateUpButton
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UnittoSearchBar(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
query: String,
|
||||||
|
onQueryChange: (String) -> Unit,
|
||||||
|
navigateUp: () -> Unit,
|
||||||
|
title: String,
|
||||||
|
searchActions: @Composable (RowScope.() -> Unit) = {},
|
||||||
|
noSearchActions: @Composable (RowScope.() -> Unit) = {},
|
||||||
|
placeholder: String,
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||||
|
colors: TopAppBarColors = TopAppBarDefaults.topAppBarColors()
|
||||||
|
) {
|
||||||
|
var showSearch by remember { mutableStateOf(false) }
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
fun stagedNavigateUp() {
|
||||||
|
if (showSearch) {
|
||||||
|
// Search text field is open, need to close it and clear search query
|
||||||
|
showSearch = false
|
||||||
|
// focusManager.clearFocus()
|
||||||
|
onQueryChange("")
|
||||||
|
} else {
|
||||||
|
// No search text field is shown, can go back as usual
|
||||||
|
navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TopAppBar(
|
||||||
|
modifier = modifier,
|
||||||
|
title = {
|
||||||
|
Crossfade(showSearch) { _showSearch ->
|
||||||
|
if (_showSearch) {
|
||||||
|
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||||
|
|
||||||
|
SearchTextField(
|
||||||
|
modifier = Modifier
|
||||||
|
.focusRequester(focusRequester),
|
||||||
|
value = query,
|
||||||
|
placeholder = placeholder,
|
||||||
|
onValueChange = onQueryChange,
|
||||||
|
onSearch = {}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
style = MaterialTheme.typography.titleLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
NavigateUpButton { stagedNavigateUp() }
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
Crossfade(showSearch) { _showSearch ->
|
||||||
|
if (_showSearch) {
|
||||||
|
ClearButton(visible = query.isNotEmpty()) { onQueryChange("") }
|
||||||
|
searchActions()
|
||||||
|
} else {
|
||||||
|
SearchButton { showSearch = true }
|
||||||
|
noSearchActions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
colors = colors,
|
||||||
|
)
|
||||||
|
|
||||||
|
BackHandler { stagedNavigateUp() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SearchTextField(
|
||||||
|
modifier: Modifier,
|
||||||
|
value: String,
|
||||||
|
placeholder: String,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
onSearch: KeyboardActionScope.() -> Unit
|
||||||
|
) {
|
||||||
|
BasicTextField(
|
||||||
|
modifier = modifier,
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
singleLine = true,
|
||||||
|
textStyle = MaterialTheme.typography.titleLarge.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||||
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface),
|
||||||
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||||
|
keyboardActions = KeyboardActions(onSearch = onSearch),
|
||||||
|
decorationBox = { innerTextField ->
|
||||||
|
innerTextField()
|
||||||
|
// Showing placeholder only when there is query is empty
|
||||||
|
value.ifEmpty {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alpha(0.7f),
|
||||||
|
text = placeholder,
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SearchButton(
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
IconButton(onClick) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Search,
|
||||||
|
contentDescription = stringResource(R.string.search_button_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ClearButton(
|
||||||
|
visible: Boolean,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
IconButton(onClick = onClick) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = visible,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut()
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Clear,
|
||||||
|
contentDescription = stringResource(R.string.clear_input_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,24 +18,72 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.timezone.navigation
|
package com.sadellie.unitto.timezone.navigation
|
||||||
|
|
||||||
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.navigation
|
||||||
|
import androidx.navigation.navArgument
|
||||||
import androidx.navigation.navDeepLink
|
import androidx.navigation.navDeepLink
|
||||||
import com.sadellie.unitto.core.base.TopLevelDestinations
|
import com.sadellie.unitto.core.base.TopLevelDestinations
|
||||||
|
import com.sadellie.unitto.timezone.AddTimeZoneRoute
|
||||||
import com.sadellie.unitto.timezone.TimeZoneRoute
|
import com.sadellie.unitto.timezone.TimeZoneRoute
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
private val timeZoneRoute: String by lazy { TopLevelDestinations.TimeZone.route }
|
private val timeZoneGraph: String by lazy { TopLevelDestinations.TimeZone.route }
|
||||||
|
internal const val timeZoneRoute = "time_zone_route"
|
||||||
|
internal const val addTimeZoneRoute = "add_time_zone_route"
|
||||||
|
internal const val userTimeArg = "userTime"
|
||||||
|
|
||||||
|
fun NavController.navigateToAddTimeZone(
|
||||||
|
userTime: ZonedDateTime?
|
||||||
|
) {
|
||||||
|
val formattedTime = userTime
|
||||||
|
?.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
|
||||||
|
?.replace("/", "|") // this is so wrong
|
||||||
|
|
||||||
|
navigate("$addTimeZoneRoute/$formattedTime")
|
||||||
|
}
|
||||||
|
|
||||||
fun NavGraphBuilder.timeZoneScreen(
|
fun NavGraphBuilder.timeZoneScreen(
|
||||||
navigateToMenu: () -> Unit,
|
navigateToMenu: () -> Unit,
|
||||||
navigateToSettings: () -> Unit
|
navigateToSettings: () -> Unit,
|
||||||
|
navController: NavHostController,
|
||||||
) {
|
) {
|
||||||
composable(
|
navigation(
|
||||||
route = timeZoneRoute,
|
startDestination = timeZoneRoute,
|
||||||
deepLinks = listOf(
|
route = timeZoneGraph,
|
||||||
navDeepLink { uriPattern = "app://com.sadellie.unitto/$timeZoneRoute" }
|
deepLinks = listOf(navDeepLink { uriPattern = "app://com.sadellie.unitto/$timeZoneRoute" })
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
TimeZoneRoute()
|
composable(timeZoneRoute) {
|
||||||
|
TimeZoneRoute(
|
||||||
|
navigateToMenu = navigateToMenu,
|
||||||
|
navigateToSettings = navigateToSettings,
|
||||||
|
navigateToAddTimeZone = navController::navigateToAddTimeZone
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable(
|
||||||
|
route = "$addTimeZoneRoute/{$userTimeArg}",
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(userTimeArg) {
|
||||||
|
defaultValue = null
|
||||||
|
nullable = true
|
||||||
|
type = NavType.StringType
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) { stackEntry ->
|
||||||
|
val userTime = stackEntry.arguments
|
||||||
|
?.getString(userTimeArg)
|
||||||
|
?.replace("|", "/") // war crime, don't look
|
||||||
|
?.let { ZonedDateTime.parse(it, DateTimeFormatter.ISO_ZONED_DATE_TIME) }
|
||||||
|
|
||||||
|
AddTimeZoneRoute(
|
||||||
|
navigateUp = navController::navigateUp,
|
||||||
|
userTime = userTime
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,3 +35,4 @@ include(":data:database")
|
|||||||
include(":data:model")
|
include(":data:model")
|
||||||
include(":data:common")
|
include(":data:common")
|
||||||
include(":data:evaluatto")
|
include(":data:evaluatto")
|
||||||
|
include(":data:timezone")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user