diff --git a/app/src/androidTest/java/com/owncloud/android/utils/DisplayUtilsIT.kt b/app/src/androidTest/java/com/nextcloud/utils/view/ScreenMetricsIT.kt similarity index 67% rename from app/src/androidTest/java/com/owncloud/android/utils/DisplayUtilsIT.kt rename to app/src/androidTest/java/com/nextcloud/utils/view/ScreenMetricsIT.kt index df69c14e052e..f1f87c449af6 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/DisplayUtilsIT.kt +++ b/app/src/androidTest/java/com/nextcloud/utils/view/ScreenMetricsIT.kt @@ -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()) } diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt index 7697399d8688..a420bcc84e3e 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/AvatarIT.kt @@ -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 @@ -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 { @@ -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) diff --git a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt index 27fb460bf679..972c96e87bf1 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt @@ -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 @@ -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 @@ -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) ) } diff --git a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt index 00ff3917ae46..240469426428 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt @@ -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 @@ -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 @@ -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 diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/ClientIntegration.kt b/app/src/main/java/com/nextcloud/ui/fileactions/ClientIntegration.kt index fa6c4bf32239..ef10a692afdd 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/ClientIntegration.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/ClientIntegration.kt @@ -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 @@ -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) diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt index 2e740cdc7985..771ce411b314 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt @@ -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 @@ -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 @@ -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 @@ -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) diff --git a/app/src/main/java/com/nextcloud/ui/sort/SortOrderUi.kt b/app/src/main/java/com/nextcloud/ui/sort/SortOrderUi.kt new file mode 100644 index 000000000000..f6833a08a515 --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/sort/SortOrderUi.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * 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 + } +} diff --git a/app/src/main/java/com/nextcloud/utils/RawResourceReader.kt b/app/src/main/java/com/nextcloud/utils/RawResourceReader.kt new file mode 100644 index 000000000000..fb32ca615f9c --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/RawResourceReader.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * 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) + "" + } +} diff --git a/app/src/main/java/com/nextcloud/utils/text/DisplayTextFormatter.kt b/app/src/main/java/com/nextcloud/utils/text/DisplayTextFormatter.kt index 3ee11ca2baa2..65a69426e6df 100644 --- a/app/src/main/java/com/nextcloud/utils/text/DisplayTextFormatter.kt +++ b/app/src/main/java/com/nextcloud/utils/text/DisplayTextFormatter.kt @@ -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 @@ -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 diff --git a/app/src/main/java/com/nextcloud/utils/text/LinkFormatter.kt b/app/src/main/java/com/nextcloud/utils/text/LinkFormatter.kt new file mode 100644 index 000000000000..0a300beb0607 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/text/LinkFormatter.kt @@ -0,0 +1,57 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * 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)) + } +} diff --git a/app/src/main/java/com/nextcloud/utils/text/SpanFormatter.kt b/app/src/main/java/com/nextcloud/utils/text/SpanFormatter.kt new file mode 100644 index 000000000000..c718fb388012 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/text/SpanFormatter.kt @@ -0,0 +1,29 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * 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 + } +} diff --git a/app/src/main/java/com/nextcloud/utils/thumbnail/VideoOverlayGenerator.kt b/app/src/main/java/com/nextcloud/utils/thumbnail/VideoOverlayGenerator.kt index 0522de7b6952..9e49067dedca 100644 --- a/app/src/main/java/com/nextcloud/utils/thumbnail/VideoOverlayGenerator.kt +++ b/app/src/main/java/com/nextcloud/utils/thumbnail/VideoOverlayGenerator.kt @@ -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 { @@ -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) } } diff --git a/app/src/main/java/com/nextcloud/utils/view/LocaleDirection.kt b/app/src/main/java/com/nextcloud/utils/view/LocaleDirection.kt new file mode 100644 index 000000000000..76ea6eb815ef --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/view/LocaleDirection.kt @@ -0,0 +1,19 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * 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 +} diff --git a/app/src/main/java/com/nextcloud/utils/view/ScreenMetrics.kt b/app/src/main/java/com/nextcloud/utils/view/ScreenMetrics.kt new file mode 100644 index 000000000000..1a8bca4676df --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/view/ScreenMetrics.kt @@ -0,0 +1,31 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.view + +import android.app.Activity +import android.content.Context +import android.graphics.Point +import android.util.DisplayMetrics + +object ScreenMetrics { + + @JvmStatic + fun size(activity: Activity?): Point = Point().also { size -> + @Suppress("DEPRECATION") + activity?.windowManager?.defaultDisplay?.getSize(size) + } + + @JvmStatic + fun dpToPx(dp: Float, context: Context): Int = (dp * (context.densityDpi / DisplayMetrics.DENSITY_DEFAULT)).toInt() + + @JvmStatic + fun pxToDp(px: Int, context: Context): Float = px * (DisplayMetrics.DENSITY_DEFAULT / context.densityDpi) + + private val Context.densityDpi: Float + get() = resources.displayMetrics.densityDpi.toFloat() +} diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index 2afd5ade94ef..23382c9bdd19 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -82,7 +82,6 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.activity.SyncedFoldersActivity; import com.owncloud.android.ui.notifications.NotificationUtils; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.ReceiversHelper; @@ -300,7 +299,6 @@ public void onCreate() { logger.i(TAG, String.format(Locale.US, "Started %d migrations", startedMigrationsCount)); new SecurityUtils(); - DisplayUtils.useCompatVectorIfNeeded(); fixStoragePath(); diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index 05181e31974a..dd720ccc985f 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -64,9 +64,11 @@ import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.common.PlainClient; import com.nextcloud.operations.PostMethod; +import com.nextcloud.utils.RawResourceReader; import com.nextcloud.utils.SnackbarUtil; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.mdm.MDMConfig; +import com.nextcloud.utils.text.LinkFormatter; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.dialog.LoginDialog; @@ -100,13 +102,11 @@ import com.owncloud.android.ui.activity.SettingsActivity; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.WebViewUtil; import com.owncloud.android.utils.theme.ViewThemeUtils; -import java.io.InputStream; import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashMap; @@ -635,8 +635,7 @@ public void onReceivedError(WebView view, WebResourceRequest request, WebResourc accountSetupWebviewBinding.loginWebviewProgressBar.setVisibility(View.GONE); accountSetupWebviewBinding.loginWebview.setVisibility(View.VISIBLE); - InputStream resources = getResources().openRawResource(R.raw.custom_error); - String customError = DisplayUtils.getData(resources); + String customError = RawResourceReader.readText(getResources(), R.raw.custom_error); if (!customError.isEmpty()) { accountSetupWebviewBinding.loginWebview.loadData(customError, "text/html; charset=UTF-8", null); @@ -1015,7 +1014,7 @@ private void checkOcServer() { // Handle internationalized domain names try { - uri = DisplayUtils.convertIdn(uri, true); + uri = LinkFormatter.toAsciiDomain(uri); } catch (IllegalArgumentException ex) { // Let the Nextcloud library check the error of the malformed URI Log_OC.e(TAG, "Error converting internationalized domain name " + uri, ex); diff --git a/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.kt b/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.kt index 5d1c01734823..57edec0f467a 100644 --- a/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.kt +++ b/app/src/main/java/com/owncloud/android/ui/AvatarGroupLayout.kt @@ -21,11 +21,11 @@ import com.nextcloud.client.account.User import com.nextcloud.utils.GlideHelper.loadCircularBitmapIntoImageView import com.nextcloud.utils.avatar.AvatarGenerationListener import com.nextcloud.utils.avatar.AvatarGenerator +import com.nextcloud.utils.view.ScreenMetrics import com.owncloud.android.R import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.lib.resources.shares.ShareeUser -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils import kotlin.math.min @@ -40,13 +40,13 @@ class AvatarGroupLayout @JvmOverloads constructor( private val borderDrawable = ContextCompat.getDrawable(context, R.drawable.round_bgnd) @Px - private val avatarSize: Int = DisplayUtils.convertDpToPixel(40f, context) + private val avatarSize: Int = ScreenMetrics.dpToPx(40f, context) @Px - private val avatarBorderSize: Int = DisplayUtils.convertDpToPixel(2f, context) + private val avatarBorderSize: Int = ScreenMetrics.dpToPx(2f, context) @Px - private val overlapPx: Int = DisplayUtils.convertDpToPixel(24f, context) + private val overlapPx: Int = ScreenMetrics.dpToPx(24f, context) var boundFileId: Long? = null set(value) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 1b3d1a06e8aa..a9e5c98fd996 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -75,6 +75,7 @@ import com.nextcloud.utils.extensions.NavigationViewExtensionsKt; import com.nextcloud.utils.extensions.ViewExtensionsKt; import com.nextcloud.utils.mdm.MDMConfig; +import com.nextcloud.utils.view.ScreenMetrics; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.PassCodeManager; @@ -106,7 +107,6 @@ import com.owncloud.android.ui.navigation.NavigatorActivity; import com.owncloud.android.ui.navigation.NavigatorScreen; import com.owncloud.android.utils.BitmapUtils; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DrawableUtil; import com.owncloud.android.utils.DrawerMenuUtil; import com.owncloud.android.utils.FilesSyncHelper; @@ -541,7 +541,7 @@ private void showTopBanner(ConstraintLayout banner) { ImageView imageView = (ImageView) view.getChildAt(0); imageView.setImageTintList(ColorStateList.valueOf(iconColor)); GradientDrawable background = (GradientDrawable) imageView.getBackground(); - background.setStroke(DisplayUtils.convertDpToPixel(1, this), iconColor); + background.setStroke(ScreenMetrics.dpToPx(1, this), iconColor); TextView textView = (TextView) view.getChildAt(1); textView.setTextColor(iconColor); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java b/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java index 7b7c1ad023e0..a35ec658eeac 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java @@ -24,16 +24,15 @@ import android.widget.ProgressBar; import com.nextcloud.client.utils.IntentUtil; +import com.nextcloud.utils.RawResourceReader; import com.nextcloud.utils.SnackbarUtil; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.ExternalsiteWebviewBinding; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.NextcloudWebViewClient; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.WebViewUtil; -import java.io.InputStream; import androidx.appcompat.app.ActionBar; import androidx.drawerlayout.widget.DrawerLayout; @@ -140,8 +139,7 @@ public void onProgressChanged(WebView view, int progress) { final ExternalSiteWebView self = this; getWebView().setWebViewClient(new NextcloudWebViewClient(getSupportFragmentManager()) { public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { - InputStream resources = getResources().openRawResource(R.raw.custom_error); - String customError = DisplayUtils.getData(resources); + String customError = RawResourceReader.readText(getResources(), R.raw.custom_error); if (!customError.isEmpty()) { getWebView().loadData(customError, "text/html; charset=UTF-8", null); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index d2dfc4bee0e3..aa90fbe4d512 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -54,6 +54,7 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.model.OCUploadLocalPathData; +import com.nextcloud.ui.sort.SortOrderUi; import com.nextcloud.utils.SnackbarUtil; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.FileExtensionsKt; @@ -87,7 +88,6 @@ import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.ui.helpers.UriUploader; import com.owncloud.android.utils.DataHolderUtil; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.FileSortOrder; import com.owncloud.android.utils.MimeType; @@ -125,7 +125,6 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.LinearLayoutManager; -import static com.owncloud.android.utils.DisplayUtils.openSortingOrderDialogFragment; import static com.owncloud.android.utils.UriUtils.getDisplayNameForUri; /** @@ -307,7 +306,7 @@ protected void onDestroy() { @Override public void onSortingOrderChosen(FileSortOrder newSortOrder) { preferences.setSortOrder(mFile, newSortOrder); - sortButton.setText(DisplayUtils.getSortOrderStringId(newSortOrder)); + sortButton.setText(SortOrderUi.labelRes(newSortOrder)); populateDirectoryList(null); } @@ -818,8 +817,8 @@ private void populateDirectoryList(OCFile file) { sortButton = binding.toolbarLayout.sortButton; FileSortOrder sortOrder = preferences.getSortOrderByFolder(mFile); - sortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder)); - sortButton.setOnClickListener(l -> openSortingOrderDialogFragment(getSupportFragmentManager(), sortOrder)); + sortButton.setText(SortOrderUi.labelRes(sortOrder)); + sortButton.setOnClickListener(l -> SortOrderUi.showDialog(getSupportFragmentManager(), sortOrder)); } private void setupReceiveExternalFilesAdapter(List files) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index 8e61cb0f71b2..f830eb7ddc8c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -34,6 +34,7 @@ import com.nextcloud.common.NextcloudClient; import com.nextcloud.utils.GlideHelper; import com.nextcloud.utils.extensions.BundleExtensionsKt; +import com.nextcloud.utils.text.LinkFormatter; import com.owncloud.android.R; import com.owncloud.android.databinding.UserInfoLayoutBinding; import com.owncloud.android.lib.common.OwnCloudAccount; @@ -46,7 +47,6 @@ import com.owncloud.android.ui.adapter.UserInfoAdapter; import com.owncloud.android.ui.dialog.AccountRemovalDialog; import com.owncloud.android.ui.events.TokenPushEvent; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.PushUtils; import org.greenrobot.eventbus.Subscribe; @@ -299,9 +299,9 @@ private LinkedList createUserInfoDetails(Us addToListIfNeeded(result, R.drawable.ic_phone, userInfo.getPhone(), R.string.user_info_phone); addToListIfNeeded(result, R.drawable.ic_email, userInfo.getEmail(), R.string.user_info_email); addToListIfNeeded(result, R.drawable.ic_map_marker, userInfo.getAddress(), R.string.user_info_address); - addToListIfNeeded(result, R.drawable.ic_web, DisplayUtils.beautifyURL(userInfo.getWebsite()), + addToListIfNeeded(result, R.drawable.ic_web, LinkFormatter.removeScheme(userInfo.getWebsite()), R.string.user_info_website); - addToListIfNeeded(result, R.drawable.ic_twitter, DisplayUtils.beautifyTwitterHandle(userInfo.getTwitter()), + addToListIfNeeded(result, R.drawable.ic_twitter, LinkFormatter.formatHandle(userInfo.getTwitter()), R.string.user_info_twitter); return result; diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 4534efbdb3f9..0d1fcfe9e0fd 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -42,6 +42,7 @@ import com.nextcloud.utils.extensions.ViewExtensionsKt; import com.nextcloud.utils.mdm.MDMConfig; import com.nextcloud.utils.text.DisplayTextFormatter; +import com.nextcloud.utils.view.LocaleDirection; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.GridItemBinding; @@ -68,7 +69,6 @@ import com.owncloud.android.ui.fragment.SearchType; import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface; import com.owncloud.android.ui.preview.PreviewTextFragment; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.FileSortOrder; import com.owncloud.android.utils.FileStorageUtils; @@ -199,7 +199,7 @@ public OCFileListAdapter( // initialize thumbnails cache on background thread ThumbnailsCacheManager.initDiskCacheAsync(); - isRTL = DisplayUtils.isRTL(); + isRTL = LocaleDirection.isRtl(); } public boolean isMultiSelect() { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt index bf40ac46e382..072c3ff7ac67 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt @@ -19,6 +19,7 @@ import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.bumptech.glide.Glide import com.nextcloud.client.account.User import com.nextcloud.utils.thumbnail.ThumbnailGenerator +import com.nextcloud.utils.view.LocaleDirection import com.owncloud.android.R import com.owncloud.android.databinding.UnifiedSearchCurrentDirectoryItemBinding import com.owncloud.android.databinding.UnifiedSearchEmptyBinding @@ -32,7 +33,6 @@ import com.owncloud.android.ui.helpers.FileOperationsHelper import com.owncloud.android.ui.interfaces.UnifiedSearchCurrentDirItemAction import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface import com.owncloud.android.ui.unifiedsearch.UnifiedSearchSection -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils /** @@ -141,7 +141,7 @@ class UnifiedSearchListAdapter( } VIEW_TYPE_CURRENT_DIR -> { - val isRTL = DisplayUtils.isRTL() + val isRTL = LocaleDirection.isRtl val binding = UnifiedSearchCurrentDirectoryItemBinding.inflate(layoutInflater, parent, false) UnifiedSearchCurrentDirItemViewHolder( binding, diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 3ad327055ac3..8112f7226442 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -26,12 +26,12 @@ import com.nextcloud.utils.avatar.AvatarGenerationListener; import com.nextcloud.utils.avatar.AvatarGenerator; import com.nextcloud.utils.mdm.MDMConfig; +import com.nextcloud.utils.text.LinkFormatter; import com.owncloud.android.R; import com.owncloud.android.databinding.AccountActionBinding; import com.owncloud.android.databinding.AccountItemBinding; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; import java.util.ArrayList; @@ -250,7 +250,7 @@ public void bind(User user, * @param user the account */ private void setUser(User user) { - binding.account.setText(DisplayUtils.convertIdn(user.getAccountName(), false)); + binding.account.setText(LinkFormatter.toUnicodeDomain(user.getAccountName())); binding.account.setTag(user.getAccountName()); } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt index 903a28b1af6d..ab5db8acb154 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt @@ -24,11 +24,11 @@ import com.nextcloud.client.utils.IntentUtil import com.nextcloud.utils.avatar.AvatarGenerationListener import com.nextcloud.utils.avatar.AvatarGenerator import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.text.LinkFormatter import com.owncloud.android.R import com.owncloud.android.databinding.AccountRemovalDialogBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.ui.dialog.extensions.themeButtons -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject @@ -70,7 +70,7 @@ class AccountRemovalDialog : viewThemeUtils.platform.themeRadioButton(binding.radioRequestDeletion) alertDialog.themeButtons(viewThemeUtils) binding.userName.text = UserAccountManager.getDisplayName(user) - binding.account.text = user?.let { DisplayUtils.convertIdn(it.accountName, false) } + binding.account.text = user?.let { LinkFormatter.toUnicodeDomain(it.accountName) } } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt index 618bd7ab9033..c27a6573b7b2 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.kt @@ -24,6 +24,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.client.di.Injectable import com.nextcloud.client.preferences.SubFolderRule import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.text.SpanFormatter import com.owncloud.android.R import com.owncloud.android.databinding.SyncedFoldersSettingsLayoutBinding import com.owncloud.android.datamodel.MediaFolderType @@ -34,7 +35,6 @@ import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.activity.FolderPickerActivity import com.owncloud.android.ui.activity.UploadFilesActivity import com.owncloud.android.ui.dialog.parcel.SyncedFolderParcelable -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.FileUtil import com.owncloud.android.utils.theme.ViewThemeUtils @@ -185,7 +185,7 @@ class SyncedFolderPreferencesDialogFragment : setEnabled(it.isEnabled) if (!TextUtils.isEmpty(it.localPath)) { - binding.syncedFoldersSettingsLocalFolderPath.text = DisplayUtils.createTextWithSpan( + binding.syncedFoldersSettingsLocalFolderPath.text = SpanFormatter.styleLast( String.format( getString(R.string.synced_folders_preferences_folder_path), it.localPath @@ -262,7 +262,7 @@ class SyncedFolderPreferencesDialogFragment : fun setLocalFolderSummary(path: String?) { syncedFolder?.localPath = path binding?.localFolderSummary?.text = FileStorageUtils.pathToUserFriendlyDisplay(path, activity, resources) - binding?.syncedFoldersSettingsLocalFolderPath?.text = DisplayUtils.createTextWithSpan( + binding?.syncedFoldersSettingsLocalFolderPath?.text = SpanFormatter.styleLast( String.format( getString(R.string.synced_folders_preferences_folder_path), syncedFolder!!.localPath diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileListLayoutManager.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileListLayoutManager.kt index 1f4375466bc7..c859dd460538 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileListLayoutManager.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileListLayoutManager.kt @@ -14,16 +14,17 @@ import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.ui.sort.SortOrderUi import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.adapter.OCFileListAdapter -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.FileSortOrder class FileListLayoutManager(private val fragment: OCFileListFragment, private val preferences: AppPreferences) { fun sortFiles(sortOrder: FileSortOrder?) { - fragment.mSortButton?.setText(DisplayUtils.getSortOrderStringId(sortOrder)) - sortOrder?.let { fragment.mAdapter.setSortOrder(fragment.mFile, it) } + sortOrder ?: return + fragment.mSortButton?.setText(SortOrderUi.labelRes(sortOrder)) + fragment.mAdapter.setSortOrder(fragment.mFile, sortOrder) } fun isGridViewPreferred(): Boolean { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 016c237a4c55..202fb2157656 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -53,6 +53,7 @@ import com.nextcloud.common.NextcloudClient; import com.nextcloud.ui.fileactions.FileAction; import com.nextcloud.ui.fileactions.FileActionsBottomSheet; +import com.nextcloud.ui.sort.SortOrderUi; import com.nextcloud.utils.EditorUtils; import com.nextcloud.utils.ShortcutUtil; import com.nextcloud.utils.SnackbarUtil; @@ -112,7 +113,6 @@ import com.owncloud.android.ui.fragment.helper.ParentFolderFinder; import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FileSortOrder; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.PermissionUtil; @@ -149,7 +149,6 @@ import static com.owncloud.android.ui.fragment.SearchType.NO_SEARCH; import static com.owncloud.android.ui.fragment.SearchType.RECENT_FILES_SEARCH; import static com.owncloud.android.ui.fragment.SearchType.SHARED_FILTER; -import static com.owncloud.android.utils.DisplayUtils.openSortingOrderDialogFragment; /** * A Fragment that lists all files and folders in a given path. @@ -464,7 +463,7 @@ public void prepareOCFileList(Bundle savedInstanceState) { setEmptyView(searchEvent); if (mSortButton != null) { - mSortButton.setOnClickListener(v -> openSortingOrderDialogFragment(requireFragmentManager(), + mSortButton.setOnClickListener(v -> SortOrderUi.showDialog(requireFragmentManager(), preferences.getSortOrderByFolder(mFile))); } @@ -1639,7 +1638,7 @@ private void updateSortButton() { sortOrder = preferences.getSortOrderByFolder(mFile); } - mSortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder)); + mSortButton.setText(SortOrderUi.labelRes(sortOrder)); } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/localfilelist/LocalFileListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/localfilelist/LocalFileListFragment.kt index 3134e8a6facb..fac147f90da4 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/localfilelist/LocalFileListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/localfilelist/LocalFileListFragment.kt @@ -21,13 +21,13 @@ import androidx.core.view.MenuProvider import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.GridLayoutManager import com.nextcloud.client.di.Injectable +import com.nextcloud.ui.sort.SortOrderUi import com.owncloud.android.R import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.adapter.localFileList.LocalFileListAdapter import com.owncloud.android.ui.fragment.EmptyListState import com.owncloud.android.ui.fragment.ExtendedListFragment import com.owncloud.android.ui.interfaces.LocalFileListFragmentInterface -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.FileSortOrder import java.io.File @@ -112,11 +112,11 @@ class LocalFileListFragment : button.setOnClickListener { val sortOrder = preferences.getSortOrderByType(FileSortOrder.Type.localFileListView) - DisplayUtils.openSortingOrderDialogFragment(parentFragmentManager, sortOrder) + SortOrderUi.showDialog(parentFragmentManager, sortOrder) } val sortOrder = preferences.getSortOrderByType(FileSortOrder.Type.localFileListView) ?: return - button.setText(DisplayUtils.getSortOrderStringId(sortOrder)) + button.setText(SortOrderUi.labelRes(sortOrder)) } private fun setupGridViewButton() { @@ -217,7 +217,7 @@ class LocalFileListFragment : //region View options fun sortFiles(sortOrder: FileSortOrder) { - mSortButton?.setText(DisplayUtils.getSortOrderStringId(sortOrder)) + mSortButton?.setText(SortOrderUi.labelRes(sortOrder)) adapter.setSortOrder(sortOrder) } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt index bdd412db5fc3..33e51fa0ebf9 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt @@ -57,6 +57,7 @@ import com.nextcloud.utils.extensions.getBigThumbnailKey import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.getSmallThumbnail import com.nextcloud.utils.extensions.typedActivity +import com.nextcloud.utils.view.ScreenMetrics import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.databinding.PreviewImageFragmentBinding @@ -71,7 +72,6 @@ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment import com.owncloud.android.ui.fragment.FileFragment import com.owncloud.android.utils.BitmapUtils -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.MimeTypeUtil import com.owncloud.android.utils.theme.ViewThemeUtils import edu.umd.cs.findbugs.annotations.SuppressFBWarnings @@ -237,7 +237,7 @@ class PreviewImageFragment : binding.image.tag = file.fileId - val screenSize = DisplayUtils.getScreenSize(activity) + val screenSize = ScreenMetrics.size(activity) val width = screenSize.x val height = screenSize.y @@ -524,7 +524,7 @@ class PreviewImageFragment : val storagePath = ocFile.storagePath try { val maxDownScale = 3 // could be a parameter passed to doInBackground(...) - val screenSize = DisplayUtils.getScreenSize(activity) + val screenSize = ScreenMetrics.size(activity) var minWidth = screenSize.x var minHeight = screenSize.y var i = 0 diff --git a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinFragment.kt b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinFragment.kt index ff08d8d75712..61b8f9abbd7c 100644 --- a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinFragment.kt @@ -40,6 +40,7 @@ import com.nextcloud.client.network.ClientFactory import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.client.utils.Throttler +import com.nextcloud.ui.sort.SortOrderUi import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet import com.nextcloud.utils.SnackbarUtil import com.nextcloud.utils.extensions.getTypedActivity @@ -52,7 +53,6 @@ import com.owncloud.android.ui.dialog.SortingOrderDialogFragment.OnSortingOrderL import com.owncloud.android.ui.interfaces.TrashbinActivityInterface import com.owncloud.android.ui.navigation.NavigatorActivity import com.owncloud.android.ui.navigation.listener.NavigatorOnBackPressListener -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.FileSortOrder import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject @@ -173,8 +173,9 @@ class TrashbinFragment : activity?.findViewById(R.id.sort_list_button_group)?.visibility = View.VISIBLE activity?.findViewById(R.id.sort_button)?.run { setOnClickListener { - DisplayUtils.openSortingOrderDialogFragment( - activity?.supportFragmentManager, + val fragmentManager = activity?.supportFragmentManager ?: return@setOnClickListener + SortOrderUi.showDialog( + fragmentManager, preferences.getSortOrderByType( FileSortOrder.Type.trashBinView, FileSortOrder.SORT_NEW_TO_OLD @@ -182,7 +183,7 @@ class TrashbinFragment : ) } - setText(DisplayUtils.getSortOrderStringId(sortOrder)) + setText(SortOrderUi.labelRes(sortOrder)) visibility = View.VISIBLE } @@ -314,9 +315,10 @@ class TrashbinFragment : } override fun onSortingOrderChosen(selection: FileSortOrder?) { + selection ?: return val navigatorActivity = getTypedActivity(NavigatorActivity::class.java) val sortButton = navigatorActivity?.findViewById(R.id.sort_button) - sortButton?.setText(DisplayUtils.getSortOrderStringId(selection)) + sortButton?.setText(SortOrderUi.labelRes(selection)) trashbinListAdapter?.setSortOrder(selection) } diff --git a/app/src/main/java/com/owncloud/android/utils/BitmapUtils.java b/app/src/main/java/com/owncloud/android/utils/BitmapUtils.java index 064fd73d5e78..3a6b19aea5a7 100644 --- a/app/src/main/java/com/owncloud/android/utils/BitmapUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/BitmapUtils.java @@ -26,6 +26,7 @@ import android.widget.ImageView; import com.nextcloud.utils.BitmapExtensionsKt; +import com.nextcloud.utils.view.ScreenMetrics; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.lib.common.utils.Log_OC; @@ -443,7 +444,7 @@ public static void setRoundedBitmapForGridMode(Bitmap thumbnail, ImageView image public static Bitmap createAvatarWithStatus(Bitmap avatar, StatusType statusType, @NonNull String icon, Context context) { float avatarRadius = getResources().getDimension(R.dimen.list_item_avatar_icon_radius); - int width = DisplayUtils.convertDpToPixel(2 * avatarRadius, context); + int width = ScreenMetrics.dpToPx(2 * avatarRadius, context); Bitmap output = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java deleted file mode 100644 index d06db1b20052..000000000000 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-FileCopyrightText: 2023 ZetaTom - * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-FileCopyrightText: 2021 TSI-mc - * SPDX-FileCopyrightText: 2020 Infomaniak Network SA - * SPDX-FileCopyrightText: 2020 Joris Bodin - * SPDX-FileCopyrightText: 2020 Kilian Périsset - * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-FileCopyrightText: 2018-2020 Tobias Kaminsky - * SPDX-FileCopyrightText: 2017 Harikrishnan Rajan - * SPDX-FileCopyrightText: 2017 Alejandro Morales - * SPDX-FileCopyrightText: 2016 Andy Scherzinger - * SPDX-FileCopyrightText: 2016 ownCloud Inc. - * SPDX-FileCopyrightText: 2015 David A. Velasco - * SPDX-FileCopyrightText: 2012 Lennart Rosam - * SPDX-FileCopyrightText: 2011 Bartosz Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) - */ -package com.owncloud.android.utils; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.text.style.StyleSpan; -import android.util.DisplayMetrics; -import android.view.View; - -import com.owncloud.android.R; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.ui.dialog.SortingOrderDialogFragment; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.net.IDN; -import java.nio.charset.Charset; -import java.util.Locale; - -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.widget.AppCompatDrawableManager; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; - -import static com.owncloud.android.ui.dialog.SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT; -import static com.owncloud.android.utils.FileSortOrder.SORT_BIG_TO_SMALL_ID; -import static com.owncloud.android.utils.FileSortOrder.SORT_NEW_TO_OLD_ID; -import static com.owncloud.android.utils.FileSortOrder.SORT_OLD_TO_NEW_ID; -import static com.owncloud.android.utils.FileSortOrder.SORT_SMALL_TO_BIG_ID; -import static com.owncloud.android.utils.FileSortOrder.SORT_Z_TO_A_ID; - -/** - * A helper class for UI/display related operations. - */ -public final class DisplayUtils { - private static final String TAG = DisplayUtils.class.getSimpleName(); - - private static final String HTTP_PROTOCOL = "http://"; - private static final String HTTPS_PROTOCOL = "https://"; - private static final String TWITTER_HANDLE_PREFIX = "@"; - - private DisplayUtils() { - // utility class -> private constructor - } - - /** - * beautifies a given URL by removing any http/https protocol prefix. - * - * @param url to be beautified url - * @return beautified url - */ - public static String beautifyURL(@Nullable String url) { - if (TextUtils.isEmpty(url)) { - return ""; - } - - if (url.length() >= 7 && HTTP_PROTOCOL.equalsIgnoreCase(url.substring(0, 7))) { - return url.substring(HTTP_PROTOCOL.length()).trim(); - } - - if (url.length() >= 8 && HTTPS_PROTOCOL.equalsIgnoreCase(url.substring(0, 8))) { - return url.substring(HTTPS_PROTOCOL.length()).trim(); - } - - return url.trim(); - } - - /** - * beautifies a given twitter handle by prefixing it with an @ in case it is missing. - * - * @param handle to be beautified twitter handle - * @return beautified twitter handle - */ - public static String beautifyTwitterHandle(@Nullable String handle) { - if (handle != null) { - String trimmedHandle = handle.trim(); - - if (TextUtils.isEmpty(trimmedHandle)) { - return ""; - } - - if (trimmedHandle.startsWith(TWITTER_HANDLE_PREFIX)) { - return trimmedHandle; - } else { - return TWITTER_HANDLE_PREFIX + trimmedHandle; - } - } else { - return ""; - } - } - - /** - * Converts an internationalized domain name (IDN) in an URL to and from ASCII/Unicode. - * - * @param url the URL where the domain name should be converted - * @param toASCII if true converts from Unicode to ASCII, if false converts from ASCII to Unicode - * @return the URL containing the converted domain name - */ - public static String convertIdn(String url, boolean toASCII) { - - String urlNoDots = url; - StringBuilder dots = new StringBuilder(); - while (urlNoDots.length() > 0 && urlNoDots.charAt(0) == '.') { - urlNoDots = url.substring(1); - dots.append("."); - } - - // Find host name after '//' or '@' - int hostStart = 0; - if (urlNoDots.contains("//")) { - hostStart = url.indexOf("//") + "//".length(); - } else if (url.contains("@")) { - hostStart = url.indexOf('@') + "@".length(); - } - - int hostEnd = url.substring(hostStart).indexOf('/'); - // Handle URL which doesn't have a path (path is implicitly '/') - hostEnd = hostEnd == -1 ? urlNoDots.length() : hostStart + hostEnd; - - String host = urlNoDots.substring(hostStart, hostEnd); - host = toASCII ? IDN.toASCII(host) : IDN.toUnicode(host); - - return dots.toString() + urlNoDots.substring(0, hostStart) + host + urlNoDots.substring(hostEnd); - } - - /** - * Gets the screen size in pixels. - * - * @param caller Activity calling; needed to get access to the {@link android.view.WindowManager} - * @return Size in pixels of the screen, or default {@link Point} if caller is null - */ - public static Point getScreenSize(Activity caller) { - Point size = new Point(); - if (caller != null) { - caller.getWindowManager().getDefaultDisplay().getSize(size); - } - return size; - } - - /** - * styling of given spanText within a given text. - * - * @param text the non styled complete text - * @param spanText the to be styled text - * @param style the style to be applied - */ - public static SpannableStringBuilder createTextWithSpan(String text, String spanText, StyleSpan style) { - if (text == null) { - return null; - } - - SpannableStringBuilder sb = new SpannableStringBuilder(text); - if (spanText == null) { - return sb; - } - - int start = text.lastIndexOf(spanText); - - if (start < 0) { - return sb; - } - - int end = start + spanText.length(); - sb.setSpan(style, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE); - return sb; - } - - /** - * Get String data from a InputStream - * - * @param inputStream The File InputStream - */ - public static String getData(InputStream inputStream) { - - BufferedReader buffreader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset())); - String line; - StringBuilder text = new StringBuilder(); - try { - while ((line = buffreader.readLine()) != null) { - text.append(line); - text.append('\n'); - } - } catch (IOException e) { - Log_OC.e(TAG, e.getMessage()); - } - return text.toString(); - } - - // Solution inspired by https://stackoverflow.com/questions/34936590/why-isnt-my-vector-drawable-scaling-as-expected - // Copied from https://raw.githubusercontent.com/nextcloud/talk-android/8ec8606bc61878e87e3ac8ad32c8b72d4680013c/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java - // under GPL3 - public static void useCompatVectorIfNeeded() { - try { - @SuppressLint("RestrictedApi") AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get(); - Class inflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$InflateDelegate"); - Class vdcInflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate"); - - Constructor constructor = vdcInflateDelegateClass.getDeclaredConstructor(); - constructor.setAccessible(true); - Object vdcInflateDelegate = constructor.newInstance(); - - Class[] args = {String.class, inflateDelegateClass}; - Method addDelegate = AppCompatDrawableManager.class.getDeclaredMethod("addDelegate", args); - addDelegate.setAccessible(true); - addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate); - } catch (Exception e) { - Log_OC.e(TAG, "Failed to use reflection to enable proper vector scaling"); - } - } - - public static int convertDpToPixel(float dp, Context context) { - Resources resources = context.getResources(); - DisplayMetrics metrics = resources.getDisplayMetrics(); - - return (int) (dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)); - } - - public static float convertPixelToDp(int px, Context context) { - Resources resources = context.getResources(); - DisplayMetrics metrics = resources.getDisplayMetrics(); - - return px * (DisplayMetrics.DENSITY_DEFAULT / (float) metrics.densityDpi); - } - - public static boolean isRTL() { - return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL; - } - - static public void openSortingOrderDialogFragment(FragmentManager supportFragmentManager, FileSortOrder sortOrder) { - FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction(); - fragmentTransaction.addToBackStack(null); - - SortingOrderDialogFragment.newInstance(sortOrder).show(fragmentTransaction, SORTING_ORDER_FRAGMENT); - } - - public static @StringRes int getSortOrderStringId(FileSortOrder sortOrder) { - return switch (sortOrder.name) { - case SORT_Z_TO_A_ID -> R.string.menu_item_sort_by_name_z_a; - case SORT_NEW_TO_OLD_ID -> R.string.menu_item_sort_by_date_newest_first; - case SORT_OLD_TO_NEW_ID -> R.string.menu_item_sort_by_date_oldest_first; - case SORT_BIG_TO_SMALL_ID -> R.string.menu_item_sort_by_size_biggest_first; - case SORT_SMALL_TO_BIG_ID -> R.string.menu_item_sort_by_size_smallest_first; - default -> R.string.menu_item_sort_by_name_a_z; - }; - } -} diff --git a/app/src/test/java/com/nextcloud/utils/text/LinkFormatterTest.kt b/app/src/test/java/com/nextcloud/utils/text/LinkFormatterTest.kt new file mode 100644 index 000000000000..ba2e48a8ef21 --- /dev/null +++ b/app/src/test/java/com/nextcloud/utils/text/LinkFormatterTest.kt @@ -0,0 +1,23 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2019 Tobias Kaminsky + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.utils.text + +import org.junit.Assert.assertEquals +import org.junit.Test + +class LinkFormatterTest { + + @Test + fun testConvertIdn() { + assertEquals("", LinkFormatter.toAsciiDomain("")) + assertEquals("", LinkFormatter.toUnicodeDomain("")) + assertEquals("http://www.nextcloud.com", LinkFormatter.toAsciiDomain("http://www.nextcloud.com")) + assertEquals("http://www.xn--wlkchen-90a.com", LinkFormatter.toAsciiDomain("http://www.wölkchen.com")) + assertEquals("http://www.wölkchen.com", LinkFormatter.toUnicodeDomain("http://www.xn--wlkchen-90a.com")) + } +} diff --git a/app/src/test/java/com/owncloud/android/utils/DisplayUtilsTest.java b/app/src/test/java/com/owncloud/android/utils/DisplayUtilsTest.java deleted file mode 100644 index fe2a7d7bfb3b..000000000000 --- a/app/src/test/java/com/owncloud/android/utils/DisplayUtilsTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky - * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.owncloud.android.utils; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class DisplayUtilsTest { - - @Test - public void testConvertIdn() { - assertEquals("", DisplayUtils.convertIdn("", true)); - assertEquals("", DisplayUtils.convertIdn("", false)); - assertEquals("http://www.nextcloud.com", DisplayUtils.convertIdn("http://www.nextcloud.com", true)); - assertEquals("http://www.xn--wlkchen-90a.com", DisplayUtils.convertIdn("http://www.wölkchen.com", true)); - assertEquals("http://www.wölkchen.com", DisplayUtils.convertIdn("http://www.xn--wlkchen-90a.com", false)); - } -}