Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.nextcloud.client.database.entity.UploadEntity
import com.nextcloud.client.database.entity.toOCUpload
import com.nextcloud.client.database.entity.toUploadEntity
import com.nextcloud.utils.text.DisplayTextFormatter
import com.owncloud.android.R
import com.owncloud.android.utils.DisplayUtils
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
Expand Down Expand Up @@ -188,7 +188,7 @@ class UploadDateTests {
minResolution: Long = DateUtils.MINUTE_IN_MILLIS,
showFuture: Boolean = false
) {
val result = DisplayUtils.getRelativeDateTimeString(
val result = DisplayTextFormatter.formatRelativeDateTime(
context,
time,
minResolution,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import com.nextcloud.client.player.model.state.PlaybackState
import com.nextcloud.client.player.util.PlayerUtil.getPlaybackFile
import com.nextcloud.client.player.util.PlayerUtil.putPlaybackFile
import com.nextcloud.utils.HumanReadableFormatter
import com.nextcloud.utils.text.DisplayTextFormatter
import com.owncloud.android.R
import com.owncloud.android.databinding.PlayerAudioFileFragmentBinding
import com.owncloud.android.utils.DisplayUtils
import dagger.android.support.AndroidSupportInjection
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -138,7 +138,7 @@ open class AudioFileFragment :
).joinToString(DETAILS_SEPARATOR)

private fun getLastModifiedText(lastModified: Long): String {
val relativeTimestamp = DisplayUtils.getRelativeTimestamp(context, lastModified)
val relativeTimestamp = DisplayTextFormatter.formatRelativeTimestamp(requireContext(), lastModified)
return getString(R.string.player_last_modified, relativeTimestamp)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.core.AsyncRunner
import com.nextcloud.client.di.Injectable
import com.nextcloud.utils.SnackbarUtil
import com.nextcloud.utils.text.DisplayTextFormatter
import com.owncloud.android.R
import com.owncloud.android.databinding.SetStatusMessageBottomSheetBinding
import com.owncloud.android.datamodel.ArbitraryDataProvider
Expand All @@ -38,7 +39,6 @@ import com.owncloud.android.lib.resources.users.Status
import com.owncloud.android.ui.activity.BaseActivity
import com.owncloud.android.ui.adapter.PredefinedStatusClickListener
import com.owncloud.android.ui.adapter.PredefinedStatusListAdapter
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
import com.vanniktech.emoji.EmojiManager
import com.vanniktech.emoji.EmojiPopup
Expand Down Expand Up @@ -180,7 +180,7 @@ class SetStatusMessageBottomSheet(val user: User, val currentStatus: Status?) :
binding.remainingClearTime.apply {
binding.clearStatusMessageTextView.text = getString(R.string.clear)
visibility = View.VISIBLE
text = DisplayUtils.getRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true)
text = DisplayTextFormatter.formatRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true)
.toString()
.replaceFirstChar { it.lowercase(Locale.getDefault()) }
setOnClickListener {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.nextcloud.utils.SnackbarUtil
import com.nextcloud.utils.avatar.AvatarGenerationListener
import com.nextcloud.utils.avatar.AvatarGenerator
import com.nextcloud.utils.extensions.setVisibleIf
import com.nextcloud.utils.text.DisplayTextFormatter
import com.nextcloud.utils.thumbnail.ThumbnailArguments
import com.owncloud.android.R
import com.owncloud.android.databinding.FileActionsBottomSheetBinding
Expand Down Expand Up @@ -304,7 +305,8 @@ class FileActionsBottomSheet :
}

private fun getLockedUntilText(lockInfo: FileActionsViewModel.LockInfo): CharSequence {
val relativeTimestamp = DisplayUtils.getRelativeTimestamp(context, lockInfo.lockedUntil!!, true)
val relativeTimestamp =
DisplayTextFormatter.formatRelativeTimestamp(requireContext(), lockInfo.lockedUntil!!, true)
return getString(R.string.lock_expiration_info, relativeTimestamp)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,11 @@ enum class DateFormatPattern(val pattern: String) {
/**
* Aug 2026
*/
MonthWithYear("MMM yyyy")
MonthWithYear("MMM yyyy"),

FullMonthWithYear("MMMM yyyy"),

FullMonth("MMMM"),

Year("yyyy")
}
86 changes: 86 additions & 0 deletions app/src/main/java/com/nextcloud/utils/text/DisplayTextFormatter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.utils.text

import android.content.Context
import android.text.format.DateUtils
import com.nextcloud.client.account.User
import com.nextcloud.utils.HumanReadableFormatter
import com.nextcloud.utils.date.DateFormatPattern
import com.owncloud.android.MainApp
import com.owncloud.android.R
import com.owncloud.android.utils.DisplayUtils
import java.text.SimpleDateFormat
import java.util.Date

object DisplayTextFormatter {

private const val DATE_TIME_SEPARATOR = ","
private const val TIME_SEPARATOR = ':'
private const val DATE_TIME_PARTS_SIZE = 2
private const val ACCOUNT_HOST_SEPARATOR = '@'

@JvmStatic
fun formatAccountName(user: User): String {
val host = user.accountName.substringAfterLast(ACCOUNT_HOST_SEPARATOR)
val displayName = user.toOwnCloudAccount().displayName
return "$displayName$ACCOUNT_HOST_SEPARATOR${DisplayUtils.convertIdn(host, false)}"
}

@JvmStatic
@JvmOverloads
fun formatRelativeTimestamp(context: Context, timestamp: Long, showFuture: Boolean = false): CharSequence =
formatRelativeDateTime(context, timestamp, DateUtils.SECOND_IN_MILLIS, showFuture = showFuture)

@JvmStatic
@JvmOverloads
fun formatRelativeDateTime(
context: Context,
time: Long,
minResolution: Long,
transitionResolution: Long = DateUtils.WEEK_IN_MILLIS,
flags: Int = 0,
showFuture: Boolean = false
): CharSequence {
val elapsed = System.currentTimeMillis() - time
return when {
!showFuture && elapsed < 0 -> HumanReadableFormatter.formatDateTime(time)

minResolution == DateUtils.MINUTE_IN_MILLIS && elapsed in 1 until DateUtils.MINUTE_IN_MILLIS ->
context.getString(R.string.file_list_seconds_ago)

else -> DateUtils.getRelativeDateTimeString(context, time, minResolution, transitionResolution, flags)
.toString()
.withoutTimeOfDay()
}
}

@JvmStatic
@JvmOverloads
fun formatDate(timestamp: Long, pattern: DateFormatPattern, context: Context = MainApp.getAppContext()): String {
val locale = context.resources.configuration.locales[0]
return SimpleDateFormat(pattern.pattern, locale).format(Date(timestamp))
}

private fun String.withoutTimeOfDay(): String {
val parts = split(DATE_TIME_SEPARATOR)
if (parts.size != DATE_TIME_PARTS_SIZE || parts.any(String::isBlank)) {
return this
}

val (first, second) = parts
val firstHasTime = TIME_SEPARATOR in first
val secondHasTime = TIME_SEPARATOR in second

return when {
secondHasTime && !firstHasTime -> first
firstHasTime && !secondHasTime -> second
else -> this
}
}
}
13 changes: 4 additions & 9 deletions app/src/main/java/com/owncloud/android/datamodel/GalleryItems.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,13 @@
*/
package com.owncloud.android.datamodel

import com.owncloud.android.utils.DisplayUtils
import com.nextcloud.utils.date.DateFormatPattern
import com.nextcloud.utils.text.DisplayTextFormatter

data class GalleryItems(val date: Long, val rows: List<GalleryRow>) {
override fun toString(): String {
val month = DisplayUtils.getDateByPattern(
date,
DisplayUtils.MONTH_PATTERN
)
val year = DisplayUtils.getDateByPattern(
date,
DisplayUtils.YEAR_PATTERN
)
val month = DisplayTextFormatter.formatDate(date, DateFormatPattern.FullMonth)
val year = DisplayTextFormatter.formatDate(date, DateFormatPattern.Year)
return "$month/$year with $rows rows"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.nextcloud.client.account.CurrentAccountProvider
import com.nextcloud.common.NextcloudClient
import com.nextcloud.utils.GlideHelper
import com.nextcloud.utils.avatar.AvatarGenerator
import com.nextcloud.utils.text.DisplayTextFormatter
import com.nextcloud.utils.text.RichSubjectFormatter
import com.nextcloud.utils.text.RichSubjectParam
import com.owncloud.android.MainApp
Expand All @@ -40,7 +41,6 @@ import com.owncloud.android.lib.resources.activities.model.RichObject
import com.owncloud.android.lib.resources.activities.models.PreviewObject
import com.owncloud.android.ui.activities.StickyHeaderAdapter
import com.owncloud.android.ui.interfaces.ActivityListInterface
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.MimeTypeUtil
import com.owncloud.android.utils.theme.ViewThemeUtils
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -81,12 +81,10 @@ open class ActivityListAdapter(

fun getHeaderDateString(context: Context, modificationTimestamp: Long): CharSequence =
if ((System.currentTimeMillis() - modificationTimestamp) < DateUtils.WEEK_IN_MILLIS) {
DisplayUtils.getRelativeDateTimeString(
DisplayTextFormatter.formatRelativeDateTime(
context,
modificationTimestamp,
DateUtils.DAY_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS,
0
DateUtils.DAY_IN_MILLIS
)
} else {
DateFormat.format(
Expand Down
21 changes: 11 additions & 10 deletions app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter
import com.afollestad.sectionedrecyclerview.SectionedViewHolder
import com.nextcloud.client.account.User
import com.nextcloud.utils.date.DateFormatPattern
import com.nextcloud.utils.extensions.toGalleryItems
import com.nextcloud.utils.text.DisplayTextFormatter
import com.nextcloud.utils.thumbnail.ThumbnailGenerator
import com.owncloud.android.R
import com.owncloud.android.databinding.GalleryHeaderBinding
Expand All @@ -36,7 +38,6 @@ import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.ui.activity.ComponentsGetter
import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.FileSortOrder
import com.owncloud.android.utils.theme.ViewThemeUtils
import me.zhanghai.android.fastscroll.PopupTextProvider
Expand Down Expand Up @@ -185,26 +186,26 @@ class GalleryAdapter(
}
}

override fun getPopupText(p0: View, position: Int): CharSequence = DisplayUtils.getDateByPattern(
override fun getPopupText(p0: View, position: Int): CharSequence = DisplayTextFormatter.formatDate(
files[getRelativePosition(position).section()].date,
context,
DisplayUtils.MONTH_YEAR_PATTERN
DateFormatPattern.FullMonthWithYear,
context
)

override fun onBindHeaderViewHolder(holder: SectionedViewHolder?, section: Int, expanded: Boolean) {
if (holder != null) {
val headerViewHolder = holder as GalleryHeaderViewHolder
val galleryItem = files[section]

headerViewHolder.binding.month.text = DisplayUtils.getDateByPattern(
headerViewHolder.binding.month.text = DisplayTextFormatter.formatDate(
galleryItem.date,
context,
DisplayUtils.MONTH_PATTERN
DateFormatPattern.FullMonth,
context
)
headerViewHolder.binding.year.text = DisplayUtils.getDateByPattern(
headerViewHolder.binding.year.text = DisplayTextFormatter.formatDate(
galleryItem.date,
context,
DisplayUtils.YEAR_PATTERN
DateFormatPattern.Year,
context
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import android.content.Context
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.utils.HumanReadableFormatter
import com.nextcloud.utils.text.DisplayTextFormatter
import com.owncloud.android.R
import com.owncloud.android.databinding.InternalTwoWaySyncViewHolderBinding
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.utils.DisplayUtils

class InternalTwoWaySyncViewHolder(val binding: InternalTwoWaySyncViewHolderBinding) :
RecyclerView.ViewHolder(binding.root) {
Expand All @@ -41,7 +41,7 @@ class InternalTwoWaySyncViewHolder(val binding: InternalTwoWaySyncViewHolderBind
if (folder.internalFolderSyncTimestamp == 0L) {
syncTimestamp.text = context.getString(R.string.internal_two_way_sync_not_yet)
} else {
syncTimestamp.text = DisplayUtils.getRelativeTimestamp(
syncTimestamp.text = DisplayTextFormatter.formatRelativeTimestamp(
context,
folder.internalFolderSyncTimestamp
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole
import com.nextcloud.utils.avatar.AvatarGenerator
import com.nextcloud.client.utils.IntentUtil
import com.nextcloud.utils.extensions.setVisibleIf
import com.nextcloud.utils.text.DisplayTextFormatter
import com.nextcloud.utils.text.RichSubjectFormatter
import com.nextcloud.utils.text.RichSubjectParam
import com.owncloud.android.R
Expand All @@ -40,7 +41,6 @@ import com.owncloud.android.lib.resources.notifications.models.RichObject
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.ui.fragment.notifications.NotificationsAdapterItemClick
import com.owncloud.android.ui.fragment.notifications.NotificationsFragment
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.theme.ViewThemeUtils

@Suppress("TooManyFunctions")
Expand Down Expand Up @@ -82,7 +82,7 @@ class NotificationListAdapter(
// region Bind helpers

private fun bindDateTime(holder: NotificationViewHolder, notification: Notification) {
val timestamp = DisplayUtils.getRelativeTimestamp(
val timestamp = DisplayTextFormatter.formatRelativeTimestamp(
fragment.requireContext(),
notification.getDatetime().time
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import com.nextcloud.utils.extensions.ImageViewExtensionsKt;
import com.nextcloud.utils.extensions.ViewExtensionsKt;
import com.nextcloud.utils.mdm.MDMConfig;
import com.nextcloud.utils.text.DisplayTextFormatter;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.databinding.GridItemBinding;
Expand Down Expand Up @@ -678,12 +679,12 @@ private void bindListItemViewHolder(ListItemViewHolder holder, OCFile file) {

final long modificationTimestamp = file.getModificationTimestamp();
if (modificationTimestamp > 0) {
holder.getLastModification().setText(DisplayUtils.getRelativeTimestamp(activity,
modificationTimestamp));
holder.getLastModification().setText(
DisplayTextFormatter.formatRelativeTimestamp(activity, modificationTimestamp));
holder.getLastModification().setVisibility(View.VISIBLE);
} else if (file.getFirstShareTimestamp() > 0) {
holder.getLastModification().setText(
DisplayUtils.getRelativeTimestamp(activity, file.getFirstShareTimestamp())
DisplayTextFormatter.formatRelativeTimestamp(activity, file.getFirstShareTimestamp())
);
holder.getLastModification().setVisibility(View.VISIBLE);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ package com.owncloud.android.ui.adapter

import android.content.Context
import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.utils.text.DisplayTextFormatter
import com.owncloud.android.R
import com.owncloud.android.databinding.PredefinedStatusBinding
import com.owncloud.android.lib.resources.users.PredefinedStatus
import com.owncloud.android.utils.DisplayUtils

private const val ONE_SECOND_IN_MILLIS = 1000

Expand All @@ -28,7 +28,7 @@ class PredefinedStatusViewHolder(private val binding: PredefinedStatusBinding) :
} else {
val clearAt = status.clearAt!!
if (clearAt.type.equals("period")) {
binding.clearAt.text = DisplayUtils.getRelativeTimestamp(
binding.clearAt.text = DisplayTextFormatter.formatRelativeTimestamp(
context,
System.currentTimeMillis() + clearAt.time.toInt() * ONE_SECOND_IN_MILLIS,
true
Expand Down
Loading
Loading