Browse Source

Use DaySeperatorFormatter

misc/jme/add-logging-to-state-machine
ganfra 2 years ago
parent
commit
a5b5371fb6
  1. 1
      features/messages/build.gradle.kts
  2. 22
      features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt
  3. 21
      libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/DaySeparatorFormatter.kt
  4. 108
      libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt
  5. 34
      libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultDaySeparatorFormatter.kt
  6. 80
      libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatter.kt
  7. 40
      libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt

1
features/messages/build.gradle.kts

@ -39,6 +39,7 @@ dependencies { @@ -39,6 +39,7 @@ dependencies {
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.textcomposer)
implementation(projects.libraries.dateformatter)
implementation(libs.coil.compose)
implementation(libs.datetime)
implementation(libs.accompanist.flowlayout)

22
features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt

@ -18,30 +18,16 @@ package io.element.android.features.messages.timeline.factories.virtual @@ -18,30 +18,16 @@ package io.element.android.features.messages.timeline.factories.virtual
import io.element.android.features.messages.timeline.model.virtual.TimelineItemDaySeparatorModel
import io.element.android.features.messages.timeline.model.virtual.TimelineItemVirtualModel
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toJavaLocalDateTime
import kotlinx.datetime.toLocalDateTime
import io.element.android.libraries.dateformatter.DaySeparatorFormatter
import org.matrix.rustcomponents.sdk.VirtualTimelineItem
import java.time.format.DateTimeFormatter
import java.util.Locale
import javax.inject.Inject
class TimelineItemDaySeparatorFactory @Inject constructor() {
//TODO use proper formatter
private val locale: Locale = Locale.getDefault()
private val dateWithYearFormatter: DateTimeFormatter by lazy {
val pattern = android.text.format.DateFormat.getBestDateTimePattern(locale, "dd.MM.yyyy")
DateTimeFormatter.ofPattern(pattern)
}
class TimelineItemDaySeparatorFactory @Inject constructor(private val daySeparatorFormatter: DaySeparatorFormatter) {
fun create(virtualItem: VirtualTimelineItem.DayDivider): TimelineItemVirtualModel {
val tsInstant = Instant.fromEpochMilliseconds(virtualItem.ts.toLong())
val tsDateTime = tsInstant.toLocalDateTime(TimeZone.currentSystemDefault())
val formattedDate = daySeparatorFormatter.format(virtualItem.ts.toLong())
return TimelineItemDaySeparatorModel(
formattedDate = dateWithYearFormatter.format(tsDateTime.toJavaLocalDateTime())
formattedDate = formattedDate
)
}
}

21
libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/DaySeparatorFormatter.kt

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.dateformatter
interface DaySeparatorFormatter {
fun format(timestamp: Long): String
}

108
libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DateFormatters.kt

@ -0,0 +1,108 @@ @@ -0,0 +1,108 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.dateformatter.impl
import android.content.Context
import android.text.format.DateFormat
import android.text.format.DateUtils
import io.element.android.libraries.di.ApplicationContext
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toJavaLocalDate
import kotlinx.datetime.toJavaLocalDateTime
import java.time.Period
import java.time.format.DateTimeFormatter
import java.util.Locale
import javax.inject.Inject
import kotlin.math.absoluteValue
class DateFormatters @Inject constructor(
@ApplicationContext private val context: Context,
private val locale: Locale,
private val clock: Clock,
private val timeZone: TimeZone,
) {
private val hourFormatter by lazy {
if (DateFormat.is24HourFormat(context)) {
DateTimeFormatter.ofPattern("HH:mm", locale)
} else {
DateTimeFormatter.ofPattern("h:mm a", locale)
}
}
private val fullDateFormatter by lazy {
val pattern = if (DateFormat.is24HourFormat(context)) {
DateFormat.getBestDateTimePattern(locale, "EEE, d MMM yyyy HH:mm")
} else {
DateFormat.getBestDateTimePattern(locale, "EEE, d MMM yyyy h:mm a")
}
DateTimeFormatter.ofPattern(pattern, locale)
}
private val dateWithMonthFormatter: DateTimeFormatter by lazy {
val pattern = DateFormat.getBestDateTimePattern(locale, "d MMM")
DateTimeFormatter.ofPattern(pattern)
}
private val dateWithYearFormatter: DateTimeFormatter by lazy {
val pattern = DateFormat.getBestDateTimePattern(locale, "d MMM y")
DateTimeFormatter.ofPattern(pattern)
}
internal fun formatFullDate(localDateTime: LocalDateTime): String {
return fullDateFormatter.format(localDateTime.toJavaLocalDateTime())
}
internal fun formatHour(localDateTime: LocalDateTime): String {
return hourFormatter.format(localDateTime.toJavaLocalDateTime())
}
internal fun formatDateWithMonth(localDateTime: LocalDateTime): String {
return dateWithMonthFormatter.format(localDateTime.toJavaLocalDateTime())
}
internal fun formatDateWithYear(localDateTime: LocalDateTime): String {
return dateWithYearFormatter.format(localDateTime.toJavaLocalDateTime())
}
internal fun formatDate(
dateToFormat: LocalDateTime,
currentDate: LocalDateTime,
useRelative: Boolean
): String {
val period = Period.between(dateToFormat.date.toJavaLocalDate(), currentDate.date.toJavaLocalDate())
return if (period.years.absoluteValue >= 1) {
formatDateWithYear(dateToFormat)
} else if (useRelative && period.days.absoluteValue < 2 && period.months.absoluteValue < 1) {
getRelativeDay(dateToFormat.toInstant(timeZone).toEpochMilliseconds())
} else {
formatDateWithMonth(dateToFormat)
}
}
private fun getRelativeDay(ts: Long): String {
return DateUtils.getRelativeTimeSpanString(
ts,
clock.now().toEpochMilliseconds(),
DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_SHOW_WEEKDAY
).toString()
}
}

34
libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultDaySeparatorFormatter.kt

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.dateformatter.impl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.dateformatter.DaySeparatorFormatter
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultDaySeparatorFormatter @Inject constructor(
private val localDateTimeProvider: LocalDateTimeProvider,
private val dateFormatters: DateFormatters,
) : DaySeparatorFormatter {
override fun format(timestamp: Long): String {
val dateToFormat = localDateTimeProvider.providesFromTimestamp(timestamp)
return dateFormatters.formatDateWithYear(dateToFormat)
}
}

80
libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageFormatter.kt

@ -16,91 +16,33 @@ @@ -16,91 +16,33 @@
package io.element.android.libraries.dateformatter.impl
import android.text.format.DateFormat
import android.text.format.DateUtils
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.dateformatter.LastMessageFormatter
import io.element.android.libraries.di.AppScope
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toJavaLocalDate
import kotlinx.datetime.toJavaLocalDateTime
import kotlinx.datetime.toLocalDateTime
import java.time.Period
import java.time.format.DateTimeFormatter
import java.util.Locale
import javax.inject.Inject
import kotlin.math.absoluteValue
@ContributesBinding(AppScope::class)
class DefaultLastMessageFormatter @Inject constructor(
private val clock: Clock,
private val locale: Locale,
private val timezone: TimeZone,
private val localDateTimeProvider: LocalDateTimeProvider,
private val dateFormatters: DateFormatters,
) : LastMessageFormatter {
private val onlyTimeFormatter: DateTimeFormatter by lazy {
val pattern = DateFormat.getBestDateTimePattern(locale, "HH:mm") ?: "HH:mm"
DateTimeFormatter.ofPattern(pattern)
}
private val dateWithMonthFormatter: DateTimeFormatter by lazy {
val pattern = DateFormat.getBestDateTimePattern(locale, "d MMM") ?: "d MMM"
DateTimeFormatter.ofPattern(pattern)
}
private val dateWithYearFormatter: DateTimeFormatter by lazy {
val pattern = DateFormat.getBestDateTimePattern(locale, "dd.MM.yyyy") ?: "dd.MM.yyyy"
DateTimeFormatter.ofPattern(pattern)
}
override fun format(timestamp: Long?): String {
if (timestamp == null) return ""
val now: Instant = clock.now()
val tsInstant = Instant.fromEpochMilliseconds(timestamp)
val nowDateTime = now.toLocalDateTime(timezone)
val tsDateTime = tsInstant.toLocalDateTime(timezone)
val isSameDay = nowDateTime.date == tsDateTime.date
val currentDate = localDateTimeProvider.providesNow()
val dateToFormat = localDateTimeProvider.providesFromTimestamp(timestamp)
val isSameDay = currentDate.date == dateToFormat.date
return when {
isSameDay -> {
onlyTimeFormatter.format(tsDateTime.toJavaLocalDateTime())
dateFormatters.formatHour(dateToFormat)
}
else -> {
formatDate(tsDateTime, nowDateTime)
dateFormatters.formatDate(
dateToFormat = dateToFormat,
currentDate = currentDate,
useRelative = true
)
}
}
}
private fun formatDate(
date: LocalDateTime,
currentDate: LocalDateTime,
): String {
val period = Period.between(date.date.toJavaLocalDate(), currentDate.date.toJavaLocalDate())
return if (period.years.absoluteValue >= 1) {
formatDateWithYear(date)
} else if (period.days.absoluteValue < 2 && period.months.absoluteValue < 1) {
getRelativeDay(date.toInstant(timezone).toEpochMilliseconds())
} else {
formatDateWithMonth(date)
}
}
private fun formatDateWithMonth(localDateTime: LocalDateTime): String {
return dateWithMonthFormatter.format(localDateTime.toJavaLocalDateTime())
}
private fun formatDateWithYear(localDateTime: LocalDateTime): String {
return dateWithYearFormatter.format(localDateTime.toJavaLocalDateTime())
}
private fun getRelativeDay(ts: Long): String {
return DateUtils.getRelativeTimeSpanString(
ts,
clock.now().toEpochMilliseconds(),
DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_SHOW_WEEKDAY
)?.toString() ?: ""
}
}

40
libraries/dateformatter/src/main/kotlin/io/element/android/libraries/dateformatter/impl/LocalDateTimeProvider.kt

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.dateformatter.impl
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import javax.inject.Inject
class LocalDateTimeProvider @Inject constructor(
private val clock: Clock,
private val timezone: TimeZone,
) {
fun providesNow(): LocalDateTime {
val now: Instant = clock.now()
return now.toLocalDateTime(timezone)
}
fun providesFromTimestamp(timestamp: Long): LocalDateTime {
val tsInstant = Instant.fromEpochMilliseconds(timestamp)
return tsInstant.toLocalDateTime(timezone)
}
}
Loading…
Cancel
Save