diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index f13865781915..e45b99e958fe 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -383 +384 diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index 3acbd96d368e..c9735370557b 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -40,6 +40,7 @@ public class SyncedFolder implements Serializable, Cloneable { private boolean subfolderByDate; private String account; private int uploadAction; + private int nameCollisionPolicy; private boolean enabled; private long enabledTimestampMs; private MediaFolderType type; @@ -48,18 +49,19 @@ public class SyncedFolder implements Serializable, Cloneable { /** * constructor for new, to be persisted entity. * - * @param localPath local path - * @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 - * @param enabled flag if synced folder config is active - * @param timestampMs the current timestamp in milliseconds - * @param type the type of the folder - * @param hidden hide item flag + * @param localPath local path + * @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 + * @param nameCollisionPolicy the behaviour to follow if detecting a collision + * @param enabled flag if synced folder config is active + * @param timestampMs the current timestamp in milliseconds + * @param type the type of the folder + * @param hidden hide item flag */ public SyncedFolder(String localPath, String remotePath, @@ -69,12 +71,25 @@ public SyncedFolder(String localPath, boolean subfolderByDate, String account, int uploadAction, + int nameCollisionPolicy, boolean enabled, long timestampMs, MediaFolderType type, boolean hidden) { - this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account, - uploadAction, enabled, timestampMs, type, hidden); + this(UNPERSISTED_ID, + localPath, + remotePath, + wifiOnly, + chargingOnly, + existing, + subfolderByDate, + account, + uploadAction, + nameCollisionPolicy, + enabled, + timestampMs, + type, + hidden); } /** @@ -91,6 +106,7 @@ protected SyncedFolder(long id, boolean subfolderByDate, String account, int uploadAction, + int nameCollisionPolicy, boolean enabled, long timestampMs, MediaFolderType type, @@ -104,6 +120,7 @@ protected SyncedFolder(long id, this.subfolderByDate = subfolderByDate; this.account = account; this.uploadAction = uploadAction; + this.nameCollisionPolicy = nameCollisionPolicy; this.setEnabled(enabled, timestampMs); this.type = type; this.hidden = hidden; @@ -161,6 +178,10 @@ public int getUploadAction() { return this.uploadAction; } + public int getNameCollisionPolicy() { + return this.nameCollisionPolicy; + } + public boolean isEnabled() { return this.enabled; } @@ -213,6 +234,10 @@ public void setUploadAction(int uploadAction) { this.uploadAction = uploadAction; } + public void setNameCollisionPolicy(int nameCollisionPolicy) { + this.nameCollisionPolicy = nameCollisionPolicy; + } + public void setType(MediaFolderType type) { this.type = type; } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index 3cd5a7d21609..e0d52834328d 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -60,6 +60,7 @@ public SyncedFolderDisplayItem(long id, boolean subfolderByDate, String account, int uploadAction, + int nameCollisionPolicy, boolean enabled, long timestampMs, List filePaths, @@ -67,8 +68,20 @@ public SyncedFolderDisplayItem(long id, long numberOfFiles, MediaFolderType type, boolean hidden) { - super(id, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account, uploadAction, - enabled, timestampMs, type, hidden); + super(id, + localPath, + remotePath, + wifiOnly, + chargingOnly, + existing, + subfolderByDate, + account, + uploadAction, + nameCollisionPolicy, + enabled, + timestampMs, + type, + hidden); this.filePaths = filePaths; this.folderName = folderName; this.numberOfFiles = numberOfFiles; @@ -83,11 +96,26 @@ public SyncedFolderDisplayItem(long id, boolean subfolderByDate, String account, int uploadAction, + int nameCollisionPolicy, boolean enabled, long timestampMs, - String folderName, MediaFolderType type, boolean hidden) { - super(id, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account, uploadAction, - enabled, timestampMs, type, hidden); + String folderName, + MediaFolderType type, + boolean hidden) { + super(id, + localPath, + remotePath, + wifiOnly, + chargingOnly, + existing, + subfolderByDate, + account, + uploadAction, + nameCollisionPolicy, + enabled, + timestampMs, + type, + hidden); this.folderName = folderName; } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index 118c60e44a31..4e3c4f7983cb 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -350,6 +350,8 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT)); int uploadAction = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION)); + int nameCollisionPolicy = cursor.getInt(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_NAME_COLLISION_POLICY)); boolean enabled = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1; long enabledTimestampMs = cursor.getLong(cursor.getColumnIndex( @@ -359,9 +361,20 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { boolean hidden = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN)) == 1; - syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, existing, - subfolderByDate, accountName, uploadAction, enabled, enabledTimestampMs, - type, hidden); + syncedFolder = new SyncedFolder(id, + localPath, + remotePath, + wifiOnly, + chargingOnly, + existing, + subfolderByDate, + accountName, + uploadAction, + nameCollisionPolicy, + enabled, + enabledTimestampMs, + type, + hidden); } return syncedFolder; } @@ -385,6 +398,8 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.isSubfolderByDate()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_NAME_COLLISION_POLICY, + syncedFolder.getNameCollisionPolicy()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().getId()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.isHidden()); diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index 5953bab1c3b1..974046815bb7 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -31,7 +31,7 @@ */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 54; + public static final int DB_VERSION = 55; private ProviderMeta() { // No instance @@ -230,6 +230,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String SYNCED_FOLDER_SUBFOLDER_BY_DATE = "subfolder_by_date"; public static final String SYNCED_FOLDER_ACCOUNT = "account"; public static final String SYNCED_FOLDER_UPLOAD_ACTION = "upload_option"; + public static final String SYNCED_FOLDER_NAME_COLLISION_POLICY = "name_collision_policy"; public static final String SYNCED_FOLDER_HIDDEN = "hidden"; // Columns of external links table diff --git a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java index f507fa224470..73efea3956d7 100644 --- a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java @@ -165,13 +165,6 @@ private void syncFolder( SimpleDateFormat sFormatter, SyncedFolder syncedFolder ) { - String remotePath; - boolean subfolderByDate; - Integer uploadAction; - boolean needsCharging; - boolean needsWifi; - File file; - ArbitraryDataProvider arbitraryDataProvider; String accountName = syncedFolder.getAccount(); Optional optionalUser = userAccountManager.getUser(accountName); if (!optionalUser.isPresent()) { @@ -179,12 +172,18 @@ private void syncFolder( } User user = optionalUser.get(); + ArbitraryDataProvider arbitraryDataProvider = null; if (lightVersion) { arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); - } else { - arbitraryDataProvider = null; } + String remotePath; + boolean subfolderByDate; + Integer uploadAction; + FileUploader.NameCollisionPolicy nameCollisionPolicy; + boolean needsCharging; + boolean needsWifi; + File file; for (String path : filesystemDataProvider.getFilesForUpload(syncedFolder.getLocalPath(), Long.toString(syncedFolder.getId()))) { file = new File(path); @@ -197,12 +196,15 @@ private void syncFolder( SettingsActivity.SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI); String uploadActionString = resources.getString(R.string.syncedFolder_light_upload_behaviour); uploadAction = getUploadAction(uploadActionString); + nameCollisionPolicy = FileUploader.NameCollisionPolicy.ASK_USER; subfolderByDate = resources.getBoolean(R.bool.syncedFolder_light_use_subfolders); remotePath = resources.getString(R.string.syncedFolder_remote_folder); } else { needsCharging = syncedFolder.isChargingOnly(); needsWifi = syncedFolder.isWifiOnly(); uploadAction = syncedFolder.getUploadAction(); + nameCollisionPolicy = FileUploader.NameCollisionPolicy.deserialize( + syncedFolder.getNameCollisionPolicy()); subfolderByDate = syncedFolder.isSubfolderByDate(); remotePath = syncedFolder.getRemotePath(); } @@ -224,7 +226,7 @@ private void syncFolder( UploadFileOperation.CREATED_AS_INSTANT_PICTURE, needsWifi, needsCharging, - FileUploader.NameCollisionPolicy.ASK_USER + nameCollisionPolicy ); filesystemDataProvider.updateFilesystemFileAsSentForUpload(path, diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 5212d73c1cbc..251d7ca92e5b 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -49,6 +49,7 @@ import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; +import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.ShareType; @@ -832,10 +833,11 @@ private void createSyncedFoldersTable(SQLiteDatabase db) { + 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_ENABLED_TIMESTAMP_MS + " INTEGER, " // enable date + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " TEXT, " // account + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER, " // upload action + + ProviderTableMeta.SYNCED_FOLDER_NAME_COLLISION_POLICY + " INTEGER, " // name collision policy + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER, " // type + ProviderTableMeta.SYNCED_FOLDER_HIDDEN + " INTEGER );" // hidden ); @@ -2168,6 +2170,29 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (!upgraded) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); } + + if (oldVersion < 55 && newVersion >= 55) { + Log_OC.i(SQL, "Entering in the #55 add synced.name_collision_policy."); + db.beginTransaction(); + try { + // Add synced.name_collision_policy + db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_NAME_COLLISION_POLICY + " INTEGER "); // integer + + // make sure all existing folders set to FileUploader.NameCollisionPolicy.ASK_USER. + db.execSQL("UPDATE " + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + " SET " + + ProviderTableMeta.SYNCED_FOLDER_NAME_COLLISION_POLICY + " = " + + FileUploader.NameCollisionPolicy.ASK_USER.serialize()); + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } } } } diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 62e3936fe7d6..2b86a0a6906d 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -420,6 +420,7 @@ private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull Sy syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), + syncedFolder.getNameCollisionPolicy(), syncedFolder.isEnabled(), clock.getCurrentTime(), filePaths, @@ -448,6 +449,7 @@ private SyncedFolderDisplayItem createSyncedFolder(@NonNull SyncedFolder syncedF syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), + syncedFolder.getNameCollisionPolicy(), syncedFolder.isEnabled(), clock.getCurrentTime(), mediaFolder.filePaths, @@ -475,6 +477,7 @@ private SyncedFolderDisplayItem createSyncedFolderFromMediaFolder(@NonNull Media false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, + FileUploader.NameCollisionPolicy.ASK_USER.serialize(), false, clock.getCurrentTime(), mediaFolder.filePaths, @@ -580,9 +583,21 @@ public boolean onOptionsItemSelected(MenuItem item) { 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, true, - false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false, - clock.getCurrentTime(), null, MediaFolderType.CUSTOM, false); + SyncedFolder.UNPERSISTED_ID, + null, + null, + true, + false, + true, + false, + getAccount().name, + FileUploader.LOCAL_BEHAVIOUR_FORGET, + FileUploader.NameCollisionPolicy.ASK_USER.serialize(), + false, + clock.getCurrentTime(), + null, + MediaFolderType.CUSTOM, + false); onSyncFolderSettingsClick(0, emptyCustomFolder); } @@ -711,25 +726,41 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { // so triggering a refresh 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.isExisting(), syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(), - syncedFolder.getUploadAction(), syncedFolder.isEnabled(), clock.getCurrentTime(), - new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.isHidden()); + SyncedFolder.UNPERSISTED_ID, + syncedFolder.getLocalPath(), + syncedFolder.getRemotePath(), + syncedFolder.isWifiOnly(), + syncedFolder.isChargingOnly(), + syncedFolder.isExisting(), + syncedFolder.isSubfolderByDate(), + syncedFolder.getAccount(), + syncedFolder.getUploadAction(), + syncedFolder.getNameCollisionPolicy().serialize(), + 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.isExisting(), - syncedFolder.isSubfolderByDate(), syncedFolder.getUploadAction(), + updateSyncedFolderItem(item, + syncedFolder.getId(), + syncedFolder.getLocalPath(), + syncedFolder.getRemotePath(), + syncedFolder.isWifiOnly(), + syncedFolder.isChargingOnly(), + syncedFolder.isExisting(), + syncedFolder.isSubfolderByDate(), + syncedFolder.getUploadAction(), + syncedFolder.getNameCollisionPolicy().serialize(), syncedFolder.isEnabled()); saveOrUpdateSyncedFolder(item); - // TODO test if notifiyItemChanged is suffiecient (should improve performance) + // TODO test if notifyItemChanged is sufficient (should improve performance) adapter.notifyDataSetChanged(); } @@ -796,6 +827,7 @@ public void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder) * @param existing also upload existing * @param subfolderByDate created sub folders * @param uploadAction upload action + * @param nameCollisionPolicy what to do on name collision * @param enabled is sync enabled */ private void updateSyncedFolderItem(SyncedFolderDisplayItem item, @@ -807,6 +839,7 @@ private void updateSyncedFolderItem(SyncedFolderDisplayItem item, boolean existing, boolean subfolderByDate, Integer uploadAction, + Integer nameCollisionPolicy, boolean enabled) { item.setId(id); item.setLocalPath(localPath); @@ -816,6 +849,7 @@ private void updateSyncedFolderItem(SyncedFolderDisplayItem item, item.setExisting(existing); item.setSubfolderByDate(subfolderByDate); item.setUploadAction(uploadAction); + item.setNameCollisionPolicy(nameCollisionPolicy); item.setEnabled(enabled, clock.getCurrentTime()); } diff --git a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java index 79c7e8596a40..083401128a56 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java @@ -39,6 +39,7 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; +import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.FolderPickerActivity; import com.owncloud.android.ui.activity.UploadFilesActivity; @@ -69,17 +70,20 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment { private final static String TAG = SyncedFolderPreferencesDialogFragment.class.getSimpleName(); private static final String BEHAVIOUR_DIALOG_STATE = "BEHAVIOUR_DIALOG_STATE"; + private static final String NAME_COLLISION_POLICY_DIALOG_STATE = "NAME_COLLISION_POLICY_DIALOG_STATE"; private final static float alphaEnabled = 1.0f; private final static float alphaDisabled = 0.7f; protected View mView; private CharSequence[] mUploadBehaviorItemStrings; + private CharSequence[] mNameCollisionPolicyItemStrings; private SwitchCompat mEnabledSwitch; private AppCompatCheckBox mUploadOnWifiCheckbox; private AppCompatCheckBox mUploadOnChargingCheckbox; private AppCompatCheckBox mUploadExistingCheckbox; private AppCompatCheckBox mUploadUseSubfoldersCheckbox; private TextView mUploadBehaviorSummary; + private TextView mNameCollisionPolicySummary; private TextView mLocalFolderPath; private TextView mLocalFolderSummary; private TextView mRemoteFolderSummary; @@ -88,6 +92,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment { private MaterialButton mCancel; private MaterialButton mSave; private boolean behaviourDialogShown; + private boolean nameCollisionPolicyDialogShown; private AlertDialog behaviourDialog; public static SyncedFolderPreferencesDialogFragment newInstance(SyncedFolderDisplayItem syncedFolder, int section) { @@ -124,6 +129,7 @@ public void onCreate(Bundle savedInstanceState) { mSyncedFolder = getArguments().getParcelable(SYNCED_FOLDER_PARCELABLE); mUploadBehaviorItemStrings = getResources().getTextArray(R.array.pref_behaviour_entries); + mNameCollisionPolicyItemStrings = getResources().getTextArray(R.array.pref_name_collision_policy_entries); } @Override @@ -192,6 +198,8 @@ private void setupDialogElements(View view) { mUploadBehaviorSummary = view.findViewById(R.id.setting_instant_behaviour_summary); + mNameCollisionPolicySummary = view.findViewById(R.id.setting_instant_name_collision_policy_summary); + mCancel = view.findViewById(R.id.cancel); ThemeUtils.themeDialogActionButton(mCancel); @@ -228,6 +236,10 @@ private void setupDialogElements(View view) { mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.isSubfolderByDate()); mUploadBehaviorSummary.setText(mUploadBehaviorItemStrings[mSyncedFolder.getUploadActionInteger()]); + + final int nameCollisionPolicyIndex = + getSelectionIndexForNameCollisionPolicy(mSyncedFolder.getNameCollisionPolicy()); + mNameCollisionPolicySummary.setText(mNameCollisionPolicyItemStrings[nameCollisionPolicyIndex]); } /** @@ -422,6 +434,14 @@ public void onClick(View v) { showBehaviourDialog(); } }); + + view.findViewById(R.id.setting_instant_name_collision_policy_container).setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + showNameCollisionPolicyDialog(); + } + }); } private void showBehaviourDialog() { @@ -454,6 +474,22 @@ public void onCancel(DialogInterface dialog) { behaviourDialog.show(); } + private void showNameCollisionPolicyDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + builder.setTitle(ThemeUtils.getColoredTitle( + getResources().getString(R.string.pref_instant_name_collision_policy_dialogTitle), + ThemeUtils.primaryAccentColor(getContext()))) + .setSingleChoiceItems(getResources().getTextArray(R.array.pref_name_collision_policy_entries), + getSelectionIndexForNameCollisionPolicy(mSyncedFolder.getNameCollisionPolicy()), + new OnNameCollisionDialogClickListener()) + .setOnCancelListener(dialog -> nameCollisionPolicyDialogShown = false); + + nameCollisionPolicyDialogShown = true; + behaviourDialog = builder.create(); + behaviourDialog.show(); + } + @Override @NonNull public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -479,6 +515,7 @@ public void onDestroyView() { @Override public void onSaveInstanceState(@NonNull Bundle outState) { outState.putBoolean(BEHAVIOUR_DIALOG_STATE, behaviourDialogShown); + outState.putBoolean(NAME_COLLISION_POLICY_DIALOG_STATE, nameCollisionPolicyDialogShown); super.onSaveInstanceState(outState); } @@ -487,10 +524,15 @@ public void onSaveInstanceState(@NonNull Bundle outState) { public void onViewStateRestored(@Nullable Bundle savedInstanceState) { behaviourDialogShown = savedInstanceState != null && savedInstanceState.getBoolean(BEHAVIOUR_DIALOG_STATE, false); + nameCollisionPolicyDialogShown = savedInstanceState != null && + savedInstanceState.getBoolean(NAME_COLLISION_POLICY_DIALOG_STATE, false); if (behaviourDialogShown) { showBehaviourDialog(); } + if (nameCollisionPolicyDialogShown){ + showNameCollisionPolicyDialog(); + } super.onViewStateRestored(savedInstanceState); } @@ -526,4 +568,49 @@ public void onClick(View v) { ((OnSyncedFolderPreferenceListener) getActivity()).onDeleteSyncedFolderPreference(mSyncedFolder); } } + + private class OnNameCollisionDialogClickListener implements DialogInterface.OnClickListener { + @Override + public void onClick(DialogInterface dialog, int which) { + mSyncedFolder.setNameCollisionPolicy(getNameCollisionPolicyForSelectionIndex(which)); + + mNameCollisionPolicySummary.setText( + SyncedFolderPreferencesDialogFragment.this.mNameCollisionPolicyItemStrings[which]); + nameCollisionPolicyDialogShown = false; + dialog.dismiss(); + } + } + + /** + * Get index for name collision selection dialog. + * @return 0 if ASK_USER, 1 if OVERWRITE, 2 if RENAME. Otherwise: 0 + */ + static private Integer getSelectionIndexForNameCollisionPolicy(FileUploader.NameCollisionPolicy nameCollisionPolicy) { + switch (nameCollisionPolicy) { + case OVERWRITE: + return 1; + case RENAME: + return 2; + case ASK_USER: + default: + return 0; + } + } + + /** + * Get index for name collision selection dialog. + * Inverse of getSelectionIndexForNameCollisionPolicy. + * @return ASK_USER if 0, OVERWRITE if 1, RENAME if 2. Otherwise: ASK_USEr + */ + static private FileUploader.NameCollisionPolicy getNameCollisionPolicyForSelectionIndex(int index) { + switch (index) { + case 1: + return FileUploader.NameCollisionPolicy.OVERWRITE; + case 2: + return FileUploader.NameCollisionPolicy.RENAME; + case 0: + default: + return FileUploader.NameCollisionPolicy.ASK_USER; + } + } } diff --git a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java index 22f5417a9632..83d559f40bcc 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java +++ b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java @@ -40,6 +40,7 @@ public class SyncedFolderParcelable implements Parcelable { private boolean enabled = false; private boolean subfolderByDate = false; private Integer uploadAction; + private FileUploader.NameCollisionPolicy nameCollisionPolicy = FileUploader.NameCollisionPolicy.ASK_USER; private MediaFolderType type; private boolean hidden = false; private long id; @@ -59,6 +60,8 @@ public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, i type = syncedFolderDisplayItem.getType(); account = syncedFolderDisplayItem.getAccount(); uploadAction = syncedFolderDisplayItem.getUploadAction(); + nameCollisionPolicy = FileUploader.NameCollisionPolicy.deserialize( + syncedFolderDisplayItem.getNameCollisionPolicy()); this.section = section; hidden = syncedFolderDisplayItem.isHidden(); } @@ -76,6 +79,7 @@ private SyncedFolderParcelable(Parcel read) { type = MediaFolderType.getById(read.readInt()); account = read.readString(); uploadAction = read.readInt(); + nameCollisionPolicy = FileUploader.NameCollisionPolicy.deserialize(read.readInt()); section = read.readInt(); hidden = read.readInt() != 0; } @@ -97,6 +101,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(type.getId()); dest.writeString(account); dest.writeInt(uploadAction); + dest.writeInt(nameCollisionPolicy.serialize()); dest.writeInt(section); dest.writeInt(hidden ? 1 : 0); } @@ -182,6 +187,10 @@ public Integer getUploadAction() { return this.uploadAction; } + public FileUploader.NameCollisionPolicy getNameCollisionPolicy() { + return this.nameCollisionPolicy; + } + public MediaFolderType getType() { return this.type; } @@ -234,6 +243,10 @@ public void setSubfolderByDate(boolean subfolderByDate) { this.subfolderByDate = subfolderByDate; } + public void setNameCollisionPolicy(FileUploader.NameCollisionPolicy nameCollisionPolicy) { + this.nameCollisionPolicy = nameCollisionPolicy; + } + public void setType(MediaFolderType type) { this.type = type; } diff --git a/src/main/res/layout/synced_folders_settings_layout.xml b/src/main/res/layout/synced_folders_settings_layout.xml index 17679f934dce..2fc545c9c16f 100644 --- a/src/main/res/layout/synced_folders_settings_layout.xml +++ b/src/main/res/layout/synced_folders_settings_layout.xml @@ -381,6 +381,36 @@ android:text="@string/placeholder_filename" android:textColor="?android:attr/textColorSecondary" /> + + + + + + + diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index 42f937b2e688..b58518a81a15 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -11,4 +11,10 @@ LOCAL_BEHAVIOUR_MOVE LOCAL_BEHAVIOUR_DELETE + + + @string/pref_instant_name_collision_policy_entries_always_ask + @string/pref_instant_name_collision_policy_entries_overwrite + @string/pref_instant_name_collision_policy_entries_rename + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 84443b771f3a..0db6176616ca 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -463,6 +463,13 @@ deleted Storage path + What to do if the file already exists? + What to do if the file already exists? + + Ask me every time + Rename new version + Overwrite remote version + Sharing Share %1$s Share with users and groups diff --git a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java index 29bffa5c3ce5..42c91bec0b9f 100644 --- a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java +++ b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java @@ -24,6 +24,7 @@ import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; +import com.owncloud.android.files.services.FileUploader; import org.junit.Test; @@ -166,7 +167,8 @@ private SyncedFolderDisplayItem create(String folderName, boolean enabled) { true, true, "test@nextcloud.com", - 1, + FileUploader.LOCAL_BEHAVIOUR_MOVE, + FileUploader.NameCollisionPolicy.ASK_USER.serialize(), enabled, System.currentTimeMillis(), new ArrayList(),