Loading app/src/main/java/foundation/e/drive/contentScanner/AbstractContentScanner.java +6 −9 Original line number Original line Diff line number Diff line Loading @@ -7,7 +7,6 @@ */ */ package foundation.e.drive.contentScanner; package foundation.e.drive.contentScanner; import android.accounts.Account; import android.content.Context; import android.content.Context; import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.FileUtils; Loading @@ -27,18 +26,16 @@ import foundation.e.drive.models.SyncedFolder; */ */ public abstract class AbstractContentScanner<T> { public abstract class AbstractContentScanner<T> { protected final Context context; protected final Context context; protected final Account account; protected final HashMap<Integer, SyncRequest> syncRequests; protected final HashMap<Integer, SyncRequest> syncRequests; protected final List<SyncedFolder> syncedFolders; protected final List<SyncedFolder> syncedFolders; /** /** * @param context Context used to access Database, etc. * @param context Context used to access Database, etc. * @param account Account used to checked if user has change some synchronization's settings * @param syncedFolders List of SyncedFolders */ */ protected AbstractContentScanner(Context context, Account account, List<SyncedFolder> syncedFolders) { protected AbstractContentScanner(Context context, List<SyncedFolder> syncedFolders) { syncRequests = new HashMap<>(); syncRequests = new HashMap<>(); this.context = context; this.context = context; this.account = account; this.syncedFolders = syncedFolders; this.syncedFolders = syncedFolders; } } Loading Loading @@ -67,7 +64,7 @@ public abstract class AbstractContentScanner<T> { } } for (SyncedFileState remainingFileState : fileStates) { for (SyncedFileState remainingFileState : fileStates) { onMissingRemoteFile(remainingFileState); onMissingFile(remainingFileState); } } return syncRequests; return syncRequests; }; }; Loading @@ -93,7 +90,7 @@ public abstract class AbstractContentScanner<T> { * When a file doesn't exist anymore we remove it from device/cloud (depending of implementation) & from Database * When a file doesn't exist anymore we remove it from device/cloud (depending of implementation) & from Database * @param fileState SyncedFileState for which we lack remote file * @param fileState SyncedFileState for which we lack remote file */ */ protected abstract void onMissingRemoteFile(SyncedFileState fileState); protected abstract void onMissingFile(SyncedFileState fileState); /** /** * A new file has been found * A new file has been found Loading app/src/main/java/foundation/e/drive/contentScanner/LocalContentScanner.java +7 −9 Original line number Original line Diff line number Diff line /* /* * Copyright © ECORP SAS 2022. * Copyright © MURENA SAS 2022. * All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * which accompanies this distribution, and is available at Loading @@ -7,7 +7,6 @@ */ */ package foundation.e.drive.contentScanner; package foundation.e.drive.contentScanner; import android.accounts.Account; import android.content.Context; import android.content.Context; import java.io.File; import java.io.File; Loading @@ -24,16 +23,17 @@ import timber.log.Timber; /** /** * Class to encapsulate function about scanning local file and * Class to encapsulate function about scanning local file and * create syncRequest when needed * create syncRequest when needed * @author vincent Bourgmayer */ */ public class LocalContentScanner extends AbstractContentScanner<File>{ public class LocalContentScanner extends AbstractContentScanner<File>{ public LocalContentScanner(Context context, Account account, List<SyncedFolder> syncedFolders) { public LocalContentScanner(Context context, List<SyncedFolder> syncedFolders) { super(context, account, syncedFolders); super(context, syncedFolders); Timber.tag(LocalContentScanner.class.getSimpleName()); Timber.tag(LocalContentScanner.class.getSimpleName()); } } @Override @Override protected void onMissingRemoteFile(SyncedFileState fileState) { protected void onMissingFile(SyncedFileState fileState) { if (!fileState.hasBeenSynchronizedOnce()) { if (!fileState.hasBeenSynchronizedOnce()) { return; return; } } Loading Loading @@ -61,11 +61,9 @@ public class LocalContentScanner extends AbstractContentScanner<File>{ if (parentDir.isScanLocal()) scannableValue += 2; if (parentDir.isScanLocal()) scannableValue += 2; } } //create the syncedFile State final SyncedFileState newSyncedFileState = new SyncedFileState(-1, file.getName(), filePath, parentDir.getRemoteFolder() + file.getName(), "", 0, parentDir.getId(), parentDir.isMediaType(),scannableValue); final SyncedFileState newSyncedFileState = new SyncedFileState(-1, file.getName(), filePath, parentDir.getRemoteFolder() + file.getName(), "", 0, parentDir.getId(), parentDir.isMediaType(),scannableValue); //Store it in DB final int storedId = DbHelper.manageSyncedFileStateDB(newSyncedFileState, "INSERT", context); int storedId = DbHelper.manageSyncedFileStateDB(newSyncedFileState, "INSERT", context); if (storedId > 0) { if (storedId > 0) { newSyncedFileState.setId( storedId ); newSyncedFileState.setId( storedId ); Timber.d("Add upload SyncRequest for new file %s", filePath); Timber.d("Add upload SyncRequest for new file %s", filePath); Loading app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java +7 −35 Original line number Original line Diff line number Diff line /* /* * Copyright © ECORP SAS 2022. * Copyright © MURENA SAS 2022. * All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * which accompanies this distribution, and is available at Loading @@ -7,19 +7,18 @@ */ */ package foundation.e.drive.contentScanner; package foundation.e.drive.contentScanner; import static foundation.e.drive.models.SyncRequest.Type.LOCAL_DELETE; import static foundation.e.drive.utils.FileDiffUtils.getActionForFileDiff; import static foundation.e.drive.utils.FileDiffUtils.getActionForFileDiff; import android.accounts.Account; import android.content.Context; import android.content.Context; import android.provider.MediaStore; import com.owncloud.android.lib.resources.files.model.RemoteFile; import com.owncloud.android.lib.resources.files.model.RemoteFile; import java.io.File; import java.util.List; import java.util.List; import foundation.e.drive.database.DbHelper; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.DownloadRequest; import foundation.e.drive.models.DownloadRequest; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.CommonUtils; Loading @@ -34,10 +33,9 @@ public class RemoteContentScanner extends AbstractContentScanner<RemoteFile> { /** /** * @param context Context used to access Database, etc. * @param context Context used to access Database, etc. * @param account Account used to checked if user has change some synchronization's settings */ */ public RemoteContentScanner(Context context, Account account, List<SyncedFolder> syncedFolders) { public RemoteContentScanner(Context context, List<SyncedFolder> syncedFolders) { super(context, account, syncedFolders); super(context, syncedFolders); Timber.tag(RemoteContentScanner.class.getSimpleName()); Timber.tag(RemoteContentScanner.class.getSimpleName()); } } Loading Loading @@ -74,7 +72,6 @@ public class RemoteContentScanner extends AbstractContentScanner<RemoteFile> { final SyncedFileState newFileState = new SyncedFileState(-1, fileName, parentDir.getLocalFolder() + fileName, remoteFilePath, file.getEtag(), 0, parentDir.getId(), parentDir.isMediaType(), scannableValue); final SyncedFileState newFileState = new SyncedFileState(-1, fileName, parentDir.getLocalFolder() + fileName, remoteFilePath, file.getEtag(), 0, parentDir.getId(), parentDir.isMediaType(), scannableValue); //Store it in DB final int storedId = DbHelper.manageSyncedFileStateDB(newFileState, "INSERT", context); final int storedId = DbHelper.manageSyncedFileStateDB(newFileState, "INSERT", context); if (storedId > 0) { if (storedId > 0) { newFileState.setId(storedId); newFileState.setId(storedId); Loading @@ -84,33 +81,8 @@ public class RemoteContentScanner extends AbstractContentScanner<RemoteFile> { } } @Override @Override protected void onMissingRemoteFile(SyncedFileState fileState) { protected void onMissingFile(SyncedFileState fileState) { if (!CommonUtils.isThisSyncAllowed(account, fileState.isMediaType())) { this.syncRequests.put(fileState.getId(), new SyncRequest(fileState, LOCAL_DELETE)); Timber.d("Sync of current file: %s isn't allowed", fileState.getName()); return; } if (!fileState.hasBeenSynchronizedOnce()) { return; } final File file = new File(fileState.getLocalPath()); if (!file.exists()) { return; } context.getContentResolver().delete(MediaStore.Files.getContentUri("external"), MediaStore.Files.FileColumns.DATA + "=?", new String[]{CommonUtils.getLocalPath(file)}); if (!file.delete()) { //May throw SecurityException or IOException Timber.d("local file ( %s ) removal failed",file.getName()); return; } if (DbHelper.manageSyncedFileStateDB(fileState, "DELETE", context) <= 0) { Timber.e("Failed to remove %s from DB", file.getName()); } } } @Override @Override Loading app/src/main/java/foundation/e/drive/database/SyncedFileStateDAO.java +18 −6 Original line number Original line Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.database.sqlite.SQLiteDoneException; import android.database.sqlite.SQLiteStatement; import android.database.sqlite.SQLiteStatement; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.List; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFileState; Loading Loading @@ -195,15 +196,26 @@ import static foundation.e.drive.database.SyncedFileStateContract.SYNCEDFOLDER_I /* package */ List<SyncedFileState> getBySyncedFolderIds(List<Long> syncedFolderIds) { /* package */ List<SyncedFileState> getBySyncedFolderIds(List<Long> syncedFolderIds) { final List<SyncedFileState> result = new ArrayList<>(); final List<SyncedFileState> result = new ArrayList<>(); if (syncedFolderIds == null || syncedFolderIds.isEmpty()) { if (syncedFolderIds == null || syncedFolderIds.isEmpty()) { Timber.d("getBySyncedFolderIds(): SyncedFOlderIds is empty or null"); return result; return result; } } final String whereClause = SYNCEDFOLDER_ID + " IN (?)"; final String[] whereValue = new String[] { final List<String> matcherList = new ArrayList<>(); //list of "?" to be replaced by value by SQliteDatabase.query() call syncedFolderIds.toString() final String[] whereValue = new String[syncedFolderIds.size()]; for (int i = 0; i < syncedFolderIds.size(); i++) { matcherList.add("?"); whereValue[i] = syncedFolderIds.get(i).toString(); } final StringBuilder whereClause = new StringBuilder(); whereClause.append(SYNCEDFOLDER_ID) .append(" IN ") .append(matcherList.toString() .replace("[", "(") .replace("[", "(") .replace("]", ")") }; .replace("]", ")")); final Cursor cursor = mDB.query(TABLE_NAME, allColumns, whereClause, whereValue, null, null, null); final Cursor cursor = mDB.query(TABLE_NAME, allColumns, whereClause.toString(), whereValue, null, null, null); cursor.moveToFirst(); cursor.moveToFirst(); while(!cursor.isAfterLast() ) { while(!cursor.isAfterLast() ) { result.add( cursorToSyncedFileState(cursor) ); result.add( cursorToSyncedFileState(cursor) ); Loading app/src/main/java/foundation/e/drive/models/SyncRequest.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -10,7 +10,7 @@ package foundation.e.drive.models; import androidx.annotation.Nullable; import androidx.annotation.Nullable; public class SyncRequest { public class SyncRequest { public enum Type { UPLOAD, DOWNLOAD, REMOTE_DELETE}; public enum Type { UPLOAD, DOWNLOAD, REMOTE_DELETE, LOCAL_DELETE}; private final SyncedFileState syncedFileState; private final SyncedFileState syncedFileState; Loading Loading
app/src/main/java/foundation/e/drive/contentScanner/AbstractContentScanner.java +6 −9 Original line number Original line Diff line number Diff line Loading @@ -7,7 +7,6 @@ */ */ package foundation.e.drive.contentScanner; package foundation.e.drive.contentScanner; import android.accounts.Account; import android.content.Context; import android.content.Context; import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.FileUtils; Loading @@ -27,18 +26,16 @@ import foundation.e.drive.models.SyncedFolder; */ */ public abstract class AbstractContentScanner<T> { public abstract class AbstractContentScanner<T> { protected final Context context; protected final Context context; protected final Account account; protected final HashMap<Integer, SyncRequest> syncRequests; protected final HashMap<Integer, SyncRequest> syncRequests; protected final List<SyncedFolder> syncedFolders; protected final List<SyncedFolder> syncedFolders; /** /** * @param context Context used to access Database, etc. * @param context Context used to access Database, etc. * @param account Account used to checked if user has change some synchronization's settings * @param syncedFolders List of SyncedFolders */ */ protected AbstractContentScanner(Context context, Account account, List<SyncedFolder> syncedFolders) { protected AbstractContentScanner(Context context, List<SyncedFolder> syncedFolders) { syncRequests = new HashMap<>(); syncRequests = new HashMap<>(); this.context = context; this.context = context; this.account = account; this.syncedFolders = syncedFolders; this.syncedFolders = syncedFolders; } } Loading Loading @@ -67,7 +64,7 @@ public abstract class AbstractContentScanner<T> { } } for (SyncedFileState remainingFileState : fileStates) { for (SyncedFileState remainingFileState : fileStates) { onMissingRemoteFile(remainingFileState); onMissingFile(remainingFileState); } } return syncRequests; return syncRequests; }; }; Loading @@ -93,7 +90,7 @@ public abstract class AbstractContentScanner<T> { * When a file doesn't exist anymore we remove it from device/cloud (depending of implementation) & from Database * When a file doesn't exist anymore we remove it from device/cloud (depending of implementation) & from Database * @param fileState SyncedFileState for which we lack remote file * @param fileState SyncedFileState for which we lack remote file */ */ protected abstract void onMissingRemoteFile(SyncedFileState fileState); protected abstract void onMissingFile(SyncedFileState fileState); /** /** * A new file has been found * A new file has been found Loading
app/src/main/java/foundation/e/drive/contentScanner/LocalContentScanner.java +7 −9 Original line number Original line Diff line number Diff line /* /* * Copyright © ECORP SAS 2022. * Copyright © MURENA SAS 2022. * All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * which accompanies this distribution, and is available at Loading @@ -7,7 +7,6 @@ */ */ package foundation.e.drive.contentScanner; package foundation.e.drive.contentScanner; import android.accounts.Account; import android.content.Context; import android.content.Context; import java.io.File; import java.io.File; Loading @@ -24,16 +23,17 @@ import timber.log.Timber; /** /** * Class to encapsulate function about scanning local file and * Class to encapsulate function about scanning local file and * create syncRequest when needed * create syncRequest when needed * @author vincent Bourgmayer */ */ public class LocalContentScanner extends AbstractContentScanner<File>{ public class LocalContentScanner extends AbstractContentScanner<File>{ public LocalContentScanner(Context context, Account account, List<SyncedFolder> syncedFolders) { public LocalContentScanner(Context context, List<SyncedFolder> syncedFolders) { super(context, account, syncedFolders); super(context, syncedFolders); Timber.tag(LocalContentScanner.class.getSimpleName()); Timber.tag(LocalContentScanner.class.getSimpleName()); } } @Override @Override protected void onMissingRemoteFile(SyncedFileState fileState) { protected void onMissingFile(SyncedFileState fileState) { if (!fileState.hasBeenSynchronizedOnce()) { if (!fileState.hasBeenSynchronizedOnce()) { return; return; } } Loading Loading @@ -61,11 +61,9 @@ public class LocalContentScanner extends AbstractContentScanner<File>{ if (parentDir.isScanLocal()) scannableValue += 2; if (parentDir.isScanLocal()) scannableValue += 2; } } //create the syncedFile State final SyncedFileState newSyncedFileState = new SyncedFileState(-1, file.getName(), filePath, parentDir.getRemoteFolder() + file.getName(), "", 0, parentDir.getId(), parentDir.isMediaType(),scannableValue); final SyncedFileState newSyncedFileState = new SyncedFileState(-1, file.getName(), filePath, parentDir.getRemoteFolder() + file.getName(), "", 0, parentDir.getId(), parentDir.isMediaType(),scannableValue); //Store it in DB final int storedId = DbHelper.manageSyncedFileStateDB(newSyncedFileState, "INSERT", context); int storedId = DbHelper.manageSyncedFileStateDB(newSyncedFileState, "INSERT", context); if (storedId > 0) { if (storedId > 0) { newSyncedFileState.setId( storedId ); newSyncedFileState.setId( storedId ); Timber.d("Add upload SyncRequest for new file %s", filePath); Timber.d("Add upload SyncRequest for new file %s", filePath); Loading
app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java +7 −35 Original line number Original line Diff line number Diff line /* /* * Copyright © ECORP SAS 2022. * Copyright © MURENA SAS 2022. * All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * which accompanies this distribution, and is available at Loading @@ -7,19 +7,18 @@ */ */ package foundation.e.drive.contentScanner; package foundation.e.drive.contentScanner; import static foundation.e.drive.models.SyncRequest.Type.LOCAL_DELETE; import static foundation.e.drive.utils.FileDiffUtils.getActionForFileDiff; import static foundation.e.drive.utils.FileDiffUtils.getActionForFileDiff; import android.accounts.Account; import android.content.Context; import android.content.Context; import android.provider.MediaStore; import com.owncloud.android.lib.resources.files.model.RemoteFile; import com.owncloud.android.lib.resources.files.model.RemoteFile; import java.io.File; import java.util.List; import java.util.List; import foundation.e.drive.database.DbHelper; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.DownloadRequest; import foundation.e.drive.models.DownloadRequest; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.CommonUtils; Loading @@ -34,10 +33,9 @@ public class RemoteContentScanner extends AbstractContentScanner<RemoteFile> { /** /** * @param context Context used to access Database, etc. * @param context Context used to access Database, etc. * @param account Account used to checked if user has change some synchronization's settings */ */ public RemoteContentScanner(Context context, Account account, List<SyncedFolder> syncedFolders) { public RemoteContentScanner(Context context, List<SyncedFolder> syncedFolders) { super(context, account, syncedFolders); super(context, syncedFolders); Timber.tag(RemoteContentScanner.class.getSimpleName()); Timber.tag(RemoteContentScanner.class.getSimpleName()); } } Loading Loading @@ -74,7 +72,6 @@ public class RemoteContentScanner extends AbstractContentScanner<RemoteFile> { final SyncedFileState newFileState = new SyncedFileState(-1, fileName, parentDir.getLocalFolder() + fileName, remoteFilePath, file.getEtag(), 0, parentDir.getId(), parentDir.isMediaType(), scannableValue); final SyncedFileState newFileState = new SyncedFileState(-1, fileName, parentDir.getLocalFolder() + fileName, remoteFilePath, file.getEtag(), 0, parentDir.getId(), parentDir.isMediaType(), scannableValue); //Store it in DB final int storedId = DbHelper.manageSyncedFileStateDB(newFileState, "INSERT", context); final int storedId = DbHelper.manageSyncedFileStateDB(newFileState, "INSERT", context); if (storedId > 0) { if (storedId > 0) { newFileState.setId(storedId); newFileState.setId(storedId); Loading @@ -84,33 +81,8 @@ public class RemoteContentScanner extends AbstractContentScanner<RemoteFile> { } } @Override @Override protected void onMissingRemoteFile(SyncedFileState fileState) { protected void onMissingFile(SyncedFileState fileState) { if (!CommonUtils.isThisSyncAllowed(account, fileState.isMediaType())) { this.syncRequests.put(fileState.getId(), new SyncRequest(fileState, LOCAL_DELETE)); Timber.d("Sync of current file: %s isn't allowed", fileState.getName()); return; } if (!fileState.hasBeenSynchronizedOnce()) { return; } final File file = new File(fileState.getLocalPath()); if (!file.exists()) { return; } context.getContentResolver().delete(MediaStore.Files.getContentUri("external"), MediaStore.Files.FileColumns.DATA + "=?", new String[]{CommonUtils.getLocalPath(file)}); if (!file.delete()) { //May throw SecurityException or IOException Timber.d("local file ( %s ) removal failed",file.getName()); return; } if (DbHelper.manageSyncedFileStateDB(fileState, "DELETE", context) <= 0) { Timber.e("Failed to remove %s from DB", file.getName()); } } } @Override @Override Loading
app/src/main/java/foundation/e/drive/database/SyncedFileStateDAO.java +18 −6 Original line number Original line Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.database.sqlite.SQLiteDoneException; import android.database.sqlite.SQLiteStatement; import android.database.sqlite.SQLiteStatement; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.List; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFileState; Loading Loading @@ -195,15 +196,26 @@ import static foundation.e.drive.database.SyncedFileStateContract.SYNCEDFOLDER_I /* package */ List<SyncedFileState> getBySyncedFolderIds(List<Long> syncedFolderIds) { /* package */ List<SyncedFileState> getBySyncedFolderIds(List<Long> syncedFolderIds) { final List<SyncedFileState> result = new ArrayList<>(); final List<SyncedFileState> result = new ArrayList<>(); if (syncedFolderIds == null || syncedFolderIds.isEmpty()) { if (syncedFolderIds == null || syncedFolderIds.isEmpty()) { Timber.d("getBySyncedFolderIds(): SyncedFOlderIds is empty or null"); return result; return result; } } final String whereClause = SYNCEDFOLDER_ID + " IN (?)"; final String[] whereValue = new String[] { final List<String> matcherList = new ArrayList<>(); //list of "?" to be replaced by value by SQliteDatabase.query() call syncedFolderIds.toString() final String[] whereValue = new String[syncedFolderIds.size()]; for (int i = 0; i < syncedFolderIds.size(); i++) { matcherList.add("?"); whereValue[i] = syncedFolderIds.get(i).toString(); } final StringBuilder whereClause = new StringBuilder(); whereClause.append(SYNCEDFOLDER_ID) .append(" IN ") .append(matcherList.toString() .replace("[", "(") .replace("[", "(") .replace("]", ")") }; .replace("]", ")")); final Cursor cursor = mDB.query(TABLE_NAME, allColumns, whereClause, whereValue, null, null, null); final Cursor cursor = mDB.query(TABLE_NAME, allColumns, whereClause.toString(), whereValue, null, null, null); cursor.moveToFirst(); cursor.moveToFirst(); while(!cursor.isAfterLast() ) { while(!cursor.isAfterLast() ) { result.add( cursorToSyncedFileState(cursor) ); result.add( cursorToSyncedFileState(cursor) ); Loading
app/src/main/java/foundation/e/drive/models/SyncRequest.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -10,7 +10,7 @@ package foundation.e.drive.models; import androidx.annotation.Nullable; import androidx.annotation.Nullable; public class SyncRequest { public class SyncRequest { public enum Type { UPLOAD, DOWNLOAD, REMOTE_DELETE}; public enum Type { UPLOAD, DOWNLOAD, REMOTE_DELETE, LOCAL_DELETE}; private final SyncedFileState syncedFileState; private final SyncedFileState syncedFileState; Loading