mirror of
https://github.com/nextcloud/android.git
synced 2024-11-29 10:49:04 +03:00
Merge pull request #4788 from nextcloud/instantupload_all
Setting to also upload existing files
This commit is contained in:
commit
5d428fe9a2
34 changed files with 1576 additions and 1263 deletions
|
@ -1 +1 @@
|
|||
385
|
||||
383
|
||||
|
|
|
@ -120,7 +120,7 @@ public class UploadIT extends AbstractIT {
|
|||
account,
|
||||
null,
|
||||
ocUpload,
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
|
@ -140,17 +140,17 @@ public class UploadIT extends AbstractIT {
|
|||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/empty.txt",
|
||||
"/testUpload/2/3/4/1.txt", account.name);
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
storageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
account,
|
||||
null,
|
||||
ocUpload,
|
||||
false,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false
|
||||
storageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
account,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false
|
||||
);
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
|
|
|
@ -773,7 +773,7 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
|
|||
|
||||
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
|
||||
if (syncedFolder.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder);
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ public class SyncedFolder implements Serializable, Cloneable {
|
|||
@Getter @Setter private String remotePath;
|
||||
@Getter @Setter private boolean wifiOnly;
|
||||
@Getter @Setter private boolean chargingOnly;
|
||||
@Getter @Setter private boolean existing;
|
||||
@Getter @Setter private boolean subfolderByDate;
|
||||
@Getter @Setter private String account;
|
||||
@Getter @Setter private int uploadAction;
|
||||
|
@ -54,6 +55,7 @@ public class SyncedFolder implements Serializable, Cloneable {
|
|||
* @param remotePath remote path
|
||||
* @param wifiOnly upload on wifi only flag
|
||||
* @param chargingOnly upload on charging only
|
||||
* @param existing upload existing files
|
||||
* @param subfolderByDate create sub-folders by date (month)
|
||||
* @param account the account owning the synced folder
|
||||
* @param uploadAction the action to be done after the upload
|
||||
|
@ -66,6 +68,7 @@ public class SyncedFolder implements Serializable, Cloneable {
|
|||
String remotePath,
|
||||
boolean wifiOnly,
|
||||
boolean chargingOnly,
|
||||
boolean existing,
|
||||
boolean subfolderByDate,
|
||||
String account,
|
||||
int uploadAction,
|
||||
|
@ -73,8 +76,8 @@ public class SyncedFolder implements Serializable, Cloneable {
|
|||
long timestampMs,
|
||||
MediaFolderType type,
|
||||
boolean hidden) {
|
||||
this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction,
|
||||
enabled, timestampMs, type, hidden);
|
||||
this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account,
|
||||
uploadAction, enabled, timestampMs, type, hidden);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,6 +90,7 @@ public class SyncedFolder implements Serializable, Cloneable {
|
|||
String remotePath,
|
||||
boolean wifiOnly,
|
||||
boolean chargingOnly,
|
||||
boolean existing,
|
||||
boolean subfolderByDate,
|
||||
String account,
|
||||
int uploadAction,
|
||||
|
@ -99,6 +103,7 @@ public class SyncedFolder implements Serializable, Cloneable {
|
|||
this.remotePath = remotePath;
|
||||
this.wifiOnly = wifiOnly;
|
||||
this.chargingOnly = chargingOnly;
|
||||
this.existing = existing;
|
||||
this.subfolderByDate = subfolderByDate;
|
||||
this.account = account;
|
||||
this.uploadAction = uploadAction;
|
||||
|
|
|
@ -45,6 +45,7 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
|
|||
* @param remotePath remote path
|
||||
* @param wifiOnly upload on wifi only flag
|
||||
* @param chargingOnly upload on charging only
|
||||
* @param existing also upload existing
|
||||
* @param subfolderByDate create sub-folders by date (month)
|
||||
* @param account the account owning the synced folder
|
||||
* @param uploadAction the action to be done after the upload
|
||||
|
@ -60,6 +61,7 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
|
|||
String remotePath,
|
||||
boolean wifiOnly,
|
||||
boolean chargingOnly,
|
||||
boolean existing,
|
||||
boolean subfolderByDate,
|
||||
String account,
|
||||
int uploadAction,
|
||||
|
@ -70,8 +72,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
|
|||
long numberOfFiles,
|
||||
MediaFolderType type,
|
||||
boolean hidden) {
|
||||
super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled,
|
||||
timestampMs, type, hidden);
|
||||
super(id, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account, uploadAction,
|
||||
enabled, timestampMs, type, hidden);
|
||||
this.filePaths = filePaths;
|
||||
this.folderName = folderName;
|
||||
this.numberOfFiles = numberOfFiles;
|
||||
|
@ -82,14 +84,15 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
|
|||
String remotePath,
|
||||
boolean wifiOnly,
|
||||
boolean chargingOnly,
|
||||
boolean existing,
|
||||
boolean subfolderByDate,
|
||||
String account,
|
||||
int uploadAction,
|
||||
boolean enabled,
|
||||
long timestampMs,
|
||||
String folderName, MediaFolderType type, boolean hidden) {
|
||||
super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled,
|
||||
timestampMs, type, hidden);
|
||||
super(id, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account, uploadAction,
|
||||
enabled, timestampMs, type, hidden);
|
||||
this.folderName = folderName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -342,6 +342,8 @@ public class SyncedFolderProvider extends Observable {
|
|||
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1;
|
||||
boolean chargingOnly = cursor.getInt(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1;
|
||||
boolean existing = cursor.getInt(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXISTING)) == 1;
|
||||
boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1;
|
||||
String accountName = cursor.getString(cursor.getColumnIndex(
|
||||
|
@ -357,8 +359,9 @@ public class SyncedFolderProvider extends Observable {
|
|||
boolean hidden = cursor.getInt(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN)) == 1;
|
||||
|
||||
syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate,
|
||||
accountName, uploadAction, enabled, enabledTimestampMs, type, hidden);
|
||||
syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, existing,
|
||||
subfolderByDate, accountName, uploadAction, enabled, enabledTimestampMs,
|
||||
type, hidden);
|
||||
}
|
||||
return syncedFolder;
|
||||
}
|
||||
|
@ -376,6 +379,7 @@ public class SyncedFolderProvider extends Observable {
|
|||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH, syncedFolder.getRemotePath());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.isWifiOnly());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.isChargingOnly());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXISTING, syncedFolder.isExisting());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED, syncedFolder.isEnabled());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS, syncedFolder.getEnabledTimestampMs());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.isSubfolderByDate());
|
||||
|
|
|
@ -84,7 +84,7 @@ public class UploadsStorageManager extends Observable {
|
|||
cv.put(ProviderTableMeta.UPLOADS_FILE_SIZE, ocUpload.getFileSize());
|
||||
cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value);
|
||||
cv.put(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR, ocUpload.getLocalAction());
|
||||
cv.put(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE, ocUpload.isForceOverwrite() ? 1 : 0);
|
||||
cv.put(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY, ocUpload.getNameCollisionPolicy().serialize());
|
||||
cv.put(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER, ocUpload.isCreateRemoteFolder() ? 1 : 0);
|
||||
cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue());
|
||||
cv.put(ProviderTableMeta.UPLOADS_CREATED_BY, ocUpload.getCreatedBy());
|
||||
|
@ -329,8 +329,8 @@ public class UploadsStorageManager extends Observable {
|
|||
UploadStatus.fromValue(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_STATUS)))
|
||||
);
|
||||
upload.setLocalAction(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR)));
|
||||
upload.setForceOverwrite(c.getInt(
|
||||
c.getColumnIndex(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE)) == 1);
|
||||
upload.setNameCollisionPolicy(FileUploader.NameCollisionPolicy.deserialize(c.getInt(
|
||||
c.getColumnIndex(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY))));
|
||||
upload.setCreateRemoteFolder(c.getInt(
|
||||
c.getColumnIndex(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER)) == 1);
|
||||
upload.setUploadEndTimestamp(c.getLong(c.getColumnIndex(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP)));
|
||||
|
|
|
@ -77,9 +77,9 @@ public class OCUpload implements Parcelable {
|
|||
@Getter @Setter private int localAction;
|
||||
|
||||
/**
|
||||
* Overwrite destination file?
|
||||
* What to do in case of name collision.
|
||||
*/
|
||||
@Getter @Setter private boolean forceOverwrite;
|
||||
@Getter @Setter private FileUploader.NameCollisionPolicy nameCollisionPolicy;
|
||||
|
||||
/**
|
||||
* Create destination folder?
|
||||
|
@ -172,7 +172,7 @@ public class OCUpload implements Parcelable {
|
|||
fileSize = -1;
|
||||
uploadId = -1;
|
||||
localAction = FileUploader.LOCAL_BEHAVIOUR_COPY;
|
||||
forceOverwrite = false;
|
||||
nameCollisionPolicy = FileUploader.NameCollisionPolicy.DEFAULT;
|
||||
createRemoteFolder = false;
|
||||
uploadStatus = UploadStatus.UPLOAD_IN_PROGRESS;
|
||||
lastResult = UploadResult.UNKNOWN;
|
||||
|
@ -281,7 +281,7 @@ public class OCUpload implements Parcelable {
|
|||
remotePath = source.readString();
|
||||
accountName = source.readString();
|
||||
localAction = source.readInt();
|
||||
forceOverwrite = source.readInt() == 1;
|
||||
nameCollisionPolicy = FileUploader.NameCollisionPolicy.deserialize(source.readInt());
|
||||
createRemoteFolder = source.readInt() == 1;
|
||||
try {
|
||||
uploadStatus = UploadStatus.valueOf(source.readString());
|
||||
|
@ -312,7 +312,7 @@ public class OCUpload implements Parcelable {
|
|||
dest.writeString(remotePath);
|
||||
dest.writeString(accountName);
|
||||
dest.writeInt(localAction);
|
||||
dest.writeInt(forceOverwrite ? 1 : 0);
|
||||
dest.writeInt(nameCollisionPolicy.serialize());
|
||||
dest.writeInt(createRemoteFolder ? 1 : 0);
|
||||
dest.writeString(uploadStatus.name());
|
||||
dest.writeLong(uploadEndTimestamp);
|
||||
|
|
|
@ -31,7 +31,7 @@ import com.owncloud.android.MainApp;
|
|||
*/
|
||||
public class ProviderMeta {
|
||||
public static final String DB_NAME = "filelist";
|
||||
public static final int DB_VERSION = 53;
|
||||
public static final int DB_VERSION = 54;
|
||||
|
||||
private ProviderMeta() {
|
||||
// No instance
|
||||
|
@ -208,7 +208,7 @@ public class ProviderMeta {
|
|||
public static final String UPLOADS_STATUS = "status";
|
||||
public static final String UPLOADS_LOCAL_BEHAVIOUR = "local_behaviour";
|
||||
public static final String UPLOADS_UPLOAD_TIME = "upload_time";
|
||||
public static final String UPLOADS_FORCE_OVERWRITE = "force_overwrite";
|
||||
public static final String UPLOADS_NAME_COLLISION_POLICY = "name_collision_policy";
|
||||
public static final String UPLOADS_IS_CREATE_REMOTE_FOLDER = "is_create_remote_folder";
|
||||
public static final String UPLOADS_UPLOAD_END_TIMESTAMP = "upload_end_timestamp";
|
||||
public static final String UPLOADS_LAST_RESULT = "last_result";
|
||||
|
@ -223,6 +223,7 @@ public class ProviderMeta {
|
|||
public static final String SYNCED_FOLDER_REMOTE_PATH = "remote_path";
|
||||
public static final String SYNCED_FOLDER_WIFI_ONLY = "wifi_only";
|
||||
public static final String SYNCED_FOLDER_CHARGING_ONLY = "charging_only";
|
||||
public static final String SYNCED_FOLDER_EXISTING = "existing";
|
||||
public static final String SYNCED_FOLDER_ENABLED = "enabled";
|
||||
public static final String SYNCED_FOLDER_ENABLED_TIMESTAMP_MS = "enabled_timestamp_ms";
|
||||
public static final String SYNCED_FOLDER_TYPE = "type";
|
||||
|
|
|
@ -43,6 +43,8 @@ import com.owncloud.android.R;
|
|||
import com.owncloud.android.authentication.AuthenticatorActivity;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.lib.common.OwnCloudAccount;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
|
||||
|
@ -87,6 +89,7 @@ public class FileDownloader extends Service
|
|||
public static final String EXTRA_FILE_PATH = "FILE_PATH";
|
||||
public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
|
||||
public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
|
||||
public static final String EXTRA_CONFLICT_UPLOAD = "CONFLICT_UPLOAD";
|
||||
public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
|
||||
|
||||
private static final int FOREGROUND_SERVICE_ID = 412;
|
||||
|
@ -110,7 +113,10 @@ public class FileDownloader extends Service
|
|||
|
||||
private Notification mNotification;
|
||||
|
||||
private OCUpload conflictUpload;
|
||||
|
||||
@Inject UserAccountManager accountManager;
|
||||
@Inject UploadsStorageManager uploadsStorageManager;
|
||||
|
||||
public static String getDownloadAddedMessage() {
|
||||
return FileDownloader.class.getName() + DOWNLOAD_ADDED_MESSAGE;
|
||||
|
@ -195,6 +201,7 @@ public class FileDownloader extends Service
|
|||
final String behaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR);
|
||||
String activityName = intent.getStringExtra(SendShareDialog.ACTIVITY_NAME);
|
||||
String packageName = intent.getStringExtra(SendShareDialog.PACKAGE_NAME);
|
||||
this.conflictUpload = intent.getParcelableExtra(FileDownloader.EXTRA_CONFLICT_UPLOAD);
|
||||
AbstractList<String> requestedDownloads = new Vector<String>();
|
||||
try {
|
||||
DownloadFileOperation newDownload = new DownloadFileOperation(account, file, behaviour, activityName,
|
||||
|
@ -634,6 +641,10 @@ public class FileDownloader extends Service
|
|||
|
||||
// Remove success notification
|
||||
if (downloadResult.isSuccess()) {
|
||||
if (this.conflictUpload != null) {
|
||||
uploadsStorageManager.removeUpload(this.conflictUpload);
|
||||
}
|
||||
|
||||
// Sleep 2 seconds, so show the notification before remove it
|
||||
NotificationUtils.cancelWithDelay(mNotificationManager,
|
||||
R.string.downloader_download_succeeded_ticker, 2000);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -169,18 +169,18 @@ public class ContactsBackupJob extends Job {
|
|||
}
|
||||
}
|
||||
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadNewFile(
|
||||
getContext(),
|
||||
user.toPlatformAccount(),
|
||||
file.getAbsolutePath(),
|
||||
backupFolder + filename,
|
||||
FileUploader.LOCAL_BEHAVIOUR_MOVE,
|
||||
null,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
FileUploader.uploadNewFile(
|
||||
getContext(),
|
||||
user.toPlatformAccount(),
|
||||
file.getAbsolutePath(),
|
||||
backupFolder + filename,
|
||||
FileUploader.LOCAL_BEHAVIOUR_MOVE,
|
||||
null,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ public class FilesSyncJob extends Job {
|
|||
userAccountManager,
|
||||
connectivityService,
|
||||
powerManagementService);
|
||||
FilesSyncHelper.insertAllDBEntries(preferences, clock, skipCustom);
|
||||
FilesSyncHelper.insertAllDBEntries(preferences, clock, skipCustom, false);
|
||||
|
||||
// Create all the providers we'll need
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
|
@ -141,12 +141,11 @@ public class FilesSyncJob extends Job {
|
|||
Locale currentLocale = context.getResources().getConfiguration().locale;
|
||||
SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale);
|
||||
sFormatter.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID()));
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
|
||||
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
|
||||
if ((syncedFolder.isEnabled()) && (!skipCustom || MediaFolderType.CUSTOM != syncedFolder.getType())) {
|
||||
syncFolder(context, resources, lightVersion, filesystemDataProvider, currentLocale, sFormatter,
|
||||
requester, syncedFolder);
|
||||
syncedFolder);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,10 +156,15 @@ public class FilesSyncJob extends Job {
|
|||
return Result.SUCCESS;
|
||||
}
|
||||
|
||||
private void syncFolder(Context context, Resources resources, boolean lightVersion,
|
||||
FilesystemDataProvider filesystemDataProvider, Locale currentLocale,
|
||||
SimpleDateFormat sFormatter, FileUploader.UploadRequester requester,
|
||||
SyncedFolder syncedFolder) {
|
||||
private void syncFolder(
|
||||
Context context,
|
||||
Resources resources,
|
||||
boolean lightVersion,
|
||||
FilesystemDataProvider filesystemDataProvider,
|
||||
Locale currentLocale,
|
||||
SimpleDateFormat sFormatter,
|
||||
SyncedFolder syncedFolder
|
||||
) {
|
||||
String remotePath;
|
||||
boolean subfolderByDate;
|
||||
Integer uploadAction;
|
||||
|
@ -203,24 +207,24 @@ public class FilesSyncJob extends Job {
|
|||
remotePath = syncedFolder.getRemotePath();
|
||||
}
|
||||
|
||||
requester.uploadFileWithOverwrite(
|
||||
context,
|
||||
user.toPlatformAccount(),
|
||||
file.getAbsolutePath(),
|
||||
FileStorageUtils.getInstantUploadFilePath(
|
||||
FileUploader.uploadNewFile(
|
||||
context,
|
||||
user.toPlatformAccount(),
|
||||
file.getAbsolutePath(),
|
||||
FileStorageUtils.getInstantUploadFilePath(
|
||||
file,
|
||||
currentLocale,
|
||||
remotePath,
|
||||
syncedFolder.getLocalPath(),
|
||||
lastModificationTime,
|
||||
subfolderByDate),
|
||||
uploadAction,
|
||||
mimeType,
|
||||
true, // create parent folder if not existent
|
||||
UploadFileOperation.CREATED_AS_INSTANT_PICTURE,
|
||||
needsWifi,
|
||||
needsCharging,
|
||||
true
|
||||
uploadAction,
|
||||
mimeType,
|
||||
true, // create parent folder if not existent
|
||||
UploadFileOperation.CREATED_AS_INSTANT_PICTURE,
|
||||
needsWifi,
|
||||
needsCharging,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
|
||||
filesystemDataProvider.updateFilesystemFileAsSentForUpload(path,
|
||||
|
|
|
@ -291,8 +291,13 @@ public class SynchronizeFileOperation extends SyncOperation {
|
|||
* @param file OCFile object representing the file to upload
|
||||
*/
|
||||
private void requestForUpload(OCFile file) {
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadUpdate(mContext, mAccount, file, FileUploader.LOCAL_BEHAVIOUR_MOVE, true);
|
||||
FileUploader.uploadUpdateFile(
|
||||
mContext,
|
||||
mAccount,
|
||||
file,
|
||||
FileUploader.LOCAL_BEHAVIOUR_MOVE,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
|
||||
mTransferWasRequested = true;
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import androidx.annotation.CheckResult;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
|
||||
|
@ -111,14 +112,14 @@ public class UploadFileOperation extends SyncOperation {
|
|||
private OCFile mFile;
|
||||
|
||||
/**
|
||||
* Original OCFile which is to be uploaded in case file had to be renamed
|
||||
* (if forceOverwrite==false and remote file already exists).
|
||||
* Original OCFile which is to be uploaded in case file had to be renamed (if nameCollisionPolicy==RENAME and remote
|
||||
* file already exists).
|
||||
*/
|
||||
private OCFile mOldFile;
|
||||
private String mRemotePath;
|
||||
private String mFolderUnlockToken;
|
||||
private boolean mRemoteFolderToBeCreated;
|
||||
private boolean mForceOverwrite;
|
||||
private FileUploader.NameCollisionPolicy mNameCollisionPolicy;
|
||||
private int mLocalBehaviour;
|
||||
private int mCreatedBy;
|
||||
private boolean mOnWifiOnly;
|
||||
|
@ -183,7 +184,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
Account account,
|
||||
OCFile file,
|
||||
OCUpload upload,
|
||||
boolean forceOverwrite,
|
||||
FileUploader.NameCollisionPolicy nameCollisionPolicy,
|
||||
int localBehaviour,
|
||||
Context context,
|
||||
boolean onWifiOnly,
|
||||
|
@ -218,7 +219,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
mOnWifiOnly = onWifiOnly;
|
||||
mWhileChargingOnly = whileChargingOnly;
|
||||
mRemotePath = upload.getRemotePath();
|
||||
mForceOverwrite = forceOverwrite;
|
||||
mNameCollisionPolicy = nameCollisionPolicy;
|
||||
mLocalBehaviour = localBehaviour;
|
||||
mOriginalStoragePath = mFile.getStoragePath();
|
||||
mContext = context;
|
||||
|
@ -504,7 +505,11 @@ public class UploadFileOperation extends SyncOperation {
|
|||
/**** E2E *****/
|
||||
|
||||
// check name collision
|
||||
checkNameCollision(client, metadata, parentFile.isEncrypted());
|
||||
RemoteOperationResult collisionResult = checkNameCollision(client, metadata, parentFile.isEncrypted());
|
||||
if (collisionResult != null) {
|
||||
result = collisionResult;
|
||||
return collisionResult;
|
||||
}
|
||||
|
||||
String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
|
||||
expectedFile = new File(expectedPath);
|
||||
|
@ -759,7 +764,11 @@ public class UploadFileOperation extends SyncOperation {
|
|||
}
|
||||
|
||||
// check name collision
|
||||
checkNameCollision(client, null, false);
|
||||
RemoteOperationResult collisionResult = checkNameCollision(client, null, false);
|
||||
if (collisionResult != null) {
|
||||
result = collisionResult;
|
||||
return collisionResult;
|
||||
}
|
||||
|
||||
String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
|
||||
expectedFile = new File(expectedPath);
|
||||
|
@ -922,24 +931,37 @@ public class UploadFileOperation extends SyncOperation {
|
|||
return new RemoteOperationResult(ResultCode.OK);
|
||||
}
|
||||
|
||||
private void checkNameCollision(OwnCloudClient client, DecryptedFolderMetadata metadata, boolean encrypted)
|
||||
throws OperationCancelledException {
|
||||
/// automatic rename of file to upload in case of name collision in server
|
||||
@CheckResult
|
||||
private RemoteOperationResult checkNameCollision(OwnCloudClient client, DecryptedFolderMetadata metadata, boolean encrypted)
|
||||
throws OperationCancelledException {
|
||||
Log_OC.d(TAG, "Checking name collision in server");
|
||||
if (!mForceOverwrite) {
|
||||
String remotePath = getAvailableRemotePath(client, mRemotePath, metadata, encrypted);
|
||||
mWasRenamed = !remotePath.equals(mRemotePath);
|
||||
if (mWasRenamed) {
|
||||
createNewOCFile(remotePath);
|
||||
Log_OC.d(TAG, "File renamed as " + remotePath);
|
||||
|
||||
if (existsFile(client, mRemotePath, metadata, encrypted)) {
|
||||
switch (mNameCollisionPolicy) {
|
||||
case CANCEL:
|
||||
Log_OC.d(TAG, "File exists; canceling");
|
||||
throw new OperationCancelledException();
|
||||
case RENAME:
|
||||
mRemotePath = getNewAvailableRemotePath(client, mRemotePath, metadata, encrypted);
|
||||
mWasRenamed = true;
|
||||
createNewOCFile(mRemotePath);
|
||||
Log_OC.d(TAG, "File renamed as " + mRemotePath);
|
||||
mRenameUploadListener.onRenameUpload();
|
||||
break;
|
||||
case OVERWRITE:
|
||||
Log_OC.d(TAG, "Overwriting file");
|
||||
break;
|
||||
case ASK_USER:
|
||||
Log_OC.d(TAG, "Name collision; asking the user what to do");
|
||||
return new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
|
||||
}
|
||||
mRemotePath = remotePath;
|
||||
mRenameUploadListener.onRenameUpload();
|
||||
}
|
||||
|
||||
if (mCancellationRequested.get()) {
|
||||
throw new OperationCancelledException();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void handleSuccessfulUpload(File temporalFile, File expectedFile, File originalFile,
|
||||
|
@ -1043,8 +1065,8 @@ public class UploadFileOperation extends SyncOperation {
|
|||
|
||||
|
||||
/**
|
||||
* Create a new OCFile mFile with new remote path. This is required if forceOverwrite==false.
|
||||
* New file is stored as mFile, original as mOldFile.
|
||||
* Create a new OCFile mFile with new remote path. This is required if nameCollisionPolicy==RENAME. New file is
|
||||
* stored as mFile, original as mOldFile.
|
||||
*
|
||||
* @param newRemotePath new remote path
|
||||
*/
|
||||
|
@ -1068,45 +1090,36 @@ public class UploadFileOperation extends SyncOperation {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if remotePath does not exist in the server and returns it, or adds
|
||||
* a suffix to it in order to avoid the server file is overwritten.
|
||||
* Returns a new and available (does not exists on the server) remotePath.
|
||||
* This adds an incremental suffix.
|
||||
*
|
||||
* @param client OwnCloud client
|
||||
* @param remotePath remote path of the file
|
||||
* @param metadata metadata of encrypted folder
|
||||
* @return new remote path
|
||||
*/
|
||||
private String getAvailableRemotePath(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata,
|
||||
boolean encrypted) {
|
||||
boolean check = existsFile(client, remotePath, metadata, encrypted);
|
||||
if (!check) {
|
||||
return remotePath;
|
||||
}
|
||||
|
||||
int pos = remotePath.lastIndexOf('.');
|
||||
private String getNewAvailableRemotePath(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata,
|
||||
boolean encrypted) {
|
||||
int extPos = remotePath.lastIndexOf('.');
|
||||
String suffix;
|
||||
String extension = "";
|
||||
String remotePathWithoutExtension = "";
|
||||
if (pos >= 0) {
|
||||
extension = remotePath.substring(pos + 1);
|
||||
remotePathWithoutExtension = remotePath.substring(0, pos);
|
||||
if (extPos >= 0) {
|
||||
extension = remotePath.substring(extPos + 1);
|
||||
remotePathWithoutExtension = remotePath.substring(0, extPos);
|
||||
}
|
||||
|
||||
int count = 2;
|
||||
boolean exists;
|
||||
String newPath;
|
||||
do {
|
||||
suffix = " (" + count + ")";
|
||||
if (pos >= 0) {
|
||||
check = existsFile(client, remotePathWithoutExtension + suffix + "." + extension, metadata, encrypted);
|
||||
} else {
|
||||
check = existsFile(client, remotePath + suffix, metadata, encrypted);
|
||||
}
|
||||
newPath = extPos >= 0 ? remotePathWithoutExtension + suffix + "." + extension : remotePath + suffix;
|
||||
exists = existsFile(client, newPath, metadata, encrypted);
|
||||
count++;
|
||||
} while (check);
|
||||
} while (exists);
|
||||
|
||||
if (pos >= 0) {
|
||||
return remotePathWithoutExtension + suffix + "." + extension;
|
||||
} else {
|
||||
return remotePath + suffix;
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
private boolean existsFile(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata,
|
||||
|
|
|
@ -804,7 +804,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
+ ProviderTableMeta.UPLOADS_STATUS + INTEGER // UploadStatus
|
||||
+ ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR + INTEGER // Upload LocalBehaviour
|
||||
+ ProviderTableMeta.UPLOADS_UPLOAD_TIME + INTEGER
|
||||
+ ProviderTableMeta.UPLOADS_FORCE_OVERWRITE + INTEGER // boolean
|
||||
+ ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY + INTEGER // boolean
|
||||
+ ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER + INTEGER // boolean
|
||||
+ ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP + INTEGER
|
||||
+ ProviderTableMeta.UPLOADS_LAST_RESULT + INTEGER // Upload LastResult
|
||||
|
@ -830,6 +830,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
+ ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH + " TEXT, " // remote path
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY + " INTEGER, " // wifi_only
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY + " INTEGER, " // charging only
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_EXISTING + " INTEGER, " // existing
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_ENABLED + " INTEGER, " // enabled
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS + " INTEGER, " // enable date
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date
|
||||
|
@ -2104,6 +2105,69 @@ public class FileContentProvider extends ContentProvider {
|
|||
if (!upgraded) {
|
||||
Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
|
||||
}
|
||||
|
||||
if (oldVersion < 54 && newVersion >= 54) {
|
||||
Log_OC.i(SQL, "Entering in the #54 add synced.existing," +
|
||||
" rename uploads.force_overwrite to uploads.name_collision_policy");
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// Add synced.existing
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME +
|
||||
ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_EXISTING + " INTEGER "); // boolean
|
||||
|
||||
|
||||
// Rename uploads.force_overwrite to uploads.name_collision_policy
|
||||
String tmpTableName = ProviderTableMeta.UPLOADS_TABLE_NAME + "_old";
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.UPLOADS_TABLE_NAME + " RENAME TO " + tmpTableName);
|
||||
createUploadsTable(db);
|
||||
db.execSQL("INSERT INTO " + ProviderTableMeta.UPLOADS_TABLE_NAME + " (" +
|
||||
ProviderTableMeta._ID + ", " +
|
||||
ProviderTableMeta.UPLOADS_LOCAL_PATH + ", " +
|
||||
ProviderTableMeta.UPLOADS_REMOTE_PATH + ", " +
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + ", " +
|
||||
ProviderTableMeta.UPLOADS_FILE_SIZE + ", " +
|
||||
ProviderTableMeta.UPLOADS_STATUS + ", " +
|
||||
ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR + ", " +
|
||||
ProviderTableMeta.UPLOADS_UPLOAD_TIME + ", " +
|
||||
ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY + ", " +
|
||||
ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER + ", " +
|
||||
ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP + ", " +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + ", " +
|
||||
ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY + ", " +
|
||||
ProviderTableMeta.UPLOADS_IS_WIFI_ONLY + ", " +
|
||||
ProviderTableMeta.UPLOADS_CREATED_BY + ", " +
|
||||
ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN +
|
||||
") " +
|
||||
" SELECT " +
|
||||
ProviderTableMeta._ID + ", " +
|
||||
ProviderTableMeta.UPLOADS_LOCAL_PATH + ", " +
|
||||
ProviderTableMeta.UPLOADS_REMOTE_PATH + ", " +
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + ", " +
|
||||
ProviderTableMeta.UPLOADS_FILE_SIZE + ", " +
|
||||
ProviderTableMeta.UPLOADS_STATUS + ", " +
|
||||
ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR + ", " +
|
||||
ProviderTableMeta.UPLOADS_UPLOAD_TIME + ", " +
|
||||
"force_overwrite" + ", " + // See FileUploader.NameCollisionPolicy
|
||||
ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER + ", " +
|
||||
ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP + ", " +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + ", " +
|
||||
ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY + ", " +
|
||||
ProviderTableMeta.UPLOADS_IS_WIFI_ONLY + ", " +
|
||||
ProviderTableMeta.UPLOADS_CREATED_BY + ", " +
|
||||
ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN +
|
||||
" FROM " + tmpTableName);
|
||||
db.execSQL("DROP TABLE " + tmpTableName);
|
||||
|
||||
upgraded = true;
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
if (!upgraded) {
|
||||
Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import android.content.Intent;
|
|||
import android.os.Bundle;
|
||||
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.files.services.FileDownloader;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
|
@ -32,52 +34,96 @@ import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
|
|||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
|
||||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper activity which will be launched if keep-in-sync file will be modified by external
|
||||
* application.
|
||||
*/
|
||||
public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
|
||||
/**
|
||||
* A nullable upload entry that must be removed when and if the conflict is resolved.
|
||||
*/
|
||||
public static final String EXTRA_CONFLICT_UPLOAD = "CONFLICT_UPLOAD";
|
||||
/**
|
||||
* Specify the upload local behaviour when there is no CONFLICT_UPLOAD.
|
||||
*/
|
||||
public static final String EXTRA_LOCAL_BEHAVIOUR = "LOCAL_BEHAVIOUR";
|
||||
|
||||
private static final String TAG = ConflictsResolveActivity.class.getSimpleName();
|
||||
|
||||
@Inject UploadsStorageManager uploadsStorageManager;
|
||||
|
||||
private OCUpload conflictUpload;
|
||||
private int localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
conflictUpload = savedInstanceState.getParcelable(EXTRA_CONFLICT_UPLOAD);
|
||||
localBehaviour = savedInstanceState.getInt(EXTRA_LOCAL_BEHAVIOUR);
|
||||
} else {
|
||||
conflictUpload = getIntent().getParcelableExtra(EXTRA_CONFLICT_UPLOAD);
|
||||
localBehaviour = getIntent().getIntExtra(EXTRA_LOCAL_BEHAVIOUR, localBehaviour);
|
||||
}
|
||||
|
||||
if (conflictUpload != null) {
|
||||
localBehaviour = conflictUpload.getLocalAction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void conflictDecisionMade(Decision decision) {
|
||||
|
||||
Integer behaviour = null;
|
||||
Boolean forceOverwrite = null;
|
||||
|
||||
switch (decision) {
|
||||
case CANCEL:
|
||||
finish();
|
||||
return;
|
||||
case OVERWRITE:
|
||||
// use local version -> overwrite on server
|
||||
forceOverwrite = true;
|
||||
break;
|
||||
case KEEP_BOTH:
|
||||
behaviour = FileUploader.LOCAL_BEHAVIOUR_MOVE;
|
||||
break;
|
||||
case SERVER:
|
||||
// use server version -> delete local, request download
|
||||
Intent intent = new Intent(this, FileDownloader.class);
|
||||
intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
|
||||
intent.putExtra(FileDownloader.EXTRA_FILE, getFile());
|
||||
startService(intent);
|
||||
finish();
|
||||
return;
|
||||
default:
|
||||
Log_OC.e(TAG, "Unhandled conflict decision " + decision);
|
||||
return;
|
||||
if (decision == Decision.CANCEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
OCFile file = getFile();
|
||||
|
||||
switch (decision) {
|
||||
case KEEP_LOCAL: // Upload
|
||||
FileUploader.uploadUpdateFile(
|
||||
this,
|
||||
getAccount(),
|
||||
file,
|
||||
localBehaviour,
|
||||
FileUploader.NameCollisionPolicy.OVERWRITE
|
||||
);
|
||||
|
||||
if (conflictUpload != null) {
|
||||
uploadsStorageManager.removeUpload(conflictUpload);
|
||||
}
|
||||
break;
|
||||
case KEEP_BOTH: // Upload
|
||||
FileUploader.uploadUpdateFile(
|
||||
this,
|
||||
getAccount(),
|
||||
file,
|
||||
localBehaviour,
|
||||
FileUploader.NameCollisionPolicy.RENAME
|
||||
);
|
||||
|
||||
if (conflictUpload != null) {
|
||||
uploadsStorageManager.removeUpload(conflictUpload);
|
||||
}
|
||||
break;
|
||||
case KEEP_SERVER: // Download
|
||||
if (!this.shouldDeleteLocal()) {
|
||||
// Overwrite local file
|
||||
Intent intent = new Intent(this, FileDownloader.class);
|
||||
intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
|
||||
intent.putExtra(FileDownloader.EXTRA_FILE, file);
|
||||
if (conflictUpload != null) {
|
||||
intent.putExtra(FileDownloader.EXTRA_CONFLICT_UPLOAD, conflictUpload);
|
||||
}
|
||||
startService(intent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadUpdate(this, getAccount(), getFile(), behaviour, forceOverwrite);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
@ -87,26 +133,27 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
|
|||
if (getAccount() != null) {
|
||||
OCFile file = getFile();
|
||||
if (getFile() == null) {
|
||||
Log_OC.e(TAG, "No conflictive file received");
|
||||
Log_OC.e(TAG, "No file received");
|
||||
finish();
|
||||
} else {
|
||||
/// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
|
||||
file = getStorageManager().getFileByPath(file.getRemotePath()); // file = null if not in the
|
||||
// current Account
|
||||
if (file != null) {
|
||||
setFile(file);
|
||||
ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(this);
|
||||
d.showDialog(this);
|
||||
|
||||
// Check whether the file is contained in the current Account
|
||||
if (getStorageManager().fileExists(file.getRemotePath())) {
|
||||
ConflictsResolveDialog dialog = new ConflictsResolveDialog(this, !this.shouldDeleteLocal());
|
||||
dialog.showDialog(this);
|
||||
} else {
|
||||
// account was changed to a different one - just finish
|
||||
// Account was changed to a different one - just finish
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the local version of the files is to be deleted.
|
||||
*/
|
||||
private boolean shouldDeleteLocal() {
|
||||
return localBehaviour == FileUploader.LOCAL_BEHAVIOUR_DELETE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1043,8 +1043,7 @@ public class FileDisplayActivity extends FileActivity
|
|||
break;
|
||||
}
|
||||
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadNewFile(
|
||||
FileUploader.uploadNewFile(
|
||||
this,
|
||||
getAccount(),
|
||||
filePaths,
|
||||
|
@ -1054,7 +1053,8 @@ public class FileDisplayActivity extends FileActivity
|
|||
false, // do not create parent folder if not existent
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
|
||||
} else {
|
||||
|
|
|
@ -888,19 +888,19 @@ public class ReceiveExternalFilesActivity extends FileActivity
|
|||
}
|
||||
|
||||
public void uploadFile(String tmpName, String filename) {
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadNewFile(
|
||||
FileUploader.uploadNewFile(
|
||||
getBaseContext(),
|
||||
getAccount(),
|
||||
tmpName,
|
||||
tmpName,
|
||||
mFile.getRemotePath() + filename,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
null,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
);
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -416,6 +416,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
syncedFolder.getRemotePath(),
|
||||
syncedFolder.isWifiOnly(),
|
||||
syncedFolder.isChargingOnly(),
|
||||
syncedFolder.isExisting(),
|
||||
syncedFolder.isSubfolderByDate(),
|
||||
syncedFolder.getAccount(),
|
||||
syncedFolder.getUploadAction(),
|
||||
|
@ -443,6 +444,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
syncedFolder.getRemotePath(),
|
||||
syncedFolder.isWifiOnly(),
|
||||
syncedFolder.isChargingOnly(),
|
||||
syncedFolder.isExisting(),
|
||||
syncedFolder.isSubfolderByDate(),
|
||||
syncedFolder.getAccount(),
|
||||
syncedFolder.getUploadAction(),
|
||||
|
@ -469,6 +471,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
getString(R.string.instant_upload_path) + "/" + mediaFolder.folderName,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
getAccount().name,
|
||||
FileUploader.LOCAL_BEHAVIOUR_FORGET,
|
||||
|
@ -577,7 +580,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
case R.id.action_create_custom_folder: {
|
||||
Log.d(TAG, "Show custom folder dialog");
|
||||
SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem(
|
||||
SyncedFolder.UNPERSISTED_ID, null, null, true, false,
|
||||
SyncedFolder.UNPERSISTED_ID, null, null, true, false, true,
|
||||
false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false,
|
||||
clock.getCurrentTime(), null, MediaFolderType.CUSTOM, false);
|
||||
onSyncFolderSettingsClick(0, emptyCustomFolder);
|
||||
|
@ -619,7 +622,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
}
|
||||
|
||||
if (syncedFolderDisplayItem.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolderDisplayItem);
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolderDisplayItem, true);
|
||||
|
||||
showBatteryOptimizationInfo();
|
||||
}
|
||||
|
@ -709,18 +712,20 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
if (MediaFolderType.CUSTOM == syncedFolder.getType() && syncedFolder.getId() == UNPERSISTED_ID) {
|
||||
SyncedFolderDisplayItem newCustomFolder = new SyncedFolderDisplayItem(
|
||||
SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(),
|
||||
syncedFolder.isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(),
|
||||
syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(),
|
||||
clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.isHidden());
|
||||
syncedFolder.isWifiOnly(), syncedFolder.isChargingOnly(),
|
||||
syncedFolder.isExisting(), syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(),
|
||||
syncedFolder.getUploadAction(), syncedFolder.isEnabled(), clock.getCurrentTime(),
|
||||
new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.isHidden());
|
||||
|
||||
saveOrUpdateSyncedFolder(newCustomFolder);
|
||||
adapter.addSyncFolderItem(newCustomFolder);
|
||||
} else {
|
||||
SyncedFolderDisplayItem item = adapter.get(syncedFolder.getSection());
|
||||
updateSyncedFolderItem(item, syncedFolder.getId(), syncedFolder.getLocalPath(),
|
||||
syncedFolder.getRemotePath(), syncedFolder
|
||||
.isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(), syncedFolder
|
||||
.getUploadAction(), syncedFolder.isEnabled());
|
||||
syncedFolder.getRemotePath(), syncedFolder.isWifiOnly(),
|
||||
syncedFolder.isChargingOnly(), syncedFolder.isExisting(),
|
||||
syncedFolder.isSubfolderByDate(), syncedFolder.getUploadAction(),
|
||||
syncedFolder.isEnabled());
|
||||
|
||||
saveOrUpdateSyncedFolder(item);
|
||||
|
||||
|
@ -743,7 +748,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
// existing synced folder setup to be updated
|
||||
syncedFolderProvider.updateSyncFolder(item);
|
||||
if (item.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item);
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item, true);
|
||||
} else {
|
||||
String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId();
|
||||
|
||||
|
@ -761,7 +766,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
if (storedId != -1) {
|
||||
item.setId(storedId);
|
||||
if (item.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item);
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item, true);
|
||||
} else {
|
||||
String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId();
|
||||
arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey);
|
||||
|
@ -788,6 +793,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
* @param remotePath the remote path
|
||||
* @param wifiOnly upload on wifi only
|
||||
* @param chargingOnly upload on charging only
|
||||
* @param existing also upload existing
|
||||
* @param subfolderByDate created sub folders
|
||||
* @param uploadAction upload action
|
||||
* @param enabled is sync enabled
|
||||
|
@ -798,6 +804,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
String remotePath,
|
||||
boolean wifiOnly,
|
||||
boolean chargingOnly,
|
||||
boolean existing,
|
||||
boolean subfolderByDate,
|
||||
Integer uploadAction,
|
||||
boolean enabled) {
|
||||
|
@ -806,6 +813,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
|
|||
item.setRemotePath(remotePath);
|
||||
item.setWifiOnly(wifiOnly);
|
||||
item.setChargingOnly(chargingOnly);
|
||||
item.setExisting(existing);
|
||||
item.setSubfolderByDate(subfolderByDate);
|
||||
item.setUploadAction(uploadAction);
|
||||
item.setEnabled(enabled, clock.getCurrentTime());
|
||||
|
|
|
@ -45,6 +45,7 @@ import com.evernote.android.job.JobRequest;
|
|||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.core.Clock;
|
||||
import com.nextcloud.client.device.PowerManagementService;
|
||||
import com.nextcloud.client.network.ConnectivityService;
|
||||
import com.nextcloud.java.util.Optional;
|
||||
|
@ -121,6 +122,9 @@ public class UploadListActivity extends FileActivity {
|
|||
@Inject
|
||||
PowerManagementService powerManagementService;
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
|
||||
@Override
|
||||
public void showFiles(boolean onDeviceOnly) {
|
||||
super.showFiles(onDeviceOnly);
|
||||
|
@ -169,9 +173,11 @@ public class UploadListActivity extends FileActivity {
|
|||
|
||||
uploadListAdapter = new UploadListAdapter(this,
|
||||
uploadsStorageManager,
|
||||
getStorageManager(),
|
||||
userAccountManager,
|
||||
connectivityService,
|
||||
powerManagementService);
|
||||
powerManagementService,
|
||||
clock);
|
||||
|
||||
final GridLayoutManager lm = new GridLayoutManager(this, 1);
|
||||
uploadListAdapter.setLayoutManager(lm);
|
||||
|
@ -214,14 +220,15 @@ public class UploadListActivity extends FileActivity {
|
|||
}
|
||||
|
||||
// retry failed uploads
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
new Thread(() -> requester.retryFailedUploads(this,
|
||||
null,
|
||||
uploadsStorageManager,
|
||||
connectivityService,
|
||||
userAccountManager,
|
||||
powerManagementService,
|
||||
null)).start();
|
||||
new Thread(() -> FileUploader.retryFailedUploads(
|
||||
this,
|
||||
null,
|
||||
uploadsStorageManager,
|
||||
connectivityService,
|
||||
userAccountManager,
|
||||
powerManagementService,
|
||||
null
|
||||
)).start();
|
||||
|
||||
// update UI
|
||||
uploadListAdapter.loadUploadItemsFromDb();
|
||||
|
|
|
@ -26,6 +26,7 @@ package com.owncloud.android.ui.adapter;
|
|||
|
||||
import android.accounts.Account;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
@ -37,6 +38,7 @@ import android.view.ViewGroup;
|
|||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
@ -44,10 +46,13 @@ import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
|
|||
import com.afollestad.sectionedrecyclerview.SectionedViewHolder;
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.core.Clock;
|
||||
import com.nextcloud.client.device.PowerManagementService;
|
||||
import com.nextcloud.client.network.ConnectivityService;
|
||||
import com.nextcloud.java.util.Optional;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
|
@ -56,7 +61,10 @@ import com.owncloud.android.db.OCUpload;
|
|||
import com.owncloud.android.db.OCUploadComparator;
|
||||
import com.owncloud.android.db.UploadResult;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.operations.RefreshFolderOperation;
|
||||
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
|
||||
import com.owncloud.android.ui.activity.FileActivity;
|
||||
import com.owncloud.android.utils.DisplayUtils;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
|
@ -78,9 +86,11 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
|
|||
private ProgressListener progressListener;
|
||||
private FileActivity parentActivity;
|
||||
private UploadsStorageManager uploadsStorageManager;
|
||||
private FileDataStorageManager storageManager;
|
||||
private ConnectivityService connectivityService;
|
||||
private PowerManagementService powerManagementService;
|
||||
private UserAccountManager accountManager;
|
||||
private Clock clock;
|
||||
private UploadGroup[] uploadGroups;
|
||||
private boolean showUser;
|
||||
|
||||
|
@ -133,16 +143,15 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
|
|||
uploadsStorageManager.clearSuccessfulUploads();
|
||||
break;
|
||||
case FAILED:
|
||||
new Thread(() -> new FileUploader.UploadRequester()
|
||||
.retryFailedUploads(
|
||||
parentActivity,
|
||||
null,
|
||||
uploadsStorageManager,
|
||||
connectivityService,
|
||||
accountManager,
|
||||
powerManagementService,
|
||||
null))
|
||||
.start();
|
||||
new Thread(() -> FileUploader.retryFailedUploads(
|
||||
parentActivity,
|
||||
null,
|
||||
uploadsStorageManager,
|
||||
connectivityService,
|
||||
accountManager,
|
||||
powerManagementService,
|
||||
null
|
||||
)).start();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -161,15 +170,19 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
|
|||
|
||||
public UploadListAdapter(final FileActivity fileActivity,
|
||||
final UploadsStorageManager uploadsStorageManager,
|
||||
final FileDataStorageManager storageManager,
|
||||
final UserAccountManager accountManager,
|
||||
final ConnectivityService connectivityService,
|
||||
final PowerManagementService powerManagementService) {
|
||||
final PowerManagementService powerManagementService,
|
||||
final Clock clock) {
|
||||
Log_OC.d(TAG, "UploadListAdapter");
|
||||
this.parentActivity = fileActivity;
|
||||
this.uploadsStorageManager = uploadsStorageManager;
|
||||
this.storageManager = storageManager;
|
||||
this.accountManager = accountManager;
|
||||
this.connectivityService = connectivityService;
|
||||
this.powerManagementService = powerManagementService;
|
||||
this.clock = clock;
|
||||
uploadGroups = new UploadGroup[3];
|
||||
|
||||
shouldShowHeadersForEmptySections(false);
|
||||
|
@ -323,14 +336,22 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
|
|||
});
|
||||
|
||||
} else if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
|
||||
// Delete
|
||||
itemViewHolder.button.setImageResource(R.drawable.ic_action_delete_grey);
|
||||
if (item.getLastResult() == UploadResult.SYNC_CONFLICT) {
|
||||
itemViewHolder.button.setImageResource(R.drawable.ic_dots_vertical);
|
||||
itemViewHolder.button.setOnClickListener(view -> {
|
||||
if (optionalUser.isPresent()) {
|
||||
Account account = optionalUser.get().toPlatformAccount();
|
||||
showItemConflictPopup(
|
||||
itemViewHolder, item, account, status, view
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Delete
|
||||
itemViewHolder.button.setImageResource(R.drawable.ic_action_delete_grey);
|
||||
itemViewHolder.button.setOnClickListener(v -> removeUpload(item));
|
||||
}
|
||||
itemViewHolder.button.setVisibility(View.VISIBLE);
|
||||
itemViewHolder.button.setOnClickListener(v -> {
|
||||
uploadsStorageManager.removeUpload(item);
|
||||
loadUploadItemsFromDb();
|
||||
});
|
||||
|
||||
} else { // UploadStatus.UPLOAD_SUCCESS
|
||||
itemViewHolder.button.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
@ -339,29 +360,32 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
|
|||
|
||||
// click on item
|
||||
if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
|
||||
if (UploadResult.CREDENTIAL_ERROR == item.getLastResult()) {
|
||||
itemViewHolder.itemLayout.setOnClickListener(v ->
|
||||
parentActivity.getFileOperationsHelper().checkCurrentCredentials(
|
||||
item.getAccount(accountManager)));
|
||||
} else {
|
||||
// not a credentials error
|
||||
itemViewHolder.itemLayout.setOnClickListener(v -> {
|
||||
File file = new File(item.getLocalPath());
|
||||
if (file.exists()) {
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.retry(parentActivity, accountManager, item);
|
||||
loadUploadItemsFromDb();
|
||||
} else {
|
||||
DisplayUtils.showSnackMessage(
|
||||
v.getRootView().findViewById(android.R.id.content),
|
||||
R.string.local_file_not_found_message
|
||||
);
|
||||
final UploadResult uploadResult = item.getLastResult();
|
||||
itemViewHolder.itemLayout.setOnClickListener(v -> {
|
||||
if (uploadResult == UploadResult.CREDENTIAL_ERROR) {
|
||||
parentActivity.getFileOperationsHelper().checkCurrentCredentials(item.getAccount(accountManager));
|
||||
return;
|
||||
} else if (uploadResult == UploadResult.SYNC_CONFLICT && optionalUser.isPresent()) {
|
||||
Account account = optionalUser.get().toPlatformAccount();
|
||||
if (checkAndOpenConflictResolutionDialog(itemViewHolder, item, account, status)) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// not a credentials error
|
||||
File file = new File(item.getLocalPath());
|
||||
if (file.exists()) {
|
||||
FileUploader.retryUpload(parentActivity, item.getAccount(accountManager), item);
|
||||
loadUploadItemsFromDb();
|
||||
} else {
|
||||
DisplayUtils.showSnackMessage(
|
||||
v.getRootView().findViewById(android.R.id.content),
|
||||
R.string.local_file_not_found_message
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
itemViewHolder.itemLayout.setOnClickListener(v ->
|
||||
onUploadItemClick(item));
|
||||
itemViewHolder.itemLayout.setOnClickListener(v -> onUploadItemClick(item));
|
||||
}
|
||||
|
||||
// Set icon or thumbnail
|
||||
|
@ -466,6 +490,90 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
|
|||
}
|
||||
}
|
||||
|
||||
private boolean checkAndOpenConflictResolutionDialog(ItemViewHolder itemViewHolder, OCUpload item, Account account, String status) {
|
||||
String remotePath = item.getRemotePath();
|
||||
OCFile ocFile = storageManager.getFileByPath(remotePath);
|
||||
|
||||
if (ocFile == null) { // Remote file doesn't exist, try to refresh folder
|
||||
OCFile folder = storageManager.getFileByPath(new File(remotePath).getParent() + "/");
|
||||
if (folder != null && folder.isFolder()) {
|
||||
this.refreshFolder(itemViewHolder, account, folder, (caller, result) -> {
|
||||
itemViewHolder.status.setText(status);
|
||||
if (result.isSuccess()) {
|
||||
OCFile file = storageManager.getFileByPath(remotePath);
|
||||
if (file != null) {
|
||||
this.openConflictActivity(file, item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Destination folder doesn't exist anymore
|
||||
}
|
||||
|
||||
if (ocFile != null) {
|
||||
this.openConflictActivity(ocFile, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remote file doesn't exist anymore = there is no more conflict
|
||||
return false;
|
||||
}
|
||||
|
||||
private void showItemConflictPopup(ItemViewHolder itemViewHolder, OCUpload item, Account account, String status, View view) {
|
||||
PopupMenu popup = new PopupMenu(MainApp.getAppContext(), view);
|
||||
popup.inflate(R.menu.upload_list_item_file_conflict);
|
||||
popup.setOnMenuItemClickListener(i -> {
|
||||
switch (i.getItemId()) {
|
||||
case R.id.action_upload_list_resolve_conflict:
|
||||
checkAndOpenConflictResolutionDialog(itemViewHolder, item, account, status);
|
||||
break;
|
||||
case R.id.action_upload_list_delete:
|
||||
default:
|
||||
removeUpload(item);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
popup.show();
|
||||
}
|
||||
|
||||
private void removeUpload(OCUpload item) {
|
||||
uploadsStorageManager.removeUpload(item);
|
||||
loadUploadItemsFromDb();
|
||||
}
|
||||
|
||||
private void refreshFolder(ItemViewHolder view, Account account, OCFile folder, OnRemoteOperationListener listener) {
|
||||
view.itemLayout.setClickable(false);
|
||||
view.status.setText(R.string.uploads_view_upload_status_fetching_server_version);
|
||||
Context context = MainApp.getAppContext();
|
||||
new RefreshFolderOperation(folder,
|
||||
clock.getCurrentTime(),
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
storageManager,
|
||||
account,
|
||||
context)
|
||||
.execute(account, context, (caller, result) -> {
|
||||
view.itemLayout.setClickable(true);
|
||||
listener.onRemoteOperationFinish(caller, result);
|
||||
}, parentActivity.getHandler());
|
||||
}
|
||||
|
||||
private void openConflictActivity(OCFile file, OCUpload upload) {
|
||||
file.setStoragePath(upload.getLocalPath());
|
||||
|
||||
Context context = MainApp.getAppContext();
|
||||
Intent i = new Intent(context, ConflictsResolveActivity.class);
|
||||
i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
|
||||
i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, upload.getAccount(accountManager));
|
||||
i.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD, upload);
|
||||
context.startActivity(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status text to show to the user according to the status and last result of the
|
||||
* the given upload.
|
||||
|
|
|
@ -219,8 +219,7 @@ public class CopyAndUploadContentUrisTask extends AsyncTask<Object, Void, Result
|
|||
}
|
||||
|
||||
private void requestUpload(Account account, String localPath, String remotePath, int behaviour, String mimeType) {
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadNewFile(
|
||||
FileUploader.uploadNewFile(
|
||||
mAppContext,
|
||||
account,
|
||||
localPath,
|
||||
|
@ -230,7 +229,8 @@ public class CopyAndUploadContentUrisTask extends AsyncTask<Object, Void, Result
|
|||
false, // do not create parent folder if not existent
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,44 +43,48 @@ public class ConflictsResolveDialog extends DialogFragment {
|
|||
public enum Decision {
|
||||
CANCEL,
|
||||
KEEP_BOTH,
|
||||
OVERWRITE,
|
||||
SERVER
|
||||
KEEP_LOCAL,
|
||||
KEEP_SERVER,
|
||||
}
|
||||
|
||||
OnConflictDecisionMadeListener mListener;
|
||||
private final OnConflictDecisionMadeListener listener;
|
||||
private final boolean canKeepServer;
|
||||
|
||||
public static ConflictsResolveDialog newInstance(OnConflictDecisionMadeListener listener) {
|
||||
ConflictsResolveDialog f = new ConflictsResolveDialog();
|
||||
f.mListener = listener;
|
||||
return f;
|
||||
public ConflictsResolveDialog(OnConflictDecisionMadeListener listener, boolean canKeepServer) {
|
||||
this.listener = listener;
|
||||
this.canKeepServer = canKeepServer;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return new AlertDialog.Builder(requireActivity(), R.style.Theme_ownCloud_Dialog)
|
||||
.setIcon(R.drawable.ic_warning)
|
||||
.setTitle(R.string.conflict_title)
|
||||
.setMessage(getString(R.string.conflict_message))
|
||||
.setPositiveButton(R.string.conflict_use_local_version,
|
||||
(dialog, which) -> {
|
||||
if (mListener != null) {
|
||||
mListener.conflictDecisionMade(Decision.OVERWRITE);
|
||||
}
|
||||
})
|
||||
.setNeutralButton(R.string.conflict_keep_both,
|
||||
(dialog, which) -> {
|
||||
if (mListener != null) {
|
||||
mListener.conflictDecisionMade(Decision.KEEP_BOTH);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.conflict_use_server_version,
|
||||
(dialog, which) -> {
|
||||
if (mListener != null) {
|
||||
mListener.conflictDecisionMade(Decision.SERVER);
|
||||
}
|
||||
})
|
||||
.create();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity(), R.style.Theme_ownCloud_Dialog)
|
||||
.setIcon(R.drawable.ic_warning)
|
||||
.setTitle(R.string.conflict_title)
|
||||
.setMessage(getString(R.string.conflict_message))
|
||||
.setPositiveButton(R.string.conflict_use_local_version,
|
||||
(dialog, which) -> {
|
||||
if (listener != null) {
|
||||
listener.conflictDecisionMade(Decision.KEEP_LOCAL);
|
||||
}
|
||||
})
|
||||
.setNeutralButton(R.string.conflict_keep_both,
|
||||
(dialog, which) -> {
|
||||
if (listener != null) {
|
||||
listener.conflictDecisionMade(Decision.KEEP_BOTH);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.canKeepServer) {
|
||||
builder.setNegativeButton(R.string.conflict_use_server_version,
|
||||
(dialog, which) -> {
|
||||
if (listener != null) {
|
||||
listener.conflictDecisionMade(Decision.KEEP_SERVER);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
public void showDialog(AppCompatActivity activity) {
|
||||
|
@ -96,8 +100,8 @@ public class ConflictsResolveDialog extends DialogFragment {
|
|||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
if (mListener != null) {
|
||||
mListener.conflictDecisionMade(Decision.CANCEL);
|
||||
if (listener != null) {
|
||||
listener.conflictDecisionMade(Decision.CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
private SwitchCompat mEnabledSwitch;
|
||||
private AppCompatCheckBox mUploadOnWifiCheckbox;
|
||||
private AppCompatCheckBox mUploadOnChargingCheckbox;
|
||||
private AppCompatCheckBox mUploadExistingCheckbox;
|
||||
private AppCompatCheckBox mUploadUseSubfoldersCheckbox;
|
||||
private TextView mUploadBehaviorSummary;
|
||||
private TextView mLocalFolderPath;
|
||||
|
@ -189,6 +190,9 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
ThemeUtils.tintCheckbox(mUploadOnChargingCheckbox, accentColor);
|
||||
}
|
||||
|
||||
mUploadExistingCheckbox = view.findViewById(R.id.setting_instant_upload_existing_checkbox);
|
||||
ThemeUtils.tintCheckbox(mUploadExistingCheckbox, accentColor);
|
||||
|
||||
mUploadUseSubfoldersCheckbox = view.findViewById(
|
||||
R.id.setting_instant_upload_path_use_subfolders_checkbox);
|
||||
ThemeUtils.tintCheckbox(mUploadUseSubfoldersCheckbox, accentColor);
|
||||
|
@ -227,6 +231,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
mUploadOnChargingCheckbox.setChecked(mSyncedFolder.isChargingOnly());
|
||||
}
|
||||
mUploadExistingCheckbox.setChecked(mSyncedFolder.isExisting());
|
||||
mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.isSubfolderByDate());
|
||||
|
||||
mUploadBehaviorSummary.setText(mUploadBehaviorItemStrings[mSyncedFolder.getUploadActionInteger()]);
|
||||
|
@ -318,6 +323,9 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
view.findViewById(R.id.setting_instant_upload_on_charging_container).setAlpha(alpha);
|
||||
}
|
||||
|
||||
view.findViewById(R.id.setting_instant_upload_existing_container).setEnabled(enable);
|
||||
view.findViewById(R.id.setting_instant_upload_existing_container).setAlpha(alpha);
|
||||
|
||||
view.findViewById(R.id.setting_instant_upload_path_use_subfolders_container).setEnabled(enable);
|
||||
view.findViewById(R.id.setting_instant_upload_path_use_subfolders_container).setAlpha(alpha);
|
||||
|
||||
|
@ -361,6 +369,15 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
});
|
||||
}
|
||||
|
||||
view.findViewById(R.id.setting_instant_upload_existing_container).setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mSyncedFolder.setExisting(!mSyncedFolder.isExisting());
|
||||
mUploadExistingCheckbox.toggle();
|
||||
}
|
||||
});
|
||||
|
||||
view.findViewById(R.id.setting_instant_upload_path_use_subfolders_container).setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
|
|
|
@ -41,6 +41,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
@Getter @Setter private String remotePath;
|
||||
@Getter @Setter private boolean wifiOnly = false;
|
||||
@Getter @Setter private boolean chargingOnly = false;
|
||||
@Getter @Setter private boolean existing = true;
|
||||
@Getter @Setter private boolean enabled = false;
|
||||
@Getter @Setter private boolean subfolderByDate = false;
|
||||
@Getter private Integer uploadAction;
|
||||
|
@ -57,6 +58,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
remotePath = syncedFolderDisplayItem.getRemotePath();
|
||||
wifiOnly = syncedFolderDisplayItem.isWifiOnly();
|
||||
chargingOnly = syncedFolderDisplayItem.isChargingOnly();
|
||||
existing = syncedFolderDisplayItem.isExisting();
|
||||
enabled = syncedFolderDisplayItem.isEnabled();
|
||||
subfolderByDate = syncedFolderDisplayItem.isSubfolderByDate();
|
||||
type = syncedFolderDisplayItem.getType();
|
||||
|
@ -73,6 +75,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
remotePath = read.readString();
|
||||
wifiOnly = read.readInt()!= 0;
|
||||
chargingOnly = read.readInt() != 0;
|
||||
existing = read.readInt() != 0;
|
||||
enabled = read.readInt() != 0;
|
||||
subfolderByDate = read.readInt() != 0;
|
||||
type = MediaFolderType.getById(read.readInt());
|
||||
|
@ -90,6 +93,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
dest.writeString(remotePath);
|
||||
dest.writeInt(wifiOnly ? 1 : 0);
|
||||
dest.writeInt(chargingOnly ? 1 : 0);
|
||||
dest.writeInt(existing ? 1 : 0);
|
||||
dest.writeInt(enabled ? 1 : 0);
|
||||
dest.writeInt(subfolderByDate ? 1 : 0);
|
||||
dest.writeInt(type.getId());
|
||||
|
|
|
@ -158,18 +158,18 @@ public class UriUploader {
|
|||
* @param remotePath Absolute path in the current OC account to set to the uploaded file.
|
||||
*/
|
||||
private void requestUpload(String localPath, String remotePath) {
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadNewFile(
|
||||
mActivity,
|
||||
mAccount,
|
||||
localPath,
|
||||
remotePath,
|
||||
mBehaviour,
|
||||
null, // MIME type will be detected from file name
|
||||
false, // do not create parent folder if not existent
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
FileUploader.uploadNewFile(
|
||||
mActivity,
|
||||
mAccount,
|
||||
localPath,
|
||||
remotePath,
|
||||
mBehaviour,
|
||||
null, // MIME type will be detected from file name
|
||||
false, // do not create parent folder if not existent
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,14 +76,14 @@ public final class ErrorMessageAdapter {
|
|||
RemoteOperation operation,
|
||||
Resources res
|
||||
) {
|
||||
String message = getSpecificMessageForResultAndOperation(result, operation, res);
|
||||
String message = getMessageForResultAndOperation(result, operation, res);
|
||||
|
||||
if (TextUtils.isEmpty(message)) {
|
||||
message = getCommonMessageForResult(result, res);
|
||||
message = getMessageForResult(result, res);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(message)) {
|
||||
message = getGenericErrorMessageForOperation(operation, res);
|
||||
message = getMessageForOperation(operation, res);
|
||||
}
|
||||
|
||||
if (message == null) {
|
||||
|
@ -109,7 +109,7 @@ public final class ErrorMessageAdapter {
|
|||
* specific message for both.
|
||||
*/
|
||||
@Nullable
|
||||
private static String getSpecificMessageForResultAndOperation(
|
||||
private static String getMessageForResultAndOperation(
|
||||
RemoteOperationResult result,
|
||||
RemoteOperation operation,
|
||||
Resources res
|
||||
|
@ -360,6 +360,9 @@ public final class ErrorMessageAdapter {
|
|||
} else if (result.getCode() == ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER) {
|
||||
return res.getString(R.string.filename_forbidden_charaters_from_server);
|
||||
|
||||
} else if(result.getCode() == ResultCode.SYNC_CONFLICT) {
|
||||
return String.format(res.getString(R.string.uploader_upload_failed_sync_conflict_error_content),
|
||||
operation.getFileName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,8 +379,7 @@ public final class ErrorMessageAdapter {
|
|||
* @return User message corresponding to 'result'.
|
||||
*/
|
||||
@Nullable
|
||||
private static String getCommonMessageForResult(RemoteOperationResult result, Resources res) {
|
||||
|
||||
private static String getMessageForResult(RemoteOperationResult result, Resources res) {
|
||||
String message = null;
|
||||
|
||||
if (!result.isSuccess()) {
|
||||
|
@ -452,7 +454,7 @@ public final class ErrorMessageAdapter {
|
|||
* @return User message corresponding to a generic error of 'operation'.
|
||||
*/
|
||||
@Nullable
|
||||
private static String getGenericErrorMessageForOperation(RemoteOperation operation, Resources res) {
|
||||
private static String getMessageForOperation(RemoteOperation operation, Resources res) {
|
||||
String message = null;
|
||||
|
||||
if (operation instanceof UploadFileOperation) {
|
||||
|
|
|
@ -79,13 +79,13 @@ public final class FilesSyncHelper {
|
|||
// utility class -> private constructor
|
||||
}
|
||||
|
||||
public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) {
|
||||
public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder, boolean syncNow) {
|
||||
final Context context = MainApp.getAppContext();
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
|
||||
final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs();
|
||||
|
||||
if (syncedFolder.isEnabled() && enabledTimestampMs >= 0) {
|
||||
if (syncedFolder.isEnabled() && (syncedFolder.isExisting() || enabledTimestampMs >= 0)) {
|
||||
MediaFolderType mediaType = syncedFolder.getType();
|
||||
if (mediaType == MediaFolderType.IMAGE) {
|
||||
FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI
|
||||
|
@ -106,7 +106,7 @@ public final class FilesSyncHelper {
|
|||
@Override
|
||||
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
|
||||
File file = path.toFile();
|
||||
if (attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) {
|
||||
if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) {
|
||||
filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(),
|
||||
attrs.lastModifiedTime().toMillis(),
|
||||
file.isDirectory(), syncedFolder);
|
||||
|
@ -124,17 +124,26 @@ public final class FilesSyncHelper {
|
|||
Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (syncNow) {
|
||||
new JobRequest.Builder(FilesSyncJob.TAG)
|
||||
.setExact(1_000L)
|
||||
.setUpdateCurrent(false)
|
||||
.build()
|
||||
.schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void insertAllDBEntries(AppPreferences preferences, Clock clock, boolean skipCustom) {
|
||||
public static void insertAllDBEntries(AppPreferences preferences, Clock clock, boolean skipCustom,
|
||||
boolean syncNow) {
|
||||
final Context context = MainApp.getAppContext();
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences, clock);
|
||||
|
||||
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
|
||||
if (syncedFolder.isEnabled() && (MediaFolderType.CUSTOM != syncedFolder.getType() || !skipCustom)) {
|
||||
insertAllDBEntriesForSyncedFolder(syncedFolder);
|
||||
if (syncedFolder.isEnabled() && (!skipCustom || syncedFolder.getType() != MediaFolderType.CUSTOM)) {
|
||||
insertAllDBEntriesForSyncedFolder(syncedFolder, syncNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +180,7 @@ public final class FilesSyncHelper {
|
|||
while (cursor.moveToNext()) {
|
||||
contentPath = cursor.getString(column_index_data);
|
||||
isFolder = new File(contentPath).isDirectory();
|
||||
if (cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) {
|
||||
if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) {
|
||||
filesystemDataProvider.storeOrUpdateFileValue(contentPath,
|
||||
cursor.getLong(column_index_date_modified), isFolder,
|
||||
syncedFolder);
|
||||
|
@ -187,8 +196,6 @@ public final class FilesSyncHelper {
|
|||
final PowerManagementService powerManagementService) {
|
||||
final Context context = MainApp.getAppContext();
|
||||
|
||||
FileUploader.UploadRequester uploadRequester = new FileUploader.UploadRequester();
|
||||
|
||||
boolean accountExists;
|
||||
|
||||
OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads();
|
||||
|
@ -212,13 +219,15 @@ public final class FilesSyncHelper {
|
|||
new Thread(() -> {
|
||||
if (connectivityService.getActiveNetworkType() != JobRequest.NetworkType.ANY &&
|
||||
!connectivityService.isInternetWalled()) {
|
||||
uploadRequester.retryFailedUploads(context,
|
||||
null,
|
||||
uploadsStorageManager,
|
||||
connectivityService,
|
||||
accountManager,
|
||||
powerManagementService,
|
||||
null);
|
||||
FileUploader.retryFailedUploads(
|
||||
context,
|
||||
null,
|
||||
uploadsStorageManager,
|
||||
connectivityService,
|
||||
accountManager,
|
||||
powerManagementService,
|
||||
null
|
||||
);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
|
|
@ -19,12 +19,11 @@
|
|||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/standard_padding">
|
||||
android:id="@+id/root"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/top_title"
|
||||
|
@ -37,15 +36,14 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_padding">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/synced_folders_settings_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/synced_folders_preferences"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"/>
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/synced_folders_settings_local_folder_path"
|
||||
|
@ -53,20 +51,15 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/synced_folders_enable_switch_container"
|
||||
android:layout_width="@dimen/synced_folders_control_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="end|top"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingRight="@dimen/zero"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:paddingTop="@dimen/standard_padding">
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/sync_enabled"
|
||||
|
@ -74,10 +67,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:focusable="false"/>
|
||||
|
||||
android:focusable="false" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
|
@ -95,17 +86,13 @@
|
|||
android:id="@+id/local_folder_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall">
|
||||
android:baselineAligned="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingBottom="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_padding">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_folder_title"
|
||||
|
@ -113,62 +100,51 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:maxLines="2"
|
||||
android:text="@string/prefs_synced_folders_local_path_title"
|
||||
android:textAppearance="?attr/textAppearanceListItem"/>
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_folder_summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignLeft="@id/local_folder_title"
|
||||
android:layout_alignStart="@id/local_folder_title"
|
||||
android:layout_below="@id/local_folder_title"
|
||||
android:layout_alignStart="@id/local_folder_title"
|
||||
android:layout_alignLeft="@id/local_folder_title"
|
||||
android:ellipsize="middle"
|
||||
android:maxLines="2"
|
||||
android:text="@string/choose_local_folder"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Preference should place its actual preference widget here. -->
|
||||
<LinearLayout
|
||||
android:id="@+id/local_folder_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="@dimen/synced_folders_control_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingRight="@dimen/zero"
|
||||
android:paddingEnd="@dimen/zero">
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/local_folder_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@null"
|
||||
android:contentDescription="@string/folder_icon"
|
||||
android:padding="@dimen/standard_quarter_padding"
|
||||
android:src="@drawable/ic_folder_open"
|
||||
android:contentDescription="@string/folder_icon"/>
|
||||
|
||||
android:src="@drawable/ic_folder_open" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/remote_folder_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall">
|
||||
android:baselineAligned="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingBottom="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_padding">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/remote_folder_title"
|
||||
|
@ -176,62 +152,51 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:maxLines="2"
|
||||
android:text="@string/prefs_synced_folders_remote_path_title"
|
||||
android:textAppearance="?attr/textAppearanceListItem"/>
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/remote_folder_summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/remote_folder_title"
|
||||
android:layout_alignStart="@id/remote_folder_title"
|
||||
android:layout_alignLeft="@id/remote_folder_title"
|
||||
android:layout_below="@id/remote_folder_title"
|
||||
android:ellipsize="middle"
|
||||
android:maxLines="2"
|
||||
android:text="@string/choose_remote_folder"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Preference should place its actual preference widget here. -->
|
||||
<LinearLayout
|
||||
android:id="@+id/remote_folder_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="@dimen/synced_folders_control_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:paddingRight="@dimen/zero">
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/remote_folder_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@null"
|
||||
android:contentDescription="@string/folder_icon"
|
||||
android:padding="@dimen/standard_quarter_padding"
|
||||
android:src="@drawable/ic_folder_open"
|
||||
android:contentDescription="@string/folder_icon"/>
|
||||
|
||||
android:src="@drawable/ic_folder_open" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_instant_upload_on_wifi_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall">
|
||||
android:baselineAligned="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingBottom="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_padding">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/setting_instant_upload_on_wifi_label"
|
||||
|
@ -240,48 +205,37 @@
|
|||
android:ellipsize="marquee"
|
||||
android:maxLines="2"
|
||||
android:text="@string/auto_upload_on_wifi"
|
||||
android:textAppearance="?attr/textAppearanceListItem"/>
|
||||
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_instant_upload_on_wifi_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="@dimen/synced_folders_control_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingRight="@dimen/zero"
|
||||
android:paddingEnd="@dimen/zero">
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/setting_instant_upload_on_wifi_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:focusable="false"/>
|
||||
|
||||
android:focusable="false" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_instant_upload_on_charging_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall">
|
||||
android:baselineAligned="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingBottom="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_padding">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/setting_instant_upload_on_charging_label"
|
||||
|
@ -290,48 +244,76 @@
|
|||
android:ellipsize="marquee"
|
||||
android:maxLines="2"
|
||||
android:text="@string/instant_upload_on_charging"
|
||||
android:textAppearance="?attr/textAppearanceListItem"/>
|
||||
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_instant_upload_on_charging_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="@dimen/synced_folders_control_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingRight="@dimen/zero"
|
||||
android:paddingEnd="@dimen/zero">
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/setting_instant_upload_on_charging_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:focusable="false"/>
|
||||
|
||||
android:focusable="false" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_instant_upload_existing_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/setting_instant_upload_existing_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="@string/instant_upload_existing"
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_instant_upload_existing"
|
||||
android:layout_width="@dimen/synced_folders_control_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/setting_instant_upload_existing_checkbox"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:focusable="false" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_instant_upload_path_use_subfolders_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall">
|
||||
android:baselineAligned="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingBottom="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_padding">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/setting_instant_upload_path_use_subfolders_label"
|
||||
|
@ -340,44 +322,37 @@
|
|||
android:ellipsize="marquee"
|
||||
android:maxLines="2"
|
||||
android:text="@string/prefs_instant_upload_path_use_subfolders_title"
|
||||
android:textAppearance="?attr/textAppearanceListItem"/>
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/setting_instant_upload_path_use_subfolders_summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignLeft="@id/setting_instant_upload_path_use_subfolders_label"
|
||||
android:layout_alignStart="@id/setting_instant_upload_path_use_subfolders_label"
|
||||
android:layout_below="@id/setting_instant_upload_path_use_subfolders_label"
|
||||
android:layout_alignStart="@id/setting_instant_upload_path_use_subfolders_label"
|
||||
android:layout_alignLeft="@id/setting_instant_upload_path_use_subfolders_label"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="@string/prefs_instant_upload_path_use_subfolders_summary"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Preference should place its actual preference widget here. -->
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_instant_upload_path_use_subfolders_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="@dimen/synced_folders_control_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingRight="@dimen/zero"
|
||||
android:paddingEnd="@dimen/zero">
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/setting_instant_upload_path_use_subfolders_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:focusable="false"/>
|
||||
|
||||
android:focusable="false" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
@ -389,8 +364,7 @@
|
|||
android:gravity="center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_padding">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/setting_instant_behaviour_title"
|
||||
|
@ -399,7 +373,7 @@
|
|||
android:ellipsize="marquee"
|
||||
android:maxLines="2"
|
||||
android:text="@string/prefs_instant_behaviour_title"
|
||||
android:textAppearance="?attr/textAppearanceListItem"/>
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/setting_instant_behaviour_summary"
|
||||
|
@ -408,49 +382,44 @@
|
|||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="@string/placeholder_filename"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/delete"
|
||||
style="@style/Button.Borderless.Destructive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:text="@string/common_delete"/>
|
||||
android:layout_alignParentLeft="true"
|
||||
android:text="@string/common_delete" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true">
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/cancel"
|
||||
style="@style/Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/common_cancel"/>
|
||||
android:text="@string/common_cancel" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/save"
|
||||
style="@style/Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/common_save"/>
|
||||
|
||||
android:text="@string/common_save" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
30
src/main/res/menu/upload_list_item_file_conflict.xml
Normal file
30
src/main/res/menu/upload_list_item_file_conflict.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2019 Nextcloud GmbH.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_upload_list_resolve_conflict"
|
||||
android:icon="@drawable/ic_history"
|
||||
android:title="@string/upload_list_resolve_conflict" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_upload_list_delete"
|
||||
android:title="@string/upload_list_delete"
|
||||
android:icon="@drawable/nav_trashbin" />
|
||||
</menu>
|
|
@ -135,6 +135,7 @@
|
|||
<dimen name="synced_folders_item_type_layout_height">32dp</dimen>
|
||||
<dimen name="synced_folders_item_type_layout_right_end_margin">24dp</dimen>
|
||||
<dimen name="synced_folders_recycler_view_layout_margin">-3dp</dimen>
|
||||
<dimen name="synced_folders_control_width">80dp</dimen>
|
||||
<dimen name="toolbar_user_information_layout_margin">12dp</dimen>
|
||||
<dimen name="bottom_sheet_text_size">16sp</dimen>
|
||||
<dimen name="permission_dialog_text_size">18sp</dimen>
|
||||
|
|
|
@ -171,6 +171,7 @@
|
|||
<string name="uploads_view_upload_status_unknown_fail">Unknown error</string>
|
||||
<string name="uploads_view_upload_status_waiting_for_wifi">Waiting for Wi-Fi</string>
|
||||
<string name="uploads_view_upload_status_waiting_exit_power_save_mode">Waiting to exit power save mode</string>
|
||||
<string name="uploads_view_upload_status_fetching_server_version">Fetching server version…</string>
|
||||
<string name="uploads_view_later_waiting_to_upload">Waiting to upload</string>
|
||||
<string name="uploads_view_group_header" translatable="false">%1$s (%2$d)</string>
|
||||
<string name="downloader_download_in_progress_ticker">Downloading…</string>
|
||||
|
@ -323,6 +324,7 @@
|
|||
|
||||
<string name="auto_upload_on_wifi">Only upload on unmetered Wi-Fi</string>
|
||||
<string name="instant_upload_on_charging">Only upload when charging</string>
|
||||
<string name="instant_upload_existing">Also upload existing files</string>
|
||||
<string name="instant_upload_path">/InstantUpload</string>
|
||||
<string name="auto_upload_path">/AutoUpload</string>
|
||||
<string name="conflict_title">File conflict</string>
|
||||
|
@ -912,6 +914,10 @@
|
|||
<string name="create_rich_workspace">Add folder info</string>
|
||||
<string name="creates_rich_workspace">creates folder info</string>
|
||||
<string name="edit_rich_workspace">edit folder info</string>
|
||||
<string name="uploader_upload_failed_sync_conflict_error">File upload conflict</string>
|
||||
<string name="uploader_upload_failed_sync_conflict_error_content">Pick which version to keep of %1$s</string>
|
||||
<string name="upload_list_resolve_conflict">Resolve conflict</string>
|
||||
<string name="upload_list_delete">Delete</string>
|
||||
<string name="create_new">Create new</string>
|
||||
<string name="editor_placeholder" translatable="false">%1$s %2$s</string>
|
||||
|
||||
|
|
|
@ -164,6 +164,7 @@ public class SyncedFoldersActivityTest {
|
|||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
"test@nextcloud.com",
|
||||
1,
|
||||
enabled,
|
||||
|
|
Loading…
Reference in a new issue