1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-2797 Date as an object | Vault default settings (#1801)

This commit is contained in:
Konstantin Ivanov 2024-11-18 14:38:03 +01:00 committed by GitHub
parent 5c16df81ec
commit 18f85ad533
Signed by: github
GPG key ID: B5690EEEBB952194
28 changed files with 432 additions and 103 deletions

View file

@ -8,6 +8,7 @@ dependencies {
implementation project(':data')
implementation project(':domain')
implementation project(':localization')
implementation project(':core-models')
implementation libs.kotlin
implementation libs.coroutinesAndroid
@ -19,6 +20,7 @@ dependencies {
testImplementation libs.kotlinTest
testImplementation libs.androidXTestCore
testImplementation libs.robolectric
testImplementation libs.mockitoKotlin
compileOnly libs.javaxInject
}

View file

@ -0,0 +1,48 @@
package com.anytypeio.anytype.device.providers
import com.anytypeio.anytype.core_models.FALLBACK_DATE_PATTERN
import com.anytypeio.anytype.domain.misc.LocaleProvider
import java.text.DateFormat
import java.text.SimpleDateFormat
import javax.inject.Inject
import timber.log.Timber
/**
* Interface for providing the default date format pattern as a [String].
*/
interface AppDefaultDateFormatProvider {
/**
* Retrieves the default date format pattern based on the current locale.
*
* @return A date format pattern [String], e.g., "MM/dd/yyyy".
*/
fun provide(): String
}
class AppDefaultDateFormatProviderImpl @Inject constructor(
private val localeProvider: LocaleProvider
) : AppDefaultDateFormatProvider {
/**
* Provides the default date format pattern based on the current locale.
*
* @return The date format pattern as a [String]. If unable to retrieve the pattern,
* returns the [FALLBACK_DATE_PATTERN].
*/
override fun provide(): String {
return try {
val locale = localeProvider.locale()
val dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale)
if (dateFormat is SimpleDateFormat) {
dateFormat.toPattern()
} else {
Timber.e("DateFormat instance is not a SimpleDateFormat for locale: %s", locale)
FALLBACK_DATE_PATTERN
}
} catch (e: Exception) {
Timber.e(e, "Error while getting date format for locale")
FALLBACK_DATE_PATTERN
}
}
}

View file

@ -0,0 +1,179 @@
package com.anytypeio.anytype.device.providers
import android.text.format.DateUtils
import com.anytypeio.anytype.core_models.TimeInMillis
import com.anytypeio.anytype.core_models.TimeInSeconds
import com.anytypeio.anytype.domain.misc.DateProvider
import com.anytypeio.anytype.domain.misc.DateType
import com.anytypeio.anytype.domain.misc.LocaleProvider
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.temporal.ChronoUnit
import java.util.Date
import javax.inject.Inject
import timber.log.Timber
class DateProviderImpl @Inject constructor(
private val defaultZoneId: ZoneId,
private val localeProvider: LocaleProvider
) : DateProvider {
override fun calculateDateType(date: TimeInSeconds): DateType {
val dateInstant = Instant.ofEpochSecond(date)
val givenDate = dateInstant.atZone(defaultZoneId).toLocalDate()
val givenDateWithZeroTime = givenDate.atStartOfDay().toLocalDate()
return when (givenDateWithZeroTime) {
LocalDate.now() -> DateType.TODAY
LocalDate.now().plusDays(1) -> DateType.TOMORROW
LocalDate.now().minusDays(1) -> DateType.YESTERDAY
else -> {
val nowAtStartOfDay = LocalDate.now().atStartOfDay().toLocalDate()
val sevenDaysAgo = nowAtStartOfDay.minusDays(7)
val thirtyDaysAgo = nowAtStartOfDay.minusDays(30)
if (givenDateWithZeroTime > nowAtStartOfDay)
DateType.UNDEFINED
else {
if (givenDateWithZeroTime < thirtyDaysAgo) {
DateType.OLDER
} else {
if (givenDateWithZeroTime < sevenDaysAgo) {
DateType.PREVIOUS_THIRTY_DAYS
} else {
DateType.PREVIOUS_SEVEN_DAYS
}
}
}
}
}
}
override fun getCurrentTimestampInSeconds(): TimeInSeconds {
return System.currentTimeMillis() / 1000
}
override fun getTimestampForTodayAtStartOfDay(): TimeInSeconds {
val today = LocalDate.now()
val todayWithZeroTime = today.atStartOfDay().toLocalDate()
return todayWithZeroTime.atStartOfDay(defaultZoneId).toEpochSecond()
}
override fun getTimestampForTomorrowAtStartOfDay(): TimeInSeconds {
val tomorrow = LocalDate.now().plusDays(1)
val tomorrowWithZeroTime = tomorrow.atStartOfDay().toLocalDate()
return tomorrowWithZeroTime.atStartOfDay(defaultZoneId).toEpochSecond()
}
override fun getTimestampForYesterdayAtStartOfDay(): TimeInSeconds {
val yesterday = LocalDate.now().minusDays(1)
val yesterdayWithZeroTime = yesterday.atStartOfDay().toLocalDate()
return yesterdayWithZeroTime.atStartOfDay(defaultZoneId).toEpochSecond()
}
override fun getTimestampForWeekAheadAtStartOfDay(): TimeInSeconds {
val weekAfter = LocalDate.now().plusWeeks(1)
val weekAfterWithZeroTime = weekAfter.atStartOfDay().toLocalDate()
return weekAfterWithZeroTime.atStartOfDay(defaultZoneId).toEpochSecond()
}
override fun getTimestampForWeekAgoAtStartOfDay(): TimeInSeconds {
val weekAgo = LocalDate.now().minusWeeks(1)
val weekAgoWithZeroTime = weekAgo.atStartOfDay().toLocalDate()
return weekAgoWithZeroTime.atStartOfDay(defaultZoneId).toEpochSecond()
}
override fun getRelativeTimeSpanString(date: TimeInSeconds): CharSequence = DateUtils.getRelativeTimeSpanString(
date,
System.currentTimeMillis(),
DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE
)
override fun adjustToStartOfDayInUserTimeZone(timestamp: TimeInSeconds): TimeInMillis {
val instant = Instant.ofEpochSecond(timestamp)
val localDate = instant.atZone(defaultZoneId).toLocalDate()
val startOfDay = localDate.atStartOfDay()
return (startOfDay.toEpochSecond(ZoneOffset.UTC) * 1000)
}
override fun adjustFromStartOfDayInUserTimeZoneToUTC(timestamp: TimeInMillis, zoneId: ZoneId): TimeInSeconds {
// Convert the timestamp to an Instan
val instant = Instant.ofEpochSecond(timestamp)
// Convert the Instant to a ZonedDateTime in UTC
val utcDateTime = instant.atZone(ZoneOffset.UTC)
// Convert the UTC ZonedDateTime to the local time zone
val localDateTime = utcDateTime.withZoneSameInstant(zoneId)
// Get the local date and the start of the day in the local time zone
val localDate = localDateTime.toLocalDate()
val startOfDay = localDate.atStartOfDay(zoneId)
// Check if the UTC timestamp is at the boundary of the day in the local time zone
return when {
utcDateTime.toLocalDate().isAfter(startOfDay.toLocalDate()) -> {
// If the UTC timestamp is after the start of the day in the local time zone, return the start of the next day
startOfDay.plusDays(1).toEpochSecond()
}
utcDateTime.toLocalDate().isBefore(startOfDay.toLocalDate()) -> {
// If the UTC timestamp is before the start of the day in the local time zone, return the start of the previous day
startOfDay.minusDays(1).toEpochSecond()
}
else -> {
// Otherwise, return the start of the day
startOfDay.toEpochSecond()
}
}
}
override fun formatToDateString(timestamp: Long, pattern: String): String {
try {
val locale = localeProvider.locale()
val formatter = SimpleDateFormat(pattern, locale)
return formatter.format(Date(timestamp))
} catch (e: Exception) {
Timber.e(e,"Error formatting timestamp to date string")
return ""
}
}
override fun formatTimestampToDateAndTime(
timestamp: TimeInMillis,
dateStyle: Int,
timeStyle: Int
): Pair<String, String> {
return try {
val locale = localeProvider.locale()
val datePattern = (DateFormat.getDateInstance(dateStyle, locale) as SimpleDateFormat).toPattern()
val timePattern = (DateFormat.getTimeInstance(timeStyle, locale) as SimpleDateFormat).toPattern()
val dateFormatter = SimpleDateFormat(datePattern, locale)
val timeFormatter = SimpleDateFormat(timePattern, locale)
val date = Date(timestamp)
val dateString = dateFormatter.format(date)
val timeString = timeFormatter.format(date)
Pair(dateString, timeString)
} catch (e: Exception) {
Timber.e(e, "Error formatting timestamp to date and time string")
Pair("", "")
}
}
override fun isSameMinute(timestamp1: Long, timestamp2: Long): Boolean {
val dateTime1 = LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp1), defaultZoneId)
val dateTime2 = LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp2), defaultZoneId)
val truncatedDateTime1 = dateTime1.truncatedTo(ChronoUnit.MINUTES)
val truncatedDateTime2 = dateTime2.truncatedTo(ChronoUnit.MINUTES)
return truncatedDateTime1 == truncatedDateTime2
}
}

