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
1,394 changes: 1,394 additions & 0 deletions app/schemas/com.nextcloud.client.database.NextcloudDatabase/107.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ class AutoUploadFragmentIT : AbstractIT() {
false,
SubFolderRule.YEAR_MONTH,
false,
SyncedFolder.NOT_SCANNED_YET
SyncedFolder.NOT_SCANNED_YET,
SyncedFolder.AUTO_DELETE_LOCAL_DISABLED
)

launch { scenario ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ class SyncedFolderUtilsTest : AbstractIT() {
false,
SubFolderRule.YEAR_MONTH,
false,
SyncedFolder.NOT_SCANNED_YET
SyncedFolder.NOT_SCANNED_YET,
SyncedFolder.AUTO_DELETE_LOCAL_DISABLED
)
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
}
Expand All @@ -201,7 +202,8 @@ class SyncedFolderUtilsTest : AbstractIT() {
false,
SubFolderRule.YEAR_MONTH,
false,
SyncedFolder.NOT_SCANNED_YET
SyncedFolder.NOT_SCANNED_YET,
SyncedFolder.AUTO_DELETE_LOCAL_DISABLED
)
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ import com.owncloud.android.db.ProviderMeta
AutoMigration(from = 102, to = 103, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
AutoMigration(from = 103, to = 104),
AutoMigration(from = 104, to = 105),
AutoMigration(from = 105, to = 106, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class)
AutoMigration(from = 105, to = 106, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
AutoMigration(from = 106, to = 107)
],
exportSchema = true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ data class SyncedFolderEntity(
@ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_EXCLUDE_HIDDEN)
val excludeHidden: Int?,
@ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS)
val lastScanTimestampMs: Long?
val lastScanTimestampMs: Long?,
@ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_AUTO_DELETE_LOCAL_AFTER_MS, defaultValue = "0")
val autoDeleteLocalAfterMs: Long? = 0
)

