Skip to content
Open
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 @@ -61,6 +61,7 @@ import eu.opencloud.android.presentation.documentsprovider.cursors.FileCursor
import eu.opencloud.android.presentation.documentsprovider.cursors.RootCursor
import eu.opencloud.android.presentation.documentsprovider.cursors.SpaceCursor
import eu.opencloud.android.presentation.settings.security.SettingsSecurityFragment.Companion.PREFERENCE_LOCK_ACCESS_FROM_DOCUMENT_PROVIDER
import eu.opencloud.android.presentation.settings.advanced.SettingsAdvancedFragment.Companion.PREFERENCE_PRETEND_LOCAL_STORAGE
import eu.opencloud.android.usecases.synchronization.SynchronizeFileUseCase
import eu.opencloud.android.usecases.transfers.downloads.DownloadFileUseCase
import eu.opencloud.android.usecases.synchronization.SynchronizeFolderUseCase
Expand Down Expand Up @@ -94,6 +95,7 @@ class DocumentsStorageProvider : DocumentsProvider() {
private var spacesSyncRequired = true

private lateinit var fileToUpload: OCFile
private val pendingUploads = ConcurrentHashMap<String, OCFile>()

// Cache to avoid redundant PROPFINDs when apps (e.g. Google Photos) call
// openDocument many times for the same file. Two layers:
Expand All @@ -115,7 +117,7 @@ class DocumentsStorageProvider : DocumentsProvider() {

// If documentId == NONEXISTENT_DOCUMENT_ID only Upload is needed because file does not exist in our database yet.
var ocFile: OCFile
val uploadOnly: Boolean = documentId == NONEXISTENT_DOCUMENT_ID || documentId == "null"
val uploadOnly: Boolean = documentId == NONEXISTENT_DOCUMENT_ID || documentId == "null" || documentId.startsWith("pending_")

var accessMode: Int = ParcelFileDescriptor.parseMode(mode)
val isWrite: Boolean = mode.contains("w")
Expand Down Expand Up @@ -197,7 +199,7 @@ class DocumentsStorageProvider : DocumentsProvider() {
}
}
} else {
ocFile = fileToUpload
ocFile = pendingUploads[documentId] ?: fileToUpload
accessMode = accessMode or ParcelFileDescriptor.MODE_CREATE
}