View file

@ -1,4 +1,4 @@
package com.anytypeio.anytype.device
package com.anytypeio.anytype.device.providers
import android.content.Context
import androidx.core.os.ConfigurationCompat

View file

@ -0,0 +1,191 @@
package com.anytypeio.anytype
import com.anytypeio.anytype.device.providers.DateProviderImpl
import com.anytypeio.anytype.domain.misc.DateProvider
import com.anytypeio.anytype.domain.misc.LocaleProvider
import java.time.ZoneId
import java.util.Locale
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
class DateProviderImplTest {
@Mock
lateinit var localeProvider: LocaleProvider
lateinit var dateProviderImpl: DateProvider
@Before
fun setUp() {
MockitoAnnotations.openMocks(this)
dateProviderImpl = DateProviderImpl(ZoneId.systemDefault(), localeProvider)
Mockito.`when`(localeProvider.locale()).thenReturn(Locale.getDefault())
}
@Test
fun adjustToStartOfDayInUserTimeZoneWithPastStart() {
val timeStamp = 1720828800L // Saturday, 13 July 2024 00:00:00
val tests = listOf(
Triple(timeStamp, ZoneId.of("UTC"), 1720828800L),
Triple(timeStamp, ZoneId.of("GMT+1"), 1720825200L),
Triple(timeStamp, ZoneId.of("GMT-1"), 1720832400L),
Triple(timeStamp, ZoneId.of("GMT+2"), 1720821600L),
Triple(timeStamp, ZoneId.of("GMT-2"), 1720836000L),
Triple(timeStamp, ZoneId.of("GMT+3"), 1720818000L),
Triple(timeStamp, ZoneId.of("GMT-3"), 1720839600L),
Triple(timeStamp, ZoneId.of("GMT+4"), 1720814400L),
Triple(timeStamp, ZoneId.of("GMT-4"), 1720843200L),
Triple(timeStamp, ZoneId.of("GMT+5"), 1720810800L),
Triple(timeStamp, ZoneId.of("GMT-5"), 1720846800L),
Triple(timeStamp, ZoneId.of("GMT+6"), 1720807200L),
Triple(timeStamp, ZoneId.of("GMT-6"), 1720850400L),
Triple(timeStamp, ZoneId.of("GMT+7"), 1720803600L),
Triple(timeStamp, ZoneId.of("GMT-7"), 1720854000L),
Triple(timeStamp, ZoneId.of("GMT+8"), 1720800000L),
Triple(timeStamp, ZoneId.of("GMT+08:45"), 1720797300L),
Triple(timeStamp, ZoneId.of("GMT-8"), 1720857600L),
Triple(timeStamp, ZoneId.of("GMT+9"), 1720796400L),
Triple(timeStamp, ZoneId.of("GMT+09:30"), 1720794600L),
Triple(timeStamp, ZoneId.of("GMT-9"), 1720861200L),
Triple(timeStamp, ZoneId.of("GMT+10"), 1720792800L),
Triple(timeStamp, ZoneId.of("GMT-10"), 1720864800L),
Triple(timeStamp, ZoneId.of("GMT+11"), 1720789200L),
Triple(timeStamp, ZoneId.of("GMT-11"), 1720868400L),
Triple(timeStamp, ZoneId.of("GMT+12"), 1720785600L),
Triple(timeStamp, ZoneId.of("GMT-12"), 1720872000L)
)
tests.forEach { (utcTimestamp, zoneId, expected) ->
val startOfDayInLocalZone =
dateProviderImpl.adjustFromStartOfDayInUserTimeZoneToUTC(utcTimestamp, zoneId)
assertEquals(expected, startOfDayInLocalZone)
}
}
@Test
fun adjustToStartOfDayInUserTimeZoneWithPastMidday() {
val timeStamp = 1720888800L // Saturday, 13 July 2024 16:40:00
val tests = listOf(
Triple(timeStamp, ZoneId.of("UTC"), 1720828800L),
Triple(timeStamp, ZoneId.of("GMT+1"), 1720825200L),
Triple(timeStamp, ZoneId.of("GMT-1"), 1720832400L),
Triple(timeStamp, ZoneId.of("GMT+2"), 1720821600L),
Triple(timeStamp, ZoneId.of("GMT-2"), 1720836000L),
Triple(timeStamp, ZoneId.of("GMT+3"), 1720818000L),
Triple(timeStamp, ZoneId.of("GMT-3"), 1720839600L),
Triple(timeStamp, ZoneId.of("GMT+4"), 1720814400L),
Triple(timeStamp, ZoneId.of("GMT-4"), 1720843200L),
Triple(timeStamp, ZoneId.of("GMT+5"), 1720810800L),
Triple(timeStamp, ZoneId.of("GMT-5"), 1720846800L),
Triple(timeStamp, ZoneId.of("GMT+6"), 1720807200L),
Triple(timeStamp, ZoneId.of("GMT-6"), 1720850400L),
Triple(timeStamp, ZoneId.of("GMT+7"), 1720803600L),
Triple(timeStamp, ZoneId.of("GMT-7"), 1720854000L),
Triple(timeStamp, ZoneId.of("GMT+8"), 1720800000L),
Triple(timeStamp, ZoneId.of("GMT+08:45"), 1720797300L),
Triple(timeStamp, ZoneId.of("GMT-8"), 1720857600L),
Triple(timeStamp, ZoneId.of("GMT+9"), 1720796400L),
Triple(timeStamp, ZoneId.of("GMT+09:30"), 1720794600L),
Triple(timeStamp, ZoneId.of("GMT-9"), 1720861200L),
Triple(timeStamp, ZoneId.of("GMT+10"), 1720792800L),
Triple(timeStamp, ZoneId.of("GMT-10"), 1720864800L),
Triple(timeStamp, ZoneId.of("GMT+11"), 1720789200L),
Triple(timeStamp, ZoneId.of("GMT-11"), 1720868400L),
Triple(timeStamp, ZoneId.of("GMT+12"), 1720785600L),
Triple(timeStamp, ZoneId.of("GMT-12"), 1720872000L)
)
tests.forEach { (utcTimestamp, zoneId, expected) ->
val startOfDayInLocalZone =
dateProviderImpl.adjustFromStartOfDayInUserTimeZoneToUTC(utcTimestamp, zoneId)
assertEquals(expected, startOfDayInLocalZone)
}
}
@Test
fun adjustToStartOfDayInUserTimeZoneWithPastEnd() {
val timeStamp = 1720915199L // Saturday, 13 July 2024 23:59:59
val tests = listOf(
Triple(timeStamp, ZoneId.of("UTC"), 1720828800L),
Triple(timeStamp, ZoneId.of("GMT+1"), 1720825200L),
Triple(timeStamp, ZoneId.of("GMT-1"), 1720832400L),
Triple(timeStamp, ZoneId.of("GMT+2"), 1720821600L),
Triple(timeStamp, ZoneId.of("GMT-2"), 1720836000L),
Triple(timeStamp, ZoneId.of("GMT+3"), 1720818000L),
Triple(timeStamp, ZoneId.of("GMT-3"), 1720839600L),
Triple(timeStamp, ZoneId.of("GMT+4"), 1720814400L),
Triple(timeStamp, ZoneId.of("GMT-4"), 1720843200L),
Triple(timeStamp, ZoneId.of("GMT+5"), 1720810800L),
Triple(timeStamp, ZoneId.of("GMT-5"), 1720846800L),
Triple(timeStamp, ZoneId.of("GMT+6"), 1720807200L),
Triple(timeStamp, ZoneId.of("GMT-6"), 1720850400L),
Triple(timeStamp, ZoneId.of("GMT+7"), 1720803600L),
Triple(timeStamp, ZoneId.of("GMT-7"), 1720854000L),
Triple(timeStamp, ZoneId.of("GMT+8"), 1720800000L),
Triple(timeStamp, ZoneId.of("GMT+08:45"), 1720797300L),
Triple(timeStamp, ZoneId.of("GMT-8"), 1720857600L),
Triple(timeStamp, ZoneId.of("GMT+9"), 1720796400L),
Triple(timeStamp, ZoneId.of("GMT+09:30"), 1720794600L),
Triple(timeStamp, ZoneId.of("GMT-9"), 1720861200L),
Triple(timeStamp, ZoneId.of("GMT+10"), 1720792800L),
Triple(timeStamp, ZoneId.of("GMT-10"), 1720864800L),
Triple(timeStamp, ZoneId.of("GMT+11"), 1720789200L),
Triple(timeStamp, ZoneId.of("GMT-11"), 1720868400L),
Triple(timeStamp, ZoneId.of("GMT+12"), 1720785600L),
Triple(timeStamp, ZoneId.of("GMT-12"), 1720872000L)
)
tests.forEach { (utcTimestamp, zoneId, expected) ->
val startOfDayInLocalZone =
dateProviderImpl.adjustFromStartOfDayInUserTimeZoneToUTC(utcTimestamp, zoneId)
assertEquals(expected, startOfDayInLocalZone)
}
}
@Test
fun adjustToStartOfDayInUserTimeZoneWithFuture() {
val timeStamp = 3720915199 // Saturday, 29 November 2087 03:33:19
val tests = listOf(
Triple(timeStamp, ZoneId.of("UTC"), 3720902400L),
Triple(timeStamp, ZoneId.of("GMT+1"), 3720898800L),
Triple(timeStamp, ZoneId.of("GMT+2"), 3720895200L),
Triple(timeStamp, ZoneId.of("GMT+3"), 3720891600L),
Triple(timeStamp, ZoneId.of("GMT+4"), 3720888000L),
Triple(timeStamp, ZoneId.of("GMT+5"), 3720884400L),
Triple(timeStamp, ZoneId.of("GMT+6"), 3720880800L),
Triple(timeStamp, ZoneId.of("GMT+7"), 3720877200L),
Triple(timeStamp, ZoneId.of("GMT+8"), 3720873600L),
Triple(timeStamp, ZoneId.of("GMT+9"), 3720870000L),
Triple(timeStamp, ZoneId.of("GMT+10"), 3720866400L),
Triple(timeStamp, ZoneId.of("GMT+11"), 3720862800L),
Triple(timeStamp, ZoneId.of("GMT+12"), 3720859200L),
Triple(timeStamp, ZoneId.of("GMT-1"), 3720906000L),
Triple(timeStamp, ZoneId.of("GMT-2"), 3720909600L),
Triple(timeStamp, ZoneId.of("GMT-3"), 3720913200L),
Triple(timeStamp, ZoneId.of("GMT-4"), 3720916800L),
Triple(timeStamp, ZoneId.of("GMT-5"), 3720920400L),
Triple(timeStamp, ZoneId.of("GMT-6"), 3720924000L),
Triple(timeStamp, ZoneId.of("GMT-7"), 3720927600L),
Triple(timeStamp, ZoneId.of("GMT-8"), 3720931200L),
Triple(timeStamp, ZoneId.of("GMT-9"), 3720934800L),
Triple(timeStamp, ZoneId.of("GMT-10"), 3720938400L),
Triple(timeStamp, ZoneId.of("GMT-11"), 3720942000L),
Triple(timeStamp, ZoneId.of("GMT-12"), 3720945600L)
)
tests.forEach { (utcTimestamp, zoneId, expected) ->
val startOfDayInLocalZone =
dateProviderImpl.adjustFromStartOfDayInUserTimeZoneToUTC(utcTimestamp, zoneId)
assertEquals(expected, startOfDayInLocalZone)
}
}
}