From e1660a454117e7194f10c2420712087620585966 Mon Sep 17 00:00:00 2001 From: "BILAL\\ba269" Date: Sun, 14 Jun 2026 11:42:23 +0500 Subject: [PATCH 1/2] Changes made for bread crumbs for different connections and also made changes for different tools like share, openas --- .../fossify/commons/enums/ConnectionTypes.kt | 18 ++++ .../fossify/commons/extensions/Activity.kt | 96 +++++++++++++++++++ .../org/fossify/commons/models/FileDirItem.kt | 4 +- .../org/fossify/commons/views/Breadcrumbs.kt | 87 ++++++++++++++--- commons/src/main/res/values/strings.xml | 7 +- 5 files changed, 199 insertions(+), 13 deletions(-) create mode 100644 commons/src/main/kotlin/org/fossify/commons/enums/ConnectionTypes.kt diff --git a/commons/src/main/kotlin/org/fossify/commons/enums/ConnectionTypes.kt b/commons/src/main/kotlin/org/fossify/commons/enums/ConnectionTypes.kt new file mode 100644 index 0000000000..fad42920ab --- /dev/null +++ b/commons/src/main/kotlin/org/fossify/commons/enums/ConnectionTypes.kt @@ -0,0 +1,18 @@ +package org.fossify.commons.enums + +enum class ConnectionTypes(val type: String) { + SMB("SMB"), + WebDav("WebDav"), + DAVx5("DAVx5"), + SFTP("SFTP"), + FTP("FTP"), + + Default("Default"); + + companion object { + fun fromType(value: String): ConnectionTypes { + return entries.find { it.type == value } ?: Default + } + } +} + diff --git a/commons/src/main/kotlin/org/fossify/commons/extensions/Activity.kt b/commons/src/main/kotlin/org/fossify/commons/extensions/Activity.kt index 3acdac26a8..960652fc81 100644 --- a/commons/src/main/kotlin/org/fossify/commons/extensions/Activity.kt +++ b/commons/src/main/kotlin/org/fossify/commons/extensions/Activity.kt @@ -37,6 +37,7 @@ import androidx.biometric.BiometricPrompt import androidx.biometric.auth.AuthPromptCallback import androidx.biometric.auth.AuthPromptHost import androidx.biometric.auth.Class2BiometricAuthPrompt +import androidx.core.content.FileProvider import androidx.core.net.toUri import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.FragmentActivity @@ -56,6 +57,7 @@ import org.fossify.commons.dialogs.UpgradeToProDialog import org.fossify.commons.dialogs.WhatsNewDialog import org.fossify.commons.dialogs.WritePermissionDialog import org.fossify.commons.dialogs.WritePermissionDialog.WritePermissionDialogMode +import org.fossify.commons.enums.ConnectionTypes import org.fossify.commons.helpers.CREATE_DOCUMENT_SDK_30 import org.fossify.commons.helpers.EXTRA_SHOW_ADVANCED import org.fossify.commons.helpers.IS_FROM_GALLERY @@ -470,6 +472,34 @@ fun Activity.sharePathIntent(path: String, applicationId: String) { } } +fun Activity.shareNetworkPathIntent(path: String, applicationId: String,file: File) { + ensureBackgroundThread { + val newUri = FileProvider.getUriForFile(this, "$applicationId.provider", file) + Intent().apply { + action = Intent.ACTION_SEND + putExtra(EXTRA_STREAM, newUri) + type = getUriMimeType(path, newUri).normalizeMimeTypeForSharing() + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + grantUriPermission("android", newUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + + try { + startActivity(Intent.createChooser(this, getString(R.string.share_via))) + } catch (e: ActivityNotFoundException) { + toast(R.string.no_app_found) + } catch (e: RuntimeException) { + if (e.cause is TransactionTooLargeException) { + toast(R.string.maximum_share_reached) + } else { + showErrorToast(e) + } + } catch (e: Exception) { + showErrorToast(e) + } + } + } +} + fun Activity.sharePathsIntent(paths: List, applicationId: String) { ensureBackgroundThread { if (paths.size == 1) { @@ -513,6 +543,49 @@ fun Activity.sharePathsIntent(paths: List, applicationId: String) { } } +fun Activity.shareNetworkPathsIntent(paths: List, applicationId: String,file: File) { + ensureBackgroundThread { + if (paths.size == 1) { + shareNetworkPathIntent(paths.first(), applicationId,file) + } else { + val uriPaths = ArrayList() + val newUris = paths.map { + val uri = getFinalUriFromPath(it, applicationId) ?: return@ensureBackgroundThread + uriPaths.add(uri.path!!) + uri + } as ArrayList + + var mimeType = uriPaths.getMimeType() + if (mimeType.isEmpty() || mimeType == "*/*") { + mimeType = paths.getMimeType() + } + mimeType = mimeType.normalizeMimeTypeForSharing() + + Intent().apply { + action = Intent.ACTION_SEND_MULTIPLE + type = mimeType + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + putParcelableArrayListExtra(EXTRA_STREAM, newUris) + + try { + startActivity(Intent.createChooser(this, getString(R.string.share_via))) + } catch (e: ActivityNotFoundException) { + toast(R.string.no_app_found) + } catch (e: RuntimeException) { + if (e.cause is TransactionTooLargeException) { + toast(R.string.maximum_share_reached) + } else { + showErrorToast(e) + } + } catch (e: Exception) { + showErrorToast(e) + } + } + } + } +} + fun Activity.setAsIntent(path: String, applicationId: String) { ensureBackgroundThread { val newUri = getFinalUriFromPath(path, applicationId) ?: return@ensureBackgroundThread @@ -534,6 +607,27 @@ fun Activity.setAsIntent(path: String, applicationId: String) { } } +fun Activity.setAsNetworkIntent(path: String, applicationId: String,file: File) { + ensureBackgroundThread { + val newUri = FileProvider.getUriForFile(this, "$applicationId.provider", file) + Intent().apply { + action = Intent.ACTION_ATTACH_DATA + setDataAndType(newUri, getUriMimeType(path, newUri)) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + val chooser = Intent.createChooser(this, getString(R.string.set_as)) + + try { + startActivityForResult(chooser, REQUEST_SET_AS) + } catch (e: ActivityNotFoundException) { + toast(R.string.no_app_found) + } catch (e: Exception) { + showErrorToast(e) + } + } + } +} + fun Activity.shareTextIntent(text: String) { ensureBackgroundThread { Intent().apply { @@ -702,6 +796,8 @@ fun Activity.getFinalUriFromPath(path: String, applicationId: String): Uri? { return uri } + + fun Activity.tryGenericMimeType(intent: Intent, mimeType: String, uri: Uri): Boolean { var genericMimeType = mimeType.getGenericMimeType() if (genericMimeType.isEmpty()) { diff --git a/commons/src/main/kotlin/org/fossify/commons/models/FileDirItem.kt b/commons/src/main/kotlin/org/fossify/commons/models/FileDirItem.kt index 0430f023ce..705bc31117 100644 --- a/commons/src/main/kotlin/org/fossify/commons/models/FileDirItem.kt +++ b/commons/src/main/kotlin/org/fossify/commons/models/FileDirItem.kt @@ -5,6 +5,7 @@ import android.net.Uri import android.provider.MediaStore import androidx.compose.runtime.Immutable import com.bumptech.glide.signature.ObjectKey +import org.fossify.commons.enums.ConnectionTypes import org.fossify.commons.extensions.* import org.fossify.commons.helpers.* import java.io.File @@ -16,7 +17,8 @@ open class FileDirItem( var children: Int = 0, var size: Long = 0L, var modified: Long = 0L, - var mediaStoreId: Long = 0L + var mediaStoreId: Long = 0L, + var connectionType: ConnectionTypes = ConnectionTypes.Default ) : Comparable { companion object { diff --git a/commons/src/main/kotlin/org/fossify/commons/views/Breadcrumbs.kt b/commons/src/main/kotlin/org/fossify/commons/views/Breadcrumbs.kt index 3b8916dd7c..a706ce6731 100644 --- a/commons/src/main/kotlin/org/fossify/commons/views/Breadcrumbs.kt +++ b/commons/src/main/kotlin/org/fossify/commons/views/Breadcrumbs.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.GradientDrawable import android.graphics.drawable.RippleDrawable +import android.net.Uri import android.util.AttributeSet import android.util.TypedValue import android.view.LayoutInflater @@ -16,6 +17,7 @@ import androidx.core.view.updatePadding import androidx.core.widget.TextViewCompat import org.fossify.commons.R import org.fossify.commons.databinding.ItemBreadcrumbBinding +import org.fossify.commons.enums.ConnectionTypes import org.fossify.commons.extensions.adjustAlpha import org.fossify.commons.extensions.getBasePath import org.fossify.commons.extensions.getDialogBackgroundColor @@ -26,6 +28,7 @@ import org.fossify.commons.extensions.onGlobalLayout import org.fossify.commons.extensions.setDrawablesRelativeWithIntrinsicBounds import org.fossify.commons.helpers.MEDIUM_ALPHA import org.fossify.commons.models.FileDirItem +import androidx.core.net.toUri class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView(context, attrs) { private val inflater = @@ -40,7 +43,7 @@ class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView( private var isFirstScroll = true private var stickyRootInitialLeft = 0 private var rootStartPadding = 0 - + val breadcrumbNames = LinkedHashMap() private val textColorStateList: ColorStateList get() = ColorStateList( arrayOf(intArrayOf(android.R.attr.state_activated), intArrayOf()), @@ -151,14 +154,29 @@ class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView( super.requestLayout() } - fun setBreadcrumb(fullPath: String) { + fun setBreadcrumb(fullPath: String, connectionType: ConnectionTypes = ConnectionTypes.Default) { lastPath = fullPath val basePath = fullPath.getBasePath(context) var currPath = basePath - val tempPath = context.humanizePath(fullPath) + val tempPath = if (connectionType != ConnectionTypes.WebDav && connectionType != ConnectionTypes.SMB) context.humanizePath(fullPath) else lastPath itemsLayout.removeAllViews() - tempPath.split("/") + var pathSegments = mutableListOf() + if(connectionType == ConnectionTypes.WebDav || connectionType == ConnectionTypes.SMB) { + val uri = tempPath.toUri() + val baseUrl = "${uri.scheme}://${uri.host}:${uri.port}" + pathSegments.add(baseUrl) + pathSegments.addAll(uri.pathSegments) + } + + else if (connectionType == ConnectionTypes.DAVx5){ + pathSegments.add(lastPath) + } + else { + pathSegments = tempPath.split("/").toMutableList() + } + + pathSegments .dropLastWhile(String::isEmpty) .forEachIndexed { i, dir -> if (i > 0) { @@ -167,8 +185,31 @@ class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView( if (dir.isEmpty()) { return@forEachIndexed } - currPath = "${currPath.trimEnd('/')}/" - val item = FileDirItem(currPath, dir, true, 0, 0, 0) + currPath = if (connectionType != ConnectionTypes.WebDav && connectionType != ConnectionTypes.SMB) "${currPath.trimEnd('/')}/" else dir + val item = FileDirItem(currPath, dir, true, 0, 0, 0, connectionType = connectionType) + addBreadcrumb( + item = item, + index = i, + isLast = item.path.trimEnd('/') == lastPath.trimEnd('/') + ) + scrollToSelectedItem() + } + } + + fun setBreadcrumbWithName(fullPath: String, name: String?,connectionType: ConnectionTypes) { + if (fullPath == "") return + lastPath = fullPath + if (!breadcrumbNames.containsKey(fullPath)){ + breadcrumbNames[fullPath] = name + } + else{ + breadcrumbNames.removeAfter(fullPath) + } + itemsLayout.removeAllViews() + + breadcrumbNames.entries.forEachIndexed { i, entry -> + entry.value?.let { + val item = FileDirItem(entry.key, it, true, 0, 0, 0, connectionType = connectionType) addBreadcrumb( item = item, index = i, @@ -176,9 +217,10 @@ class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView( ) scrollToSelectedItem() } + } } - private fun addBreadcrumb(item: FileDirItem, index: Int, isLast: Boolean) { + public fun addBreadcrumb(item: FileDirItem, index: Int, isLast: Boolean) { ItemBreadcrumbBinding.inflate(inflater, itemsLayout, false).apply { breadcrumbText.isActivated = isLast && index != 0 @@ -230,15 +272,15 @@ class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView( } } - fun updateColor(color: Int) { + fun updateColor(color: Int,connectionType: ConnectionTypes = ConnectionTypes.Default) { textColor = color - setBreadcrumb(lastPath) + setBreadcrumb(lastPath,connectionType) } - fun updateFontSize(size: Float, updateTexts: Boolean) { + fun updateFontSize(size: Float, updateTexts: Boolean,connectionType: ConnectionTypes = ConnectionTypes.Default) { fontSize = size if (updateTexts) { - setBreadcrumb(lastPath) + setBreadcrumb(lastPath, connectionType = connectionType) } } @@ -252,6 +294,29 @@ class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView( fun getItemCount() = itemsLayout.childCount + fun getItemsTillIndex(index: Int): List{ + return (0..index).mapNotNull { index -> + itemsLayout.getChildAt(index).tag as? FileDirItem + } + } + + fun getAllItems(): List { + return (0 until itemsLayout.childCount).mapNotNull { index -> + itemsLayout.getChildAt(index).tag as? FileDirItem + } + } + + fun LinkedHashMap.removeAfter(key: K) { + val index = breadcrumbNames.keys.indexOf(key as String) + if (index != -1) { + val keysToRemove = breadcrumbNames.keys.drop(index + 1) + + for (key in keysToRemove) { + breadcrumbNames.remove(key) + } + } + } + interface BreadcrumbsListener { fun breadcrumbClicked(id: Int) } diff --git a/commons/src/main/res/values/strings.xml b/commons/src/main/res/values/strings.xml index 492bd28fb6..e1e715d6a1 100644 --- a/commons/src/main/res/values/strings.xml +++ b/commons/src/main/res/values/strings.xml @@ -302,6 +302,7 @@ Loading… Please grant our app access to all your files, it might not work well without it. + --- items %d item %d items @@ -485,6 +486,7 @@ Are you sure you want to proceed with the deletion? Are you sure you want to delete %s? + Are you sure you want to delete connection %s? Delete %s? Are you sure you want to move %s into the recycle bin? Are you sure you want to delete this item? @@ -1212,4 +1214,7 @@ A group of simple, open source Android apps with customizable widgets, without ads and unnecessary permissions. - \ No newline at end of file + + + + From 5c20ea66228c069f6023de0e0e1b8187286427f5 Mon Sep 17 00:00:00 2001 From: "BILAL\\ba269" Date: Sun, 14 Jun 2026 11:52:35 +0500 Subject: [PATCH 2/2] made lines consise --- .../org/fossify/commons/views/Breadcrumbs.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/commons/src/main/kotlin/org/fossify/commons/views/Breadcrumbs.kt b/commons/src/main/kotlin/org/fossify/commons/views/Breadcrumbs.kt index a706ce6731..f7b22be2b6 100644 --- a/commons/src/main/kotlin/org/fossify/commons/views/Breadcrumbs.kt +++ b/commons/src/main/kotlin/org/fossify/commons/views/Breadcrumbs.kt @@ -158,8 +158,14 @@ class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView( lastPath = fullPath val basePath = fullPath.getBasePath(context) var currPath = basePath - val tempPath = if (connectionType != ConnectionTypes.WebDav && connectionType != ConnectionTypes.SMB) context.humanizePath(fullPath) else lastPath - + val tempPath = if ( + connectionType != ConnectionTypes.WebDav && + connectionType != ConnectionTypes.SMB + ) { + context.humanizePath(fullPath) + } else { + lastPath + } itemsLayout.removeAllViews() var pathSegments = mutableListOf() if(connectionType == ConnectionTypes.WebDav || connectionType == ConnectionTypes.SMB) { @@ -185,7 +191,11 @@ class Breadcrumbs(context: Context, attrs: AttributeSet) : HorizontalScrollView( if (dir.isEmpty()) { return@forEachIndexed } - currPath = if (connectionType != ConnectionTypes.WebDav && connectionType != ConnectionTypes.SMB) "${currPath.trimEnd('/')}/" else dir + currPath = if (connectionType != ConnectionTypes.WebDav && connectionType != ConnectionTypes.SMB) { + "${currPath.trimEnd('/')}/" + } + else + {dir} val item = FileDirItem(currPath, dir, true, 0, 0, 0, connectionType = connectionType) addBreadcrumb( item = item,