create folders
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
package com.vaultdrop.mobile.features.saf
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un dossier physique via DocumentsContract (SAF) — facteur commun de
|
||||||
|
* `ensureVaultDropFolder` (onboarding) et de la feature « Créer un dossier ».
|
||||||
|
*
|
||||||
|
* Le parent peut être un URI *tree* (racine importée) ou *document* (dossier
|
||||||
|
* déjà créé/parcouru) : les deux sont acceptés par `createDocument`.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class SafFolderCreator @Inject constructor(
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val resolver: ContentResolver get() = context.contentResolver
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un sous-dossier nommé `name` sous `parentUri`. Retourne son URI SAF,
|
||||||
|
* ou null si le parent est absente ou si la création échoue.
|
||||||
|
*/
|
||||||
|
suspend fun createFolder(parentUri: String?, name: String): Uri? {
|
||||||
|
val parentDocumentUri = toDocumentUri(parentUri) ?: return null
|
||||||
|
val created = runCatching {
|
||||||
|
DocumentsContract.createDocument(
|
||||||
|
resolver,
|
||||||
|
parentDocumentUri,
|
||||||
|
DocumentsContract.Document.MIME_TYPE_DIR,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
}.getOrElse { e ->
|
||||||
|
Timber.w(e, "cannot create folder '$name' in $parentUri")
|
||||||
|
null
|
||||||
|
} ?: return null
|
||||||
|
|
||||||
|
// Best-effort : prolonge l'accès au-delà de l'intent original.
|
||||||
|
runCatching {
|
||||||
|
resolver.takePersistableUriPermission(
|
||||||
|
created,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
|
||||||
|
)
|
||||||
|
}.onFailure { Timber.w(it, "persistable uri permission absent for new folder") }
|
||||||
|
|
||||||
|
return created
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `DocumentsContract.createDocument` attend un URI *document*, pas un URI
|
||||||
|
* *tree*. Convertit un tree URI en document URI (équivalent au dossier
|
||||||
|
* racine de l'arbre) — les deux autorités sont identiques.
|
||||||
|
*/
|
||||||
|
private fun toDocumentUri(uri: String?): Uri? {
|
||||||
|
val raw = uri?.let(Uri::parse) ?: return null
|
||||||
|
return if (DocumentsContract.isTreeUri(raw)) {
|
||||||
|
DocumentsContract.buildDocumentUriUsingTree(
|
||||||
|
raw,
|
||||||
|
DocumentsContract.getTreeDocumentId(raw),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
package com.vaultdrop.mobile.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import com.vaultdrop.mobile.R
|
||||||
|
|
||||||
|
/** Dialog de nommage d'un nouveau dossier — partagé accueil / dossier. */
|
||||||
|
@Composable
|
||||||
|
fun FolderNameDialog(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onConfirm: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
var name by remember { mutableStateOf("") }
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = { Text(stringResource(R.string.new_folder_name_title)) },
|
||||||
|
text = {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = name,
|
||||||
|
onValueChange = { name = it },
|
||||||
|
placeholder = { Text(stringResource(R.string.new_folder_name_hint)) },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = { onConfirm(name.trim()) },
|
||||||
|
enabled = name.isNotBlank(),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.create_folder))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text(stringResource(R.string.pdf_builder_cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user