Merge pull request #13947 from nextcloud/feature/offline-file-remove

Feature - Offline File Deletion
This commit is contained in:
Tobias Kaminsky 2024-11-20 12:26:24 +01:00 committed by GitHub
commit 9d7d5bb283
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 108 additions and 30 deletions

View file

@ -46,6 +46,11 @@ class OfflineOperationTypeAdapter : JsonSerializer<OfflineOperationType>, JsonDe
jsonObject.addProperty("newName", src.newName)
}
is OfflineOperationType.RemoveFile -> {
jsonObject.addProperty("type", src.type)
jsonObject.addProperty("path", src.path)
}
null -> Unit
}
@ -78,6 +83,11 @@ class OfflineOperationTypeAdapter : JsonSerializer<OfflineOperationType>, JsonDe
jsonObject.get("newName").asString
)
OfflineOperationRawType.RemoveFile.name -> OfflineOperationType.RemoveFile(
jsonObject.get("type").asString,
jsonObject.get("path").asString
)
else -> null
}
}

View file

@ -25,6 +25,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation
import com.owncloud.android.operations.CreateFolderOperation
import com.owncloud.android.operations.RemoveFileOperation
import com.owncloud.android.operations.RenameFileOperation
import com.owncloud.android.utils.theme.ViewThemeUtils
import kotlinx.coroutines.Dispatchers
@ -163,6 +164,16 @@ class OfflineOperationsWorker(
renameFileOperation?.execute(client) to renameFileOperation
}
is OfflineOperationType.RemoveFile -> {
val removeFileOperation = withContext(NonCancellable) {
val operationType = (operation.type as OfflineOperationType.RemoveFile)
val ocFile = fileDataStorageManager.getFileByDecryptedRemotePath(operationType.path)
RemoveFileOperation(ocFile, false, user, true, context, fileDataStorageManager)
}
removeFileOperation.execute(client) to removeFileOperation
}
else -> {
Log_OC.d(TAG, "Unsupported operation type: ${operation.type}")
null
@ -186,7 +197,15 @@ class OfflineOperationsWorker(
Log_OC.d(TAG, "$logMessage filename: ${operation.filename}, type: ${operation.type}")
if (result.isSuccess) {
repository.updateNextOperations(operation)
if (operation.type is OfflineOperationType.RemoveFile) {
val operationType = operation.type as OfflineOperationType.RemoveFile
fileDataStorageManager.getFileByDecryptedRemotePath(operationType.path)?.let { ocFile ->
repository.deleteOperation(ocFile)
}
} else {
repository.updateNextOperations(operation)
}
fileDataStorageManager.offlineOperationDao.delete(operation)
notificationManager.update(totalOperations, currentSuccessfulOperationIndex, operation.filename ?: "")
} else {

View file

@ -24,10 +24,13 @@ sealed class OfflineOperationType {
var ocFileId: Long,
val newName: String
) : OfflineOperationType()
data class RemoveFile(override val type: String, var path: String) : OfflineOperationType()
}
enum class OfflineOperationRawType {
CreateFolder,
CreateFile,
RenameFile
RenameFile,
RemoveFile
}

View file

@ -175,6 +175,10 @@ public class FileDataStorageManager {
}
}
public OfflineOperationEntity getOfflineEntityFromOCFile(OCFile file) {
return offlineOperationDao.getByPath(file.getDecryptedRemotePath());
}
public OfflineOperationEntity addCreateFolderOfflineOperation(String path, String filename, Long parentOCFileId) {
OfflineOperationEntity entity = new OfflineOperationEntity();
@ -246,6 +250,25 @@ public class FileDataStorageManager {
return filename;
}
public void addRemoveFileOfflineOperation(String path, String filename, Long parentOCFileId) {
OfflineOperationEntity entity = new OfflineOperationEntity();
entity.setFilename(filename);
entity.setParentOCFileId(parentOCFileId);
OfflineOperationType.RemoveFile operationType = new OfflineOperationType.RemoveFile(OfflineOperationRawType.RemoveFile.name(), path);
entity.setType(operationType);
entity.setPath(path);
long createdAt = System.currentTimeMillis();
long modificationTimestamp = System.currentTimeMillis();
entity.setCreatedAt(createdAt);
entity.setModifiedAt(modificationTimestamp / 1000);
offlineOperationDao.insert(entity);
}
public void renameOfflineOperation(OCFile file, String newFolderName) {
var entity = offlineOperationDao.getByPath(file.getDecryptedRemotePath());
if (entity == null) {

View file

@ -612,28 +612,11 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
localSize = localFile.length();
}
holder.getFileSize().setVisibility(View.VISIBLE);
if (file.isOfflineOperation()) {
holder.getFileSize().setText(MainApp.string(R.string.oc_file_list_adapter_offline_operation_description_text));
} else {
holder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(localSize));
}
holder.getFileSizeSeparator().setVisibility(View.VISIBLE);
prepareFileSize(holder, file, localSize);
} else {
final long fileLength = file.getFileLength();
if (fileLength >= 0) {
holder.getFileSize().setVisibility(View.VISIBLE);
if (file.isOfflineOperation()) {
holder.getFileSize().setText(MainApp.string(R.string.oc_file_list_adapter_offline_operation_description_text));
} else {
holder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(fileLength));
}
holder.getFileSizeSeparator().setVisibility(View.VISIBLE);
prepareFileSize(holder, file, fileLength);
} else {
holder.getFileSize().setVisibility(View.GONE);
holder.getFileSizeSeparator().setVisibility(View.GONE);
@ -671,6 +654,28 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
applyVisualsForOfflineOperations(holder, file);
}
private void prepareFileSize(ListItemViewHolder holder, OCFile file, long size) {
holder.getFileSize().setVisibility(View.VISIBLE);
ViewExtensionsKt.setVisibleIf(holder.getFileSizeSeparator(), !file.isOfflineOperation());
String fileSizeText = getFileSizeText(file, size);
holder.getFileSize().setText(fileSizeText);
}
private String getFileSizeText(OCFile file, long size) {
OfflineOperationEntity entity = mStorageManager.getOfflineEntityFromOCFile(file);
boolean isRemoveOperation = entity != null && entity.getType() instanceof OfflineOperationType.RemoveFile;
if (isRemoveOperation) {
return activity.getString(R.string.oc_file_list_adapter_offline_operation_remove_description_text);
}
if (file.isOfflineOperation()) {
return activity.getString(R.string.oc_file_list_adapter_offline_operation_description_text);
}
return DisplayUtils.bytesToHumanReadable(size);
}
private void applyVisualsForOfflineOperations(ListItemViewHolder holder, OCFile file) {
ViewExtensionsKt.setVisibleIf(holder.getShared(), !file.isOfflineOperation());

View file

@ -90,17 +90,33 @@ class RemoveFilesDialogFragment : ConfirmationDialogFragment(), ConfirmationDial
fileDataStorageManager.deleteOfflineOperation(it)
}
if (files.isNotEmpty()) {
val cg = activity as ComponentsGetter?
cg?.fileOperationsHelper?.removeFiles(files, onlyLocalCopy, false)
}
if (requireActivity() is FileDisplayActivity) {
val activity = requireActivity() as FileDisplayActivity
activity.connectivityService.isNetworkAndServerAvailable { result ->
if (result) {
if (files.isNotEmpty()) {
val cg = activity as ComponentsGetter?
cg?.fileOperationsHelper?.removeFiles(files, onlyLocalCopy, false)
}
if (offlineFiles.isNotEmpty()) {
val activity = requireActivity() as? FileDisplayActivity
activity?.refreshCurrentDirectory()
}
if (offlineFiles.isNotEmpty()) {
activity.refreshCurrentDirectory()
}
} else {
files.forEach { file ->
fileDataStorageManager.addRemoveFileOfflineOperation(
file.decryptedRemotePath,
file.fileName,
file.parentId
)
}
finishActionMode()
activity.refreshCurrentDirectory()
}
finishActionMode()
}
}
}
override fun onNeutral(callerTag: String?) {

View file

@ -1269,4 +1269,6 @@
<string name="two_way_sync_activity_empty_list_desc">To set up a two way sync folder, please enable it in the details tab of the folder in question.</string>
<string name="two_way_sync_activity_title">Internal two way sync</string>
<string name="two_way_sync_activity_disable_all_button_title">Disable for all folders</string>
<string name="oc_file_list_adapter_offline_operation_remove_description_text">Pending Remove Operation</string>
</resources>