Expand All @@ -214,6 +216,8 @@ class DocumentsStorageProvider : DocumentsProvider() {
Timber.d("A file with id $documentId has been closed! Time to synchronize it with server.")
// If only needs to upload that file
if (uploadOnly) {
pendingUploads.remove(documentId)

ocFile.length = fileToOpen.length()
val uploadFilesUseCase: UploadFilesFromSystemUseCase by inject()
val uploadFilesUseCaseParams = UploadFilesFromSystemUseCase.Params(
Expand Down Expand Up @@ -318,7 +322,13 @@ class DocumentsStorageProvider : DocumentsProvider() {
override fun queryDocument(documentId: String, projection: Array<String>?): Cursor {
Timber.d("Query Document: $documentId")
if (documentId == NONEXISTENT_DOCUMENT_ID) return FileCursor(projection).apply {
addFile(fileToUpload)
if (this@DocumentsStorageProvider::fileToUpload.isInitialized) addFile(fileToUpload)
}

if (documentId.startsWith("pending_")) {
return FileCursor(projection).apply {
pendingUploads[documentId]?.let { addFile(it) }
}
}

val fileId = try {
Expand Down Expand Up @@ -350,6 +360,10 @@ class DocumentsStorageProvider : DocumentsProvider() {
// If access from document provider is not allowed, return empty cursor
val preferences: SharedPreferencesProvider by inject()
val lockAccessFromDocumentProvider = preferences.getBoolean(PREFERENCE_LOCK_ACCESS_FROM_DOCUMENT_PROVIDER, false)

// Get if user selected to pretend local storage
val pretendLocal = preferences.getBoolean(PREFERENCE_PRETEND_LOCAL_STORAGE, false)

return if (lockAccessFromDocumentProvider && accounts.isNotEmpty()) {
result.apply { addProtectedRoot(contextApp) }
} else {
Expand All @@ -362,7 +376,7 @@ class DocumentsStorageProvider : DocumentsProvider() {
)
val spacesFeatureAllowedForAccount = AccountUtils.isSpacesFeatureAllowedForAccount(contextApp, account, capabilities)

result.addRoot(account, contextApp, spacesFeatureAllowedForAccount)
result.addRoot(account, contextApp, spacesFeatureAllowedForAccount, pretendLocal)
}
result
}
Expand Down Expand Up @@ -497,6 +511,41 @@ class DocumentsStorageProvider : DocumentsProvider() {
}
}

override fun isChildDocument(parentDocumentId: String, documentId: String): Boolean {
Timber.d("isChildDocument($parentDocumentId, $documentId)")

// If they are the same, Android specs usually consider it a child/match
if (parentDocumentId == documentId) return true

return try {
// Parse the child file. If it's a new un-uploaded file, pull from memory. Otherwise, query DB.
val childFile = if (documentId == NONEXISTENT_DOCUMENT_ID && this::fileToUpload.isInitialized) {
fileToUpload
} else if (documentId.startsWith("pending_")) {
pendingUploads[documentId] ?: return false
} else {
getFileByIdOrException(documentId.toInt())
}

val parentIdInt = parentDocumentId.toIntOrNull()

if (parentIdInt != null) {
// The parent is a standard folder
val parentFile = getFileByIdOrException(parentIdInt)

// Check if the child belongs to the same account and its path sits inside the parent's path
childFile.owner == parentFile.owner && childFile.remotePath.startsWith(parentFile.remotePath)
} else {
// The parentDocumentId is a string, meaning it's the account root (e.g., "user@server.com")
// Just verify the child file belongs to this account
childFile.owner == parentDocumentId
}
} catch (e: Exception) {
Timber.e(e, "Error evaluating isChildDocument for parent: $parentDocumentId, child: $documentId")
false
}
}

private fun checkUseCaseResult(result: UseCaseResult<Any>, folderToNotify: String) {
if (!result.isSuccess) {
Timber.e(result.getThrowableOrNull()!!)
Expand Down Expand Up @@ -530,12 +579,13 @@ class DocumentsStorageProvider : DocumentsProvider() {
mimeType: String,
displayName: String,
): String {
// We just need to return a Document ID, so we'll return an empty one. File does not exist in our db yet.
// File will be created at [openDocument] method.
val tempDir = File(FileStorageUtils.getTemporalPath(parentDocument.owner, parentDocument.spaceId))
val newFile = File(tempDir, displayName)
newFile.parentFile?.mkdirs()
fileToUpload = OCFile(

val pendingId = "pending_" + UUID.randomUUID().toString()

val ocFile = OCFile(
remotePath = parentDocument.remotePath + displayName,
mimeType = mimeType,
parentId = parentDocument.id,
Expand All @@ -545,7 +595,9 @@ class DocumentsStorageProvider : DocumentsProvider() {
storagePath = newFile.path
}

return NONEXISTENT_DOCUMENT_ID
pendingUploads[pendingId] = ocFile

return pendingId
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,23 @@ import eu.opencloud.android.datamodel.FileDataStorageManager

class RootCursor(projection: Array<String>?) : MatrixCursor(projection ?: DEFAULT_ROOT_PROJECTION) {

fun addRoot(account: Account, context: Context, spacesAllowed: Boolean) {
fun addRoot(account: Account, context: Context, spacesAllowed: Boolean, pretendLocal: Boolean) {
val manager = FileDataStorageManager(account)
val mainDirId = if (spacesAllowed) {
// To display the list of spaces for an account, we need to do this trick.
// If the document id is not a number, we will know that it is the time to display the list of spaces for the account
account.name
} else {
// Root directory of the personal space or "Files" (old server)
manager.getRootPersonalFolder()?.id
}

val flags = Root.FLAG_SUPPORTS_SEARCH or Root.FLAG_SUPPORTS_CREATE
// Add FLAG_SUPPORTS_IS_CHILD to enable Folder selection
var flags = Root.FLAG_SUPPORTS_SEARCH or Root.FLAG_SUPPORTS_CREATE or Root.FLAG_SUPPORTS_IS_CHILD

// Add FLAG_LOCAL_ONLY if the user enabled it
if (pretendLocal) {
flags = flags or Root.FLAG_LOCAL_ONLY
}

newRow()
.add(Root.COLUMN_ROOT_ID, account.name)
Expand Down Expand Up @@ -72,8 +77,7 @@ class RootCursor(projection: Array<String>?) : MatrixCursor(projection ?: DEFAUL
Root.COLUMN_TITLE,
Root.COLUMN_DOCUMENT_ID,
Root.COLUMN_AVAILABLE_BYTES,
Root.COLUMN_SUMMARY,
Root.COLUMN_FLAGS
Root.COLUMN_SUMMARY
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ package eu.opencloud.android.presentation.settings.advanced

import android.os.Bundle
import android.view.View
import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import eu.opencloud.android.R
import eu.opencloud.android.presentation.documentsprovider.DocumentsProviderUtils.notifyDocumentsProviderRoots
import org.koin.androidx.viewmodel.ext.android.viewModel

class SettingsAdvancedFragment : PreferenceFragmentCompat() {
Expand All @@ -37,11 +39,13 @@ class SettingsAdvancedFragment : PreferenceFragmentCompat() {

private var prefShowHiddenFiles: SwitchPreferenceCompat? = null
private var prefRemoveLocalFiles: ListPreference? = null
private var prefPretendLocal: CheckBoxPreference? = null

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.settings_advanced, rootKey)

prefShowHiddenFiles = findPreference(PREF_SHOW_HIDDEN_FILES)
prefPretendLocal = findPreference(PREFERENCE_PRETEND_LOCAL_STORAGE)
prefRemoveLocalFiles = findPreference<ListPreference>(PREFERENCE_REMOVE_LOCAL_FILES)?.apply {
entries = listOf(
getString(R.string.prefs_delete_local_files_entries_never),
Expand Down Expand Up @@ -80,9 +84,15 @@ class SettingsAdvancedFragment : PreferenceFragmentCompat() {
advancedViewModel.scheduleDeleteLocalFiles(newValue)
true
}

prefPretendLocal?.setOnPreferenceChangeListener { _: Preference?, _: Any ->
notifyDocumentsProviderRoots(requireContext())
true
}
}

companion object {
const val PREF_SHOW_HIDDEN_FILES = "show_hidden_files"
const val PREFERENCE_PRETEND_LOCAL_STORAGE = "pretend_local_storage"
}
}
2 changes: 2 additions & 0 deletions opencloudApp/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
<string name="prefs_lock_application_entries_30minutes">Nach 30 Minuten</string>
<string name="prefs_lock_access_from_document_provider">Zugriff vom Dokumentanbieter sperren</string>
<string name="prefs_lock_access_from_document_provider_summary">Sperren Sie den Zugriff anderer Apps auf die Dateien der Konten innerhalb der App über den nativen Android-Dateimanager.</string>
<string name="prefs_pretend_local_title">Als lokaler Speicher ausgeben</string>
<string name="prefs_pretend_local_summary">Ermöglicht Drittanbieter-Apps, OpenCloud zu sehen, wenn diese strikt nach lokalen Dateien suchen. Dies umgeht standardmäßige Android-Einschränkungen, kann jedoch in einigen Apps zu Rucklern oder Aufhängern der Benutzeroberfläche führen, wenn Ihre Netzwerkverbindung langsam ist.</string>
<string name="prefs_touches_with_other_visible_windows">Berührungen mit anderen sichtbaren Fenstern</string>
<string name="prefs_touches_with_other_visible_windows_summary">Ermöglicht Berührungen, wenn die Ansicht durch ein anderes sichtbares Fenster verdeckt ist. Aktivieren Sie diese Option, um Apps zur Lichtfilterung zu nutzen.</string>
<string name="confirmation_touches_with_other_windows_title">Sind Sie sicher, dass Sie diese Funktion aktivieren möchten\?</string>
Expand Down
2 changes: 2 additions & 0 deletions opencloudApp/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
<string name="prefs_lock_application_entries_30minutes">Después de 30 minutos</string>
<string name="prefs_lock_access_from_document_provider">Bloquear acceso desde administrador de archivos</string>
<string name="prefs_lock_access_from_document_provider_summary">Bloquear el acceso de otras aplicaciones a los archivos de las cuentas a través del administrador de archivos de Android.</string>
<string name="prefs_pretend_local_title">Simular almacenamiento local</string>
<string name="prefs_pretend_local_summary">Permite que las aplicaciones de terceros vean OpenCloud cuando soliciten estrictamente archivos locales. Esto elude las limitaciones estándar de Android, pero puede causar bloqueos en la interfaz de algunas aplicaciones si la conexión de red es lenta.</string>
<string name="prefs_touches_with_other_visible_windows">Bloquear pulsaciones con aplicaciones superpuestas</string>
<string name="prefs_touches_with_other_visible_windows_summary">Permite interactuar con la aplicación aunque haya otras ventanas visibles por encima. Activa para usar aplicaciones de atenuación de pantalla.</string>
<string name="confirmation_touches_with_other_windows_title">¿Seguro que quieres activar esta función\?</string>
Expand Down
2 changes: 2 additions & 0 deletions opencloudApp/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
<string name="prefs_lock_application_entries_30minutes">Après 30 minutes</string>
<string name="prefs_lock_access_from_document_provider">Verrouiller l’accès au gestionnaire de fichier</string>
<string name="prefs_lock_access_from_document_provider_summary">Verrouiller l\'accès des autres applications aux fichiers d\'utilisateurs de l\'application via le navigateur de fichiers natif d\'Android.</string>
<string name="prefs_pretend_local_title">Simuler un stockage local</string>
<string name="prefs_pretend_local_summary">Permet aux applications tierces de voir OpenCloud lorsqu\'elles demandent strictement des fichiers locaux. Cela contourne les limitations standard d\'Android, mais peut provoquer des blocages de l\'interface dans certaines applications si votre réseau est lent.</string>
<string name="prefs_touches_with_other_visible_windows">Interactions avec d\'autres fenêtres visibles</string>
<string name="prefs_touches_with_other_visible_windows_summary">Autoriser les interactions quand une autre fenêtre visible se superpose à l\'affichage. Activer pour utiliser le fonctionnement des applications de filtre lumineux.</string>
<string name="confirmation_touches_with_other_windows_title">Êtes-vous sûr de vouloir activer cette fonctionnalité \?</string>
Expand Down
2 changes: 2 additions & 0 deletions opencloudApp/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
<string name="prefs_lock_application_entries_30minutes">Dopo 30 minuti</string>
<string name="prefs_lock_access_from_document_provider">Blocca accesso dal provider documenti</string>
<string name="prefs_lock_access_from_document_provider_summary">Impedisci ad altre app di accedere ai file degli account dell\'app tramite l\'esploratore file nativo di Android.</string>
<string name="prefs_pretend_local_title">Simula archiviazione locale</string>
<string name="prefs_pretend_local_summary">Consente alle app di terze parti di vedere OpenCloud quando richiedono rigorosamente file locali. Questo aggira le limitazioni standard di Android, ma potrebbe causare blocchi dell\'interfaccia utente in alcune app se la rete è lenta.</string>
<string name="prefs_touches_with_other_visible_windows">Tocchi con altre finestre visibili</string>
<string name="prefs_touches_with_other_visible_windows_summary">Consenti tocchi quando la vista è oscurata da un\'altra finestra visibile. Attiva per utilizzare app con filtro luminosità.</string>
<string name="confirmation_touches_with_other_windows_title">Vuoi davvero abilitare questa funzione?</string>
Expand Down
2 changes: 2 additions & 0 deletions opencloudApp/src/main/res/values-nl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
<string name="prefs_lock_application_entries_30minutes">Na 30 minuten</string>
<string name="prefs_lock_access_from_document_provider">Toegang blokkeren vanuit bestandsbeheer</string>
<string name="prefs_lock_access_from_document_provider_summary">Blokkeer de toegang van andere apps tot de bestanden van accounts in de app via Android bestandsbeheer.</string>
<string name="prefs_pretend_local_title">Lokale opslag simuleren</string>
<string name="prefs_pretend_local_summary">Hiermee kunnen externe apps OpenCloud zien wanneer ze specifiek om lokale bestanden vragen. Dit omzeilt standaard Android-beperkingen, maar kan bij een trage netwerkverbinding leiden tot vastlopers in de gebruikersinterface van sommige apps.</string>
<string name="prefs_touches_with_other_visible_windows">Raakt andere zichtbare vensters</string>
<string name="prefs_touches_with_other_visible_windows_summary">Sta aanrakingen toe als de weergave door een ander zichtbaar venster wordt bedekt. Schakel dit in om apps voor lichtfiltering te kunnen gebruiken.</string>
<string name="confirmation_touches_with_other_windows_title">Weet u zeker dat u deze functie wilt inschakelen\?</string>
Expand Down
2 changes: 2 additions & 0 deletions opencloudApp/src/main/res/values-pl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
<string name="prefs_lock_application_entries_30minutes">Po 30 minutach</string>
<string name="prefs_lock_access_from_document_provider">Zablokuj dostęp dostawcy dokumentów</string>
<string name="prefs_lock_access_from_document_provider_summary">Wymagaj kodu PIN</string>
<string name="prefs_pretend_local_title">Symuluj pamięć lokalną</string>
<string name="prefs_pretend_local_summary">Pozwala aplikacjom innych firm widzieć OpenCloud, gdy ściśle wymagają plików lokalnych. Omija to standardowe ograniczenia Androida, ale może powodować zawieszanie się interfejsu w niektórych aplikacjach, jeśli połączenie sieciowe jest powolne.</string>
<string name="prefs_touches_with_other_visible_windows">Zezwalaj na dotyk przy innych widocznych oknach</string>
<string name="prefs_touches_with_other_visible_windows_summary">Zezwalaj na interakcję przy nakładających się oknach</string>
<string name="confirmation_touches_with_other_windows_title">Czy na pewno chcesz aktywować tę funkcję\?</string>
Expand Down
2 changes: 2 additions & 0 deletions opencloudApp/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
<string name="prefs_lock_application_entries_30minutes">After 30 minutes</string>
<string name="prefs_lock_access_from_document_provider">Lock access from document provider</string>
<string name="prefs_lock_access_from_document_provider_summary">Lock access from other apps to the files of the accounts in the app via the Android native file explorer.</string>
<string name="prefs_pretend_local_title">Pretend to be local storage</string>
<string name="prefs_pretend_local_summary">Allows third-party apps to see OpenCloud when they strictly request local files. This bypasses standard Android limitations but may cause UI lockups in some apps if your network is slow.</string>
<string name="prefs_touches_with_other_visible_windows">Touches with other visible windows</string>
<string name="prefs_touches_with_other_visible_windows_summary">Allow touches when the view is obscured by another visible window. Enable it to use light filtering apps.</string>

Expand Down
7 changes: 7 additions & 0 deletions opencloudApp/src/main/res/xml/settings_advanced.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,11 @@
app:key="remove_local_files"
app:negativeButtonText=""
app:title="@string/prefs_delete_local_files" />

<CheckBoxPreference
app:key="pretend_local_storage"
app:title="@string/prefs_pretend_local_title"
app:summary="@string/prefs_pretend_local_summary"
app:defaultValue="false"
app:iconSpaceReserved="false" />
</PreferenceScreen>