fun SyncedFolderEntity.toSyncedFolder(): SyncedFolder = SyncedFolder(
Expand Down Expand Up @@ -88,5 +90,7 @@ fun SyncedFolderEntity.toSyncedFolder(): SyncedFolder = SyncedFolder(
// excludeHidden
this.excludeHidden == 1,
// lastScanTimestampMs
this.lastScanTimestampMs ?: SyncedFolder.NOT_SCANNED_YET
this.lastScanTimestampMs ?: SyncedFolder.NOT_SCANNED_YET,
// autoDeleteLocalAfterMs
this.autoDeleteLocalAfterMs ?: SyncedFolder.AUTO_DELETE_LOCAL_DISABLED
)
Original file line number Diff line number Diff line change
Expand Up @@ -179,5 +179,5 @@ interface BackgroundJobManager {
fun startMetadataSyncJob(currentDirPath: String, folderAlreadySynced: Boolean = false)
fun downloadFolder(folder: OCFile, accountName: String)
fun cancelFolderDownload()
fun locallyDeleteAutoUploadedFiles(syncedFolders: List<SyncedFolder>)
fun locallyDeleteAutoUploadedFiles(syncedFolders: List<SyncedFolder>, minAgeMs: Long = 0L)
}
Original file line number Diff line number Diff line change
Expand Up @@ -939,20 +939,24 @@ internal class BackgroundJobManagerImpl(
workManager.cancelAllWorkByTag(JOB_DOWNLOAD_FOLDER)
}

override fun locallyDeleteAutoUploadedFiles(syncedFolders: List<SyncedFolder>) {
override fun locallyDeleteAutoUploadedFiles(syncedFolders: List<SyncedFolder>, minAgeMs: Long) {
val syncedFolderIDs = syncedFolders
.filter { it.isEnabled }
.map { it.id }
if (syncedFolderIDs.isEmpty()) {
return
}

val arguments = Data.Builder()
.putLongArray(AutoUploadLocalDeletionWorker.SYNCED_FOLDER_IDS, syncedFolderIDs.toLongArray())
.putLong(AutoUploadLocalDeletionWorker.MIN_AGE_MS, minAgeMs)
.build()

val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()

val jobName = JOB_AUTO_UPLOAD_LOCAL_DELETION + "_" + syncedFolderIDs.joinToString("-")
val jobName = JOB_AUTO_UPLOAD_LOCAL_DELETION + "_" + minAgeMs + "_" + syncedFolderIDs.joinToString("-")
val request = oneTimeRequestBuilder(
jobClass = AutoUploadLocalDeletionWorker::class,
jobName = jobName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class AutoUploadLocalDeletionWorker(

companion object {
const val SYNCED_FOLDER_IDS = "synced_folder_IDs"
const val MIN_AGE_MS = "min_age_ms"
const val NOTIFICATION_ID = 267

private const val TAG = "AutoUploadLocalDeletionWorker"
Expand All @@ -50,9 +51,13 @@ class AutoUploadLocalDeletionWorker(
)

override suspend fun doWork(): Result {
showNotification(
createNotification(context.getString(R.string.autoupload_delete_uploaded_notif_started_title))
)
val minAgeMs = inputData.getLong(MIN_AGE_MS, 0L)
val isAutomaticRun = minAgeMs > 0
if (!isAutomaticRun) {
showNotification(
createNotification(context.getString(R.string.autoupload_delete_uploaded_notif_started_title))
)
}
Log_OC.d(TAG, "Started")

val syncedFolderIDs = inputData.getLongArray(SYNCED_FOLDER_IDS)
Expand All @@ -77,14 +82,19 @@ class AutoUploadLocalDeletionWorker(
val op = DeleteUploadedFileOperation(
it,
context,
fileDataStorageManager
fileDataStorageManager,
minAgeMs
)
val res = op.run()
if (res.code != RemoteOperationResult.ResultCode.OK) {
Log_OC.d(TAG, "Failed")
showNotification(
createNotification(context.getString(R.string.autoupload_delete_uploaded_notif_error_title))
)
if (!isAutomaticRun) {
showNotification(
createNotification(
context.getString(R.string.autoupload_delete_uploaded_notif_error_title)
)
)
}
return Result.failure()
}
foldersAnalyzed++
Expand All @@ -94,16 +104,18 @@ class AutoUploadLocalDeletionWorker(
}

val runTimeMs = System.currentTimeMillis() - timeStarted
showNotification(
createSuccessNotification(
users.size,
foldersAnalyzed,
filesRemoved,
filesPreserved,
spaceFreed,
runTimeMs
if (!isAutomaticRun) {
showNotification(
createSuccessNotification(
users.size,
foldersAnalyzed,
filesRemoved,
filesPreserved,
spaceFreed,
runTimeMs
)
)
)
}
Log_OC.d(
TAG,
"Success: users=$users, foldersAnalyzed=$foldersAnalyzed, filesPreserved=$filesPreserved, " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ class AutoUploadRescanWorker(

Log_OC.d(TAG, "requested auto upload for $requestedFolders folder(s)")

syncedFolderProvider.syncedFolders
.filter { it.isAutoDeleteLocal }
.groupBy { it.autoDeleteLocalAfterMs }
.forEach { (retentionMs, folders) ->
backgroundJobManager.locallyDeleteAutoUploadedFiles(folders, retentionMs)
}

Result.success()
}
}
27 changes: 24 additions & 3 deletions app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.io.File;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;

/**
* Synced folder entity containing all information per synced folder.
Expand All @@ -27,6 +28,8 @@ public class SyncedFolder implements Serializable, Cloneable {
public static final long UNPERSISTED_ID = Long.MIN_VALUE;
public static final long EMPTY_ENABLED_TIMESTAMP_MS = -1;
public static final long NOT_SCANNED_YET = -1;
public static final long AUTO_DELETE_LOCAL_DISABLED = 0;
public static final long DEFAULT_AUTO_DELETE_LOCAL_AFTER_MS = TimeUnit.DAYS.toMillis(60);
private static final long serialVersionUID = -793476118299906429L;


Expand All @@ -48,6 +51,7 @@ public class SyncedFolder implements Serializable, Cloneable {
private SubFolderRule subfolderRule;
private boolean excludeHidden;
private long lastScanTimestampMs;
private long autoDeleteLocalAfterMs;

/**
* constructor for new, to be persisted entity.
Expand All @@ -67,6 +71,7 @@ public class SyncedFolder implements Serializable, Cloneable {
* @param hidden hide item flag
* @param subFolderRule whether to filter subFolder by year/month/day
* @param excludeHidden exclude hidden file or folder, for {@link MediaFolderType#CUSTOM} only
* @param autoDeleteLocalAfterMs age in milliseconds after which uploaded local files are deleted, {@link #AUTO_DELETE_LOCAL_DISABLED} to keep them
*/
public SyncedFolder(String localPath,
String remotePath,
Expand All @@ -83,7 +88,8 @@ public SyncedFolder(String localPath,
boolean hidden,
SubFolderRule subFolderRule,
boolean excludeHidden,
long lastScanTimestampMs) {
long lastScanTimestampMs,
long autoDeleteLocalAfterMs) {
this(UNPERSISTED_ID,
localPath,
remotePath,
Expand All @@ -100,7 +106,8 @@ public SyncedFolder(String localPath,
hidden,
subFolderRule,
excludeHidden,
lastScanTimestampMs);
lastScanTimestampMs,
autoDeleteLocalAfterMs);
}

/**
Expand All @@ -124,7 +131,8 @@ public SyncedFolder(long id,
boolean hidden,
SubFolderRule subFolderRule,
boolean excludeHidden,
long lastScanTimestampMs) {
long lastScanTimestampMs,
long autoDeleteLocalAfterMs) {
this.id = id;
this.localPath = localPath;
this.remotePath = remotePath;
Expand All @@ -141,6 +149,7 @@ public SyncedFolder(long id,
this.subfolderRule = subFolderRule;
this.excludeHidden = excludeHidden;
this.lastScanTimestampMs = lastScanTimestampMs;
this.autoDeleteLocalAfterMs = autoDeleteLocalAfterMs;
}

/**
Expand Down Expand Up @@ -332,6 +341,18 @@ public boolean isFileInFolderWithCorrectMediaType(File file, String filePath) {
return isInLocalDirectory && isCorrectMediaType;
}

public long getAutoDeleteLocalAfterMs() {
return autoDeleteLocalAfterMs;
}

public void setAutoDeleteLocalAfterMs(long autoDeleteLocalAfterMs) {
this.autoDeleteLocalAfterMs = autoDeleteLocalAfterMs;
}

public boolean isAutoDeleteLocal() {
return autoDeleteLocalAfterMs > AUTO_DELETE_LOCAL_DISABLED;
}

public long getLastScanTimestampMs() { return lastScanTimestampMs; }

public void setLastScanTimestampMs(long lastScanTimestampMs) { this.lastScanTimestampMs = lastScanTimestampMs; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public SyncedFolderDisplayItem(long id,
boolean hidden,
SubFolderRule subFolderRule,
boolean excludeHidden,
long lastScanTimestampMs) {
long lastScanTimestampMs,
long autoDeleteLocalAfterMs) {
super(id,
localPath,
remotePath,
Expand All @@ -78,7 +79,8 @@ public SyncedFolderDisplayItem(long id,
hidden,
subFolderRule,
excludeHidden,
lastScanTimestampMs);
lastScanTimestampMs,
autoDeleteLocalAfterMs);
this.filePaths = filePaths;
this.folderName = folderName;
this.numberOfFiles = numberOfFiles;
Expand All @@ -101,7 +103,8 @@ public SyncedFolderDisplayItem(long id,
boolean hidden,
SubFolderRule subFolderRule,
boolean excludeHidden,
long lastScanTimestampMs) {
long lastScanTimestampMs,
long autoDeleteLocalAfterMs) {
super(id,
localPath,
remotePath,
Expand All @@ -118,7 +121,8 @@ public SyncedFolderDisplayItem(long id,
hidden,
subFolderRule,
excludeHidden,
lastScanTimestampMs);
lastScanTimestampMs,
autoDeleteLocalAfterMs);
this.folderName = folderName;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ class SyncedFolderProvider(
bool(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN),
SubFolderRule.entries[int(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE)],
bool(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXCLUDE_HIDDEN),
long(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS)
long(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS),
long(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_AUTO_DELETE_LOCAL_AFTER_MS)
)
}

Expand All @@ -226,5 +227,9 @@ class SyncedFolderProvider(
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS,
syncedFolder.lastScanTimestampMs
)
put(
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_AUTO_DELETE_LOCAL_AFTER_MS,
syncedFolder.autoDeleteLocalAfterMs
)
}
}
3 changes: 2 additions & 1 deletion app/src/main/java/com/owncloud/android/db/ProviderMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/
public class ProviderMeta {
public static final String DB_NAME = "filelist";
public static final int DB_VERSION = 106;
public static final int DB_VERSION = 107;

private ProviderMeta() {
// No instance
Expand Down Expand Up @@ -347,6 +347,7 @@ static public class ProviderTableMeta implements BaseColumns {
public static final String SYNCED_FOLDER_SUBFOLDER_RULE = "sub_folder_rule";
public static final String SYNCED_FOLDER_EXCLUDE_HIDDEN = "exclude_hidden";
public static final String SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS = "last_scan_timestamp_ms";
public static final String SYNCED_FOLDER_AUTO_DELETE_LOCAL_AFTER_MS = "auto_delete_local_after_ms";

// Columns of external links table
public static final String EXTERNAL_LINKS_ICON_URL = "icon_url";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import com.nextcloud.utils.AppConstants.MILLIS_PER_SECOND
class DeleteUploadedFileOperation(
private val syncedFolder: SyncedFolder,
private val context: Context,
private val storageManager: FileDataStorageManager
private val storageManager: FileDataStorageManager,
private val minAgeMs: Long = 0L
) {

companion object {
Expand Down Expand Up @@ -92,7 +93,15 @@ class DeleteUploadedFileOperation(
return result
}

internal fun isOldEnough(localFile: File): Boolean =
System.currentTimeMillis() - localFile.lastModified() >= minAgeMs

private suspend fun fileCanBeDeleted(localFile: File, folder: OCFile): Boolean {
if (!isOldEnough(localFile)) {
Log_OC.d(TAG, "File ${localFile.name} is newer than the retention period, leaving in place")
return false
}

val remotePath = syncFolderHelper.getAutoUploadRemotePath(syncedFolder, localFile)
val ocFile =
storageManager.getFileByRemotePath(remotePath)
Expand Down
Loading
Loading