Skip to content

Commit 5739997

Browse files
authored
Merge pull request #4802 from mykh-hailo/feat/copy_snack_bar
[FEATURE REQUEST] Allow to go to the destination folder when the copy/move operation is finished
2 parents c034631 + a790a92 commit 5739997

9 files changed

Lines changed: 118 additions & 15 deletions

File tree

changelog/unreleased/4802

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Enhancement: Show destination folder snackbar for copy/move operations
2+
3+
A snackbar message has been displayed after copy or move operations with an action button that allows users to quickly navigate to the destination folder.
4+
5+
https://github.com/owncloud/android/issues/4379
6+
https://github.com/owncloud/android/pull/4802

owncloudApp/src/main/java/com/owncloud/android/extensions/ActivityExt.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@ fun Activity.showMessageInSnackbar(
9090
Snackbar.make(findViewById(layoutId), message, duration).show()
9191
}
9292

93+
fun Activity.showSnackbarWithAction(
94+
message: CharSequence,
95+
actionText: CharSequence,
96+
action: () -> Unit,
97+
duration: Int = Snackbar.LENGTH_LONG,
98+
layoutId: Int = android.R.id.content
99+
) {
100+
Snackbar.make(findViewById(layoutId), message, duration)
101+
.setAction(actionText) { action() }
102+
.show()
103+
}
104+
93105
fun Activity.showErrorInToast(
94106
genericErrorMessageId: Int,
95107
throwable: Throwable?,

owncloudApp/src/main/java/com/owncloud/android/extensions/FragmentExt.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ fun Fragment.showMessageInSnackbar(
5151
Snackbar.make(requiredView, message, duration).show()
5252
}
5353

54+
fun Fragment.showSnackbarWithAction(
55+
message: CharSequence,
56+
actionText: CharSequence,
57+
action: () -> Unit,
58+
duration: Int = Snackbar.LENGTH_LONG
59+
) {
60+
val requiredView = view ?: return
61+
Snackbar.make(requiredView, message, duration)
62+
.setAction(actionText) { action() }
63+
.show()
64+
}
65+
5466
fun Fragment.showAlertDialog(
5567
title: String,
5668
message: String,

owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import com.owncloud.android.extensions.openOCFile
5757
import com.owncloud.android.extensions.sendDownloadedFilesByShareSheet
5858
import com.owncloud.android.extensions.showErrorInSnackbar
5959
import com.owncloud.android.extensions.showMessageInSnackbar
60+
import com.owncloud.android.extensions.showSnackbarWithAction
6061
import com.owncloud.android.presentation.authentication.ACTION_UPDATE_EXPIRED_TOKEN
6162
import com.owncloud.android.presentation.authentication.EXTRA_ACCOUNT
6263
import com.owncloud.android.presentation.authentication.EXTRA_ACTION
@@ -168,16 +169,19 @@ class FileDetailsFragment : FileFragment() {
168169
when (uiResult) {
169170
is UIResult.Error -> {
170171
if (uiResult.error is AccountNotFoundException) {
171-
Snackbar.make(view, getString(R.string.sync_fail_ticker_unauthorized), Snackbar.LENGTH_INDEFINITE)
172-
.setAction(R.string.auth_oauth_failure_snackbar_action) {
172+
showSnackbarWithAction(
173+
message = getString(R.string.sync_fail_ticker_unauthorized),
174+
actionText = getString(R.string.auth_oauth_failure_snackbar_action),
175+
action = {
173176
val updateAccountCredentials = Intent(requireActivity(), LoginActivity::class.java)
174177
updateAccountCredentials.apply {
175178
putExtra(EXTRA_ACCOUNT, fileDetailsViewModel.getAccount())
176179
putExtra(EXTRA_ACTION, ACTION_UPDATE_EXPIRED_TOKEN)
177180
addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
178181
}
179182
startActivity(updateAccountCredentials)
180-
}.show()
183+
},
184+
duration = Snackbar.LENGTH_INDEFINITE)
181185
} else {
182186
showErrorInSnackbar(R.string.sync_fail_ticker, uiResult.error)
183187
fileDetailsViewModel.updateActionInDetailsView(NONE)

owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ class FileListAdapter(
288288
params -> params.marginStart = if (isFolderInKw) 0 else
289289
context.resources.getDimensionPixelSize(R.dimen.standard_quarter_margin) }
290290
it.fileListLastMod.text = DisplayUtils.getRelativeTimestamp(context, file.modificationTimestamp)
291-
it.threeDotMenu.isVisible = getCheckedItems().isEmpty()
291+
it.threeDotMenu.isVisible = !isPickerMode && getCheckedItems().isEmpty()
292292
it.threeDotMenu.contentDescription = context.getString(R.string.content_description_file_operations, file.fileName)
293293
if (fileListOption.isAvailableOffline() || (fileListOption.isSharedByLink() && fileWithSyncInfo.space == null)) {
294294
it.spacePathLine.path.apply {

owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ class ReleaseNotesViewModel(
6363
subtitle = R.string.release_notes_4_8_0_subtitle_spaces_permanent_links,
6464
type = ReleaseNoteType.ENHANCEMENT
6565
),
66+
ReleaseNote(
67+
title = R.string.release_notes_4_8_0_title_action_to_copy_or_move_destination_folder,
68+
subtitle = R.string.release_notes_4_8_0_subtitle_action_to_copy_or_move_destination_folder,
69+
type = ReleaseNoteType.ENHANCEMENT
70+
),
6671
ReleaseNote(
6772
title = R.string.release_notes_bugfixes_title,
6873
subtitle = R.string.release_notes_bugfixes_subtitle,

owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,11 @@
5858
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
5959
import com.owncloud.android.ui.errorhandling.ErrorMessageAdapter;
6060
import com.owncloud.android.ui.helpers.FileOperationsHelper;
61+
import kotlin.Unit;
6162
import timber.log.Timber;
6263

64+
import static com.owncloud.android.extensions.ActivityExtKt.showSnackbarWithAction;
65+
6366
/**
6467
* Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s .
6568
*/
@@ -278,18 +281,32 @@ protected void showRequestAccountChangeNotice(String errorMessage, boolean mustC
278281
.setCancelable(false)
279282
.show();
280283
} else {
281-
Snackbar.make(findViewById(android.R.id.content), errorMessage, Snackbar.LENGTH_INDEFINITE)
282-
.setAction(R.string.auth_oauth_failure_snackbar_action, v ->
283-
requestCredentialsUpdate())
284-
.show();
284+
showSnackbarWithAction(
285+
this,
286+
errorMessage,
287+
getString(R.string.auth_oauth_failure_snackbar_action),
288+
() -> {
289+
requestCredentialsUpdate();
290+
return Unit.INSTANCE;
291+
},
292+
Snackbar.LENGTH_INDEFINITE,
293+
android.R.id.content
294+
);
285295
}
286296
}
287297

288298
protected void showRequestRegainAccess() {
289-
Snackbar.make(findViewById(android.R.id.content), R.string.auth_oauth_failure, Snackbar.LENGTH_INDEFINITE)
290-
.setAction(R.string.auth_oauth_failure_snackbar_action, v ->
291-
requestCredentialsUpdate())
292-
.show();
299+
showSnackbarWithAction(
300+
this,
301+
getString(R.string.auth_oauth_failure),
302+
getString(R.string.auth_oauth_failure_snackbar_action),
303+
() -> {
304+
requestCredentialsUpdate();
305+
return Unit.INSTANCE;
306+
},
307+
Snackbar.LENGTH_INDEFINITE,
308+
android.R.id.content
309+
);
293310
}
294311

295312
/**

owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ import com.owncloud.android.extensions.parseError
8484
import com.owncloud.android.extensions.sendDownloadedFilesByShareSheet
8585
import com.owncloud.android.extensions.showErrorInSnackbar
8686
import com.owncloud.android.extensions.showMessageInSnackbar
87+
import com.owncloud.android.extensions.showSnackbarWithAction
8788
import com.owncloud.android.lib.common.accounts.AccountUtils
8889
import com.owncloud.android.lib.common.authentication.OwnCloudBearerCredentials
8990
import com.owncloud.android.lib.common.network.CertificateCombinedException
@@ -181,6 +182,8 @@ class FileDisplayActivity : FileActivity(),
181182
private var waitingToSend: OCFile? = null
182183
private var waitingToOpen: OCFile? = null
183184

185+
private var copyMoveTargetFolder: OCFile? = null
186+
184187
private var localBroadcastManager: LocalBroadcastManager? = null
185188

186189
private val fileOperationsViewModel: FileOperationsViewModel by viewModel()
@@ -707,6 +710,7 @@ class FileDisplayActivity : FileActivity(),
707710
private fun requestMoveOperation(data: Intent) {
708711
val folderToMoveAt = data.getParcelableExtra<OCFile>(FolderPickerActivity.EXTRA_FOLDER) ?: return
709712
val files = data.getParcelableArrayListExtra<OCFile>(FolderPickerActivity.EXTRA_FILES) ?: return
713+
copyMoveTargetFolder = folderToMoveAt
710714
val moveOperation = FileOperation.MoveOperation(
711715
listOfFilesToMove = files.toList(),
712716
targetFolder = folderToMoveAt,
@@ -723,6 +727,7 @@ class FileDisplayActivity : FileActivity(),
723727
private fun requestCopyOperation(data: Intent) {
724728
val folderToCopyAt = data.getParcelableExtra<OCFile>(FolderPickerActivity.EXTRA_FOLDER) ?: return
725729
val files = data.getParcelableArrayListExtra<OCFile>(FolderPickerActivity.EXTRA_FILES) ?: return
730+
copyMoveTargetFolder = folderToCopyAt
726731
val copyOperation = FileOperation.CopyOperation(
727732
listOfFilesToCopy = files.toList(),
728733
targetFolder = folderToCopyAt,
@@ -808,12 +813,10 @@ class FileDisplayActivity : FileActivity(),
808813
Timber.v("onResume() start")
809814
super.onResume()
810815

816+
updateBottombar(mainFileListFragment?.getCurrentSpace())
811817
if (mainFileListFragment?.getCurrentSpace()?.isProject == true ||
812818
(mainFileListFragment?.getCurrentSpace()?.isPersonal == true && isMultiPersonal)) {
813-
setCheckedItemAtBottomBar(getMenuItemForFileListOption(FileListOption.SPACES_LIST))
814819
updateToolbar(null, mainFileListFragment?.getCurrentSpace())
815-
} else {
816-
setCheckedItemAtBottomBar(getMenuItemForFileListOption(fileListOption))
817820
}
818821

819822
if (secondFragment == null) {
@@ -1082,6 +1085,9 @@ class FileDisplayActivity : FileActivity(),
10821085
// Refresh the spaces and update the quota
10831086
spacesListViewModel.refreshSpacesFromServer()
10841087
}
1088+
if (uiResult.data.isNullOrEmpty()) {
1089+
showCopyMoveSuccessSnackbar(isCopy = false)
1090+
}
10851091
}
10861092

10871093
is UIResult.Error -> {
@@ -1130,6 +1136,9 @@ class FileDisplayActivity : FileActivity(),
11301136

11311137
// Refresh the spaces and update the quota
11321138
spacesListViewModel.refreshSpacesFromServer()
1139+
if (uiResult.data.isNullOrEmpty()) {
1140+
showCopyMoveSuccessSnackbar(isCopy = true)
1141+
}
11331142
}
11341143

11351144
is UIResult.Error -> {
@@ -1148,6 +1157,29 @@ class FileDisplayActivity : FileActivity(),
11481157
}
11491158
}
11501159

1160+
private fun showCopyMoveSuccessSnackbar(isCopy: Boolean) {
1161+
val message = getString(if (isCopy) R.string.copy_file_correctly else R.string.move_file_correctly)
1162+
val targetFolderId = copyMoveTargetFolder?.id
1163+
if (targetFolderId != null) {
1164+
showSnackbarWithAction(
1165+
message = message,
1166+
actionText = getString(R.string.go_to_destination_folder),
1167+
action = {
1168+
val fileListFragment = mainFileListFragment
1169+
?: supportFragmentManager.findFragmentById(R.id.left_fragment_container) as? MainFileListFragment
1170+
fileListFragment?.navigateToFolderId(targetFolderId)
1171+
val targetFolderSpace = spacesListViewModel.spacesList.value.spaces.find {
1172+
it.id == copyMoveTargetFolder?.spaceId
1173+
}
1174+
updateBottombar(targetFolderSpace)
1175+
},
1176+
layoutId = R.id.list_layout
1177+
)
1178+
} else {
1179+
showMessageInSnackbar(R.id.list_layout, message)
1180+
}
1181+
}
1182+
11511183
private fun showConflictDecisionDialog(
11521184
uiResult: UIResult.Success<List<OCFile>>,
11531185
data: List<OCFile>,
@@ -1866,6 +1898,15 @@ class FileDisplayActivity : FileActivity(),
18661898
file = newCurrentFolder
18671899
}
18681900

1901+
private fun updateBottombar(currentSpace: OCSpace?) {
1902+
val bottomBarOption = if (currentSpace?.isProject == true) {
1903+
FileListOption.SPACES_LIST
1904+
} else {
1905+
fileListOption
1906+
}
1907+
setCheckedItemAtBottomBar(getMenuItemForFileListOption(bottomBarOption))
1908+
}
1909+
18691910
override fun onFileClicked(file: OCFile) {
18701911
when {
18711912
PreviewImageFragment.canBePreviewed(file) -> {

owncloudApp/src/main/res/values/strings.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,14 +544,18 @@
544544
<string name="move_file_invalid_into_descendent">It is not possible to move a folder into a descendant.</string>
545545
<string name="move_file_invalid_overwrite">The file exists already in the destination folder.</string>
546546
<string name="move_file_error">An error occurred while trying to move this file or folder.</string>
547+
<string name="move_file_correctly">Item(s) moved correctly</string>
547548
<string name="forbidden_permissions_move">to move this file</string>
548549

549550
<string name="copy_file_not_found">Unable to copy. Please check whether the file exists.</string>
550551
<string name="copy_file_invalid_into_descendent">It is not possible to copy a folder into a descendant.</string>
551552
<string name="copy_file_invalid_overwrite">The file exists already in the destination folder.</string>
552553
<string name="copy_file_error">An error occurred while trying to copy this file or folder.</string>
554+
<string name="copy_file_correctly">Item(s) copied correctly</string>
553555
<string name="forbidden_permissions_copy">to copy this file</string>
554556

557+
<string name="go_to_destination_folder">Open Folder</string>
558+
555559
<string name="prefs_category_camera_upload">Camera uploads</string>
556560

557561
<string name="sync_folder_failed_content">Synchronization of %1$s folder could not be completed</string>
@@ -748,6 +752,8 @@
748752
<string name="release_notes_4_8_0_subtitle_spaces_permanent_links">Infinite Scale users can now get a permanent link for a space and share it with other members</string>
749753
<string name="release_notes_4_8_0_title_space_public_links">Space public links</string>
750754
<string name="release_notes_4_8_0_subtitle_space_public_links">Infinite Scale users can see all public links of a space and manage them with right permissions</string>
755+
<string name="release_notes_4_8_0_title_action_to_copy_or_move_destination_folder">Navigation to target folder</string>
756+
<string name="release_notes_4_8_0_subtitle_action_to_copy_or_move_destination_folder">New action to navigate to the destination folder when a file or folder is copied or moved</string>
751757

752758
<!-- Open in web -->
753759
<string name="ic_action_open_in_web">Open in web</string>

0 commit comments

Comments
 (0)