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 @@ -5,18 +5,18 @@
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.owncloud.android.utils
package com.nextcloud.utils.view

import com.owncloud.android.AbstractIT
import org.junit.Assert.assertEquals
import org.junit.Test

class DisplayUtilsIT : AbstractIT() {
class ScreenMetricsIT : AbstractIT() {
@Test
fun testPixelToDP() {
val px = 123
val dp = DisplayUtils.convertPixelToDp(px, targetContext)
val newPx = DisplayUtils.convertDpToPixel(dp, targetContext)
val dp = ScreenMetrics.pxToDp(px, targetContext)
val newPx = ScreenMetrics.dpToPx(dp, targetContext)

assertEquals(px.toLong(), newPx.toLong())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId
import com.nextcloud.test.TestActivity
import com.nextcloud.utils.view.ScreenMetrics
import com.owncloud.android.AbstractIT
import com.owncloud.android.R
import com.owncloud.android.lib.resources.users.StatusType
import com.owncloud.android.ui.TextDrawable
import com.owncloud.android.utils.BitmapUtils
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.ScreenshotTest
import org.junit.Test

Expand All @@ -42,7 +42,7 @@ class AvatarIT : AbstractIT() {

scenario.onActivity { sut ->
val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius)
val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext)
val width = ScreenMetrics.dpToPx(2 * avatarRadius, targetContext)
val fragment = sut.supportFragmentManager.fragments.last() as AvatarTestFragment

fragment.run {
Expand Down Expand Up @@ -79,7 +79,7 @@ class AvatarIT : AbstractIT() {

scenario.onActivity { sut ->
val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius)
val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext)
val width = ScreenMetrics.dpToPx(2 * avatarRadius, targetContext)

val paulette = BitmapFactory.decodeFile(getFile("paulette.jpg").absolutePath)
val christine = BitmapFactory.decodeFile(getFile("christine.jpg").absolutePath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.client.utils.IntentUtil
import com.nextcloud.utils.SnackbarUtil
import com.nextcloud.utils.mdm.MDMConfig
import com.nextcloud.utils.view.ScreenMetrics
import com.owncloud.android.BuildConfig
import com.owncloud.android.R
import com.owncloud.android.authentication.AuthenticatorActivity
Expand All @@ -35,7 +36,6 @@ import com.owncloud.android.features.FeatureItem
import com.owncloud.android.ui.activity.BaseActivity
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.ui.adapter.FeaturesViewAdapter
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
import javax.inject.Inject

Expand Down Expand Up @@ -222,7 +222,7 @@ class FirstRunActivity :
@Suppress("MagicNumber")
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
DisplayUtils.convertDpToPixel(if (isLandscape) 100f else 150f, this)
ScreenMetrics.dpToPx(if (isLandscape) 100f else 150f, this)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.nextcloud.utils.avatar.AvatarGenerationListener
import com.nextcloud.utils.avatar.AvatarGenerator
import com.nextcloud.utils.extensions.getParcelableArgument
import com.nextcloud.utils.mdm.MDMConfig
import com.nextcloud.utils.view.ScreenMetrics
import com.owncloud.android.R
import com.owncloud.android.databinding.DialogChooseAccountBinding
import com.owncloud.android.datamodel.FileDataStorageManager
Expand All @@ -37,7 +38,6 @@ import com.owncloud.android.ui.activity.BaseActivity
import com.owncloud.android.ui.activity.DrawerActivity
import com.owncloud.android.ui.adapter.UserListAdapter
import com.owncloud.android.ui.adapter.UserListItem
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand Down Expand Up @@ -244,7 +244,7 @@ class ChooseAccountDialogFragment :
fun setStatus(newStatus: Status, context: Context) {
currentStatus = newStatus

val size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, context)
val size = ScreenMetrics.dpToPx(STATUS_SIZE_IN_DP, context)
binding.currentAccount.ticker.background = null
binding.currentAccount.ticker.setImageDrawable(StatusDrawable(newStatus, size.toFloat(), context))
binding.currentAccount.ticker.visibility = View.VISIBLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ import com.nextcloud.ui.composeActivity.ComposeActivity
import com.nextcloud.ui.composeActivity.ComposeDestination
import com.nextcloud.utils.GlideHelper
import com.nextcloud.utils.SnackbarUtil
import com.nextcloud.utils.view.ScreenMetrics
import com.owncloud.android.R
import com.owncloud.android.databinding.FileActionsBottomSheetBinding
import com.owncloud.android.databinding.FileActionsBottomSheetItemBinding
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.lib.ocs.ServerResponse
import com.owncloud.android.lib.resources.status.Method
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -114,7 +114,7 @@ class ClientIntegration(
}

if (drawable is PictureDrawable) {
val defaultSize = DisplayUtils.convertDpToPixel(
val defaultSize = ScreenMetrics.dpToPx(
context.resources.getDimension(R.dimen.iconized_single_line_item_icon_size),
context
).toInt().coerceAtLeast(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ 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.text.SpanFormatter
import com.nextcloud.utils.thumbnail.ThumbnailArguments
import com.nextcloud.utils.view.LocaleDirection
import com.nextcloud.utils.view.ScreenMetrics
import com.owncloud.android.R
import com.owncloud.android.databinding.FileActionsBottomSheetBinding
import com.owncloud.android.databinding.FileActionsBottomSheetItemBinding
Expand All @@ -48,7 +51,6 @@ import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.SyncedFolderProvider
import com.owncloud.android.lib.resources.files.model.FileLockType
import com.owncloud.android.ui.activity.ComponentsGetter
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.FileStorageUtils
import com.nextcloud.utils.thumbnail.ThumbnailGenerator
import com.owncloud.android.utils.theme.ViewThemeUtils
Expand Down Expand Up @@ -239,11 +241,11 @@ class FileActionsBottomSheet :
val decryptedFileName = titleFile?.decryptedFileName
if (decryptedFileName != null) {
val isFolder = titleFile.isFolder
val isRTL = DisplayUtils.isRTL()
val isRTL = LocaleDirection.isRtl
val (base, ext) = FileStorageUtils.getFilenameAndExtension(decryptedFileName, isFolder, isRTL)
val titleMaxWidth = DisplayUtils.convertDpToPixel(
val titleMaxWidth = ScreenMetrics.dpToPx(
requireContext().resources.configuration.screenWidthDp.times(FILENAME_MAX_WIDTH_PERCENTAGE).toFloat(),
context
requireContext()
)

binding.title.maxWidth = titleMaxWidth
Expand Down Expand Up @@ -297,7 +299,7 @@ class FileActionsBottomSheet :
FileLockType.COLLABORATIVE -> R.string.locked_by_app
else -> R.string.locked_by
}
return DisplayUtils.createTextWithSpan(
return SpanFormatter.styleLast(
getString(resource, lockInfo.lockedBy),
lockInfo.lockedBy,
StyleSpan(Typeface.BOLD)
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/java/com/nextcloud/ui/sort/SortOrderUi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.ui.sort

import androidx.annotation.StringRes
import androidx.fragment.app.FragmentManager
import com.owncloud.android.R
import com.owncloud.android.ui.dialog.SortingOrderDialogFragment
import com.owncloud.android.utils.FileSortOrder

object SortOrderUi {

@JvmStatic
fun showDialog(fragmentManager: FragmentManager, sortOrder: FileSortOrder) {
val transaction = fragmentManager.beginTransaction().addToBackStack(null)
SortingOrderDialogFragment.newInstance(sortOrder)
.show(transaction, SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT)
}

@JvmStatic
@StringRes
fun labelRes(sortOrder: FileSortOrder): Int = when (sortOrder.name) {
FileSortOrder.SORT_Z_TO_A_ID -> R.string.menu_item_sort_by_name_z_a
FileSortOrder.SORT_NEW_TO_OLD_ID -> R.string.menu_item_sort_by_date_newest_first
FileSortOrder.SORT_OLD_TO_NEW_ID -> R.string.menu_item_sort_by_date_oldest_first
FileSortOrder.SORT_BIG_TO_SMALL_ID -> R.string.menu_item_sort_by_size_biggest_first
FileSortOrder.SORT_SMALL_TO_BIG_ID -> R.string.menu_item_sort_by_size_smallest_first
else -> R.string.menu_item_sort_by_name_a_z
}
}
26 changes: 26 additions & 0 deletions app/src/main/java/com/nextcloud/utils/RawResourceReader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.utils

import android.content.res.Resources
import androidx.annotation.RawRes
import com.owncloud.android.lib.common.utils.Log_OC
import java.io.IOException

object RawResourceReader {

private const val TAG = "RawResourceReader"

@JvmStatic
fun readText(resources: Resources, @RawRes id: Int): String = try {
resources.openRawResource(id).bufferedReader().use { it.readText() }
} catch (e: IOException) {
Log_OC.e(TAG, "Failed to read raw resource", e)
""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ 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

Expand All @@ -29,7 +28,7 @@ object DisplayTextFormatter {
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)}"
return "$displayName$ACCOUNT_HOST_SEPARATOR${LinkFormatter.toUnicodeDomain(host)}"
}

@JvmStatic
Expand Down
57 changes: 57 additions & 0 deletions app/src/main/java/com/nextcloud/utils/text/LinkFormatter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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 java.net.IDN

object LinkFormatter {

private val WEB_SCHEMES = listOf("http://", "https://")
private const val HANDLE_PREFIX = "@"
private const val DOMAIN_LABEL_SEPARATOR = '.'
private const val HOST_PREFIX = "//"
private const val USER_INFO_SEPARATOR = "@"
private const val PATH_SEPARATOR = '/'

@JvmStatic
fun removeScheme(url: String?): String {
val link = url.orEmpty()
val scheme = WEB_SCHEMES.firstOrNull { link.startsWith(it, ignoreCase = true) }.orEmpty()
return link.drop(scheme.length).trim()
}

@JvmStatic
fun formatHandle(handle: String?): String {
val trimmed = handle?.trim().orEmpty()
if (trimmed.isEmpty() || trimmed.startsWith(HANDLE_PREFIX)) {
return trimmed
}
return HANDLE_PREFIX + trimmed
}

@JvmStatic
fun toAsciiDomain(url: String): String = convertHost(url, IDN::toASCII)

@JvmStatic
fun toUnicodeDomain(url: String): String = convertHost(url, IDN::toUnicode)

private fun convertHost(url: String, convert: (String) -> String): String {
val leadingDots = url.takeWhile { it == DOMAIN_LABEL_SEPARATOR }
val link = url.drop(leadingDots.length)

val hostStart = when {
HOST_PREFIX in link -> link.indexOf(HOST_PREFIX) + HOST_PREFIX.length
USER_INFO_SEPARATOR in link -> link.indexOf(USER_INFO_SEPARATOR) + USER_INFO_SEPARATOR.length
else -> 0
}
val hostEnd = link.indexOf(PATH_SEPARATOR, hostStart).takeIf { it >= 0 } ?: link.length
val host = link.substring(hostStart, hostEnd)

return leadingDots + link.replaceRange(hostStart, hostEnd, convert(host))
}
}
29 changes: 29 additions & 0 deletions app/src/main/java/com/nextcloud/utils/text/SpanFormatter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.CharacterStyle

object SpanFormatter {

@JvmStatic
fun styleLast(text: String, part: String?, style: CharacterStyle): SpannableStringBuilder {
val builder = SpannableStringBuilder(text)
if (part == null) {
return builder
}

val start = text.lastIndexOf(part)
if (start >= 0) {
builder.setSpan(style, start, start + part.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
}
return builder
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import android.graphics.Paint
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.applyCanvas
import androidx.core.graphics.createBitmap
import com.nextcloud.utils.view.ScreenMetrics
import com.owncloud.android.R
import com.owncloud.android.utils.BitmapUtils
import com.owncloud.android.utils.DisplayUtils

object VideoOverlayGenerator {

Expand Down Expand Up @@ -50,7 +50,7 @@ object VideoOverlayGenerator {
private fun createPlayButton(context: Context): Bitmap? {
val drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.video_white, null)
?: return null
val px = DisplayUtils.convertDpToPixel(PLAY_BUTTON_SIZE_IN_DP, context)
val px = ScreenMetrics.dpToPx(PLAY_BUTTON_SIZE_IN_DP, context)
return BitmapUtils.drawableToBitmap(drawable, px, px)
}
}
19 changes: 19 additions & 0 deletions app/src/main/java/com/nextcloud/utils/view/LocaleDirection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.utils.view

import android.text.TextUtils
import android.view.View
import java.util.Locale

object LocaleDirection {

@JvmStatic
val isRtl: Boolean
get() = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL
}
Loading
Loading