Skip to content

Commit db5c34f

Browse files
committed
Albums delete and rename api integrated with ui flow.
1 parent 61c8a95 commit db5c34f

20 files changed

Lines changed: 530 additions & 20 deletions

app/src/main/java/com/nextcloud/client/di/ComponentsModule.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.nextcloud.ui.ChooseStorageLocationDialogFragment;
3232
import com.nextcloud.ui.ImageDetailFragment;
3333
import com.nextcloud.ui.SetStatusDialogFragment;
34+
import com.nextcloud.ui.albumItemActions.AlbumItemActionsBottomSheet;
3435
import com.nextcloud.ui.composeActivity.ComposeActivity;
3536
import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
3637
import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet;
@@ -324,6 +325,9 @@ abstract class ComponentsModule {
324325
@ContributesAndroidInjector
325326
abstract AlbumItemsFragment albumItemsFragment();
326327

328+
@ContributesAndroidInjector
329+
abstract AlbumItemActionsBottomSheet albumItemActionsBottomSheet();
330+
327331
@ContributesAndroidInjector
328332
abstract MultipleAccountsDialog multipleAccountsDialog();
329333

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2024 TSI-mc <surinder.kumar@t-systems.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
package com.nextcloud.ui.albumItemActions
8+
9+
import androidx.annotation.DrawableRes
10+
import androidx.annotation.IdRes
11+
import androidx.annotation.StringRes
12+
import com.owncloud.android.R
13+
14+
enum class AlbumItemAction(@IdRes val id: Int, @StringRes val title: Int, @DrawableRes val icon: Int? = null) {
15+
RENAME_ALBUM(R.id.action_rename_file, R.string.album_rename, R.drawable.ic_edit),
16+
DELETE_ALBUM(R.id.action_delete, R.string.album_delete, R.drawable.ic_delete);
17+
18+
companion object {
19+
/**
20+
* All file actions, in the order they should be displayed
21+
*/
22+
@JvmField
23+
val SORTED_VALUES = listOf(
24+
RENAME_ALBUM,
25+
DELETE_ALBUM,
26+
)
27+
}
28+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2024 TSI-mc <surinder.kumar@t-systems.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
package com.nextcloud.ui.albumItemActions
8+
9+
import android.os.Bundle
10+
import android.view.LayoutInflater
11+
import android.view.View
12+
import android.view.ViewGroup
13+
import androidx.annotation.IdRes
14+
import androidx.appcompat.content.res.AppCompatResources
15+
import androidx.core.os.bundleOf
16+
import androidx.core.view.isEmpty
17+
import androidx.fragment.app.FragmentManager
18+
import androidx.fragment.app.setFragmentResult
19+
import androidx.lifecycle.LifecycleOwner
20+
import com.google.android.material.bottomsheet.BottomSheetBehavior
21+
import com.google.android.material.bottomsheet.BottomSheetDialog
22+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
23+
import com.nextcloud.android.common.ui.theme.utils.ColorRole
24+
import com.nextcloud.client.di.Injectable
25+
import com.owncloud.android.databinding.FileActionsBottomSheetBinding
26+
import com.owncloud.android.databinding.FileActionsBottomSheetItemBinding
27+
import com.owncloud.android.utils.theme.ViewThemeUtils
28+
import javax.inject.Inject
29+
30+
class AlbumItemActionsBottomSheet : BottomSheetDialogFragment(), Injectable {
31+
32+
@Inject
33+
lateinit var viewThemeUtils: ViewThemeUtils
34+
35+
private var _binding: FileActionsBottomSheetBinding? = null
36+
val binding
37+
get() = _binding!!
38+
39+
fun interface ResultListener {
40+
fun onResult(@IdRes actionId: Int)
41+
}
42+
43+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
44+
_binding = FileActionsBottomSheetBinding.inflate(inflater, container, false)
45+
46+
val bottomSheetDialog = dialog as BottomSheetDialog
47+
bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
48+
bottomSheetDialog.behavior.skipCollapsed = true
49+
50+
viewThemeUtils.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE)
51+
52+
return binding.root
53+
}
54+
55+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
56+
super.onViewCreated(view, savedInstanceState)
57+
binding.bottomSheetHeader.visibility = View.GONE
58+
binding.bottomSheetLoading.visibility = View.GONE
59+
displayActions(AlbumItemAction.SORTED_VALUES)
60+
}
61+
62+
override fun onDestroyView() {
63+
super.onDestroyView()
64+
_binding = null
65+
}
66+
67+
fun setResultListener(
68+
fragmentManager: FragmentManager,
69+
lifecycleOwner: LifecycleOwner,
70+
listener: ResultListener
71+
): AlbumItemActionsBottomSheet {
72+
fragmentManager.setFragmentResultListener(REQUEST_KEY, lifecycleOwner) { _, result ->
73+
@IdRes val actionId = result.getInt(RESULT_KEY_ACTION_ID, -1)
74+
if (actionId != -1) {
75+
listener.onResult(actionId)
76+
}
77+
}
78+
return this
79+
}
80+
81+
private fun displayActions(actions: List<AlbumItemAction>) {
82+
if (binding.fileActionsList.isEmpty()) {
83+
actions.forEach { action ->
84+
val view = inflateActionView(action)
85+
binding.fileActionsList.addView(view)
86+
}
87+
}
88+
}
89+
90+
private fun inflateActionView(action: AlbumItemAction): View {
91+
val itemBinding = FileActionsBottomSheetItemBinding.inflate(layoutInflater, binding.fileActionsList, false)
92+
.apply {
93+
root.setOnClickListener {
94+
dispatchActionClick(action.id)
95+
}
96+
text.setText(action.title)
97+
if (action.icon != null) {
98+
val drawable =
99+
viewThemeUtils.platform.tintDrawable(
100+
requireContext(),
101+
AppCompatResources.getDrawable(requireContext(), action.icon)!!
102+
)
103+
icon.setImageDrawable(drawable)
104+
}
105+
}
106+
return itemBinding.root
107+
}
108+
109+
private fun dispatchActionClick(id: Int?) {
110+
if (id != null) {
111+
setFragmentResult(REQUEST_KEY, bundleOf(RESULT_KEY_ACTION_ID to id))
112+
parentFragmentManager.clearFragmentResultListener(REQUEST_KEY)
113+
dismiss()
114+
}
115+
}
116+
117+
companion object {
118+
private const val REQUEST_KEY = "REQUEST_KEY_ACTION"
119+
private const val RESULT_KEY_ACTION_ID = "RESULT_KEY_ACTION_ID"
120+
121+
@JvmStatic
122+
fun newInstance(): AlbumItemActionsBottomSheet {
123+
return AlbumItemActionsBottomSheet()
124+
}
125+
}
126+
}

app/src/main/java/com/owncloud/android/operations/albums/CopyFileToAlbumOperation.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,10 @@ private RemoteOperationResult performCopyOperation(String targetRemotePath, OwnC
106106
return new RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT);
107107
} else {
108108
CopyMethod copyMethod = null;
109-
RemoteOperationResult result = null;
109+
RemoteOperationResult result;
110110

111111
try {
112-
copyMethod = new CopyMethod(client.getFilesDavUri(this.srcPath), "https://pre1.next.magentacloud.de/remote.php/dav/photos/" + client.getUserId() + "/albums/" + WebdavUtils.encodePath(targetRemotePath), false);
112+
copyMethod = new CopyMethod(client.getFilesDavUri(this.srcPath), "https://pre1.next.magentacloud.de/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(targetRemotePath), false);
113113
int status = client.executeMethod(copyMethod);
114114
if (status == 207) {
115115
result = this.processPartialError(copyMethod);

app/src/main/java/com/owncloud/android/operations/albums/CreateNewAlbumOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ protected RemoteOperationResult<Void> run(OwnCloudClient client) {
4141
MkColMethod mkCol = null;
4242
RemoteOperationResult<Void> result;
4343
try {
44-
mkCol = new MkColMethod("https://pre1.next.magentacloud.de/remote.php/dav/photos/" + client.getUserId() + "/albums/" + WebdavUtils.encodePath(newAlbumName));
44+
mkCol = new MkColMethod("https://pre1.next.magentacloud.de/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(newAlbumName));
4545
client.executeMethod(mkCol);
4646
if (405 == mkCol.getStatusCode()) {
4747
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS);

app/src/main/java/com/owncloud/android/operations/albums/ReadAlbumsOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ protected RemoteOperationResult<List<ReadAlbumsOperation.PhotoAlbumEntry>> run(O
6161
Log_OC.e(TAG, "Fetch albums remote operation running");
6262
PropFindMethod propfind = null;
6363
RemoteOperationResult<List<ReadAlbumsOperation.PhotoAlbumEntry>> result;
64-
String url = "https://pre1.next.magentacloud.de/remote.php/dav/photos/" + client.getUserId() + "/albums/";
64+
String url = "https://pre1.next.magentacloud.de/remote.php/dav/photos/" + client.getUserId() + "/albums";
6565
if (!TextUtils.isEmpty(albumPath)) {
6666
url += WebdavUtils.encodePath(albumPath);
6767
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2021 Tobias Kaminsky <tobias@kaminsky.me>
5+
* SPDX-FileCopyrightText: 2019 Andy Scherzinger <info@andy-scherzinger.de>
6+
* SPDX-FileCopyrightText: 2015 ownCloud Inc.
7+
* SPDX-FileCopyrightText: 2015 María Asensio Valverde <masensio@solidgear.es>
8+
* SPDX-FileCopyrightText: 2014 David A. Velasco <dvelasco@solidgear.es>
9+
* SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only)
10+
*/
11+
package com.owncloud.android.operations.albums;
12+
13+
import com.owncloud.android.datamodel.FileDataStorageManager;
14+
import com.owncloud.android.lib.common.OwnCloudClient;
15+
import com.owncloud.android.lib.common.network.WebdavUtils;
16+
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
17+
import com.owncloud.android.lib.common.utils.Log_OC;
18+
import com.owncloud.android.operations.common.SyncOperation;
19+
20+
import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
21+
22+
public class RemoveAlbumOperation extends SyncOperation {
23+
private static final String TAG = RemoveAlbumOperation.class.getSimpleName();
24+
private final String albumName;
25+
26+
public RemoveAlbumOperation(String albumName, FileDataStorageManager storageManager) {
27+
super(storageManager);
28+
Log_OC.e(TAG, "Fetch albums remote operation");
29+
this.albumName = albumName;
30+
}
31+
32+
/**
33+
* Performs the operation.
34+
*
35+
* @param client Client object to communicate with the remote ownCloud server.
36+
*/
37+
@Override
38+
protected RemoteOperationResult run(OwnCloudClient client) {
39+
RemoteOperationResult result;
40+
DeleteMethod delete = null;
41+
42+
try {
43+
delete = new DeleteMethod("https://pre1.next.magentacloud.de/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(albumName));
44+
int status = client.executeMethod(delete);
45+
delete.getResponseBodyAsString();
46+
result = new RemoteOperationResult(delete.succeeded() || status == 404, delete);
47+
Log_OC.i(TAG, "Remove " + this.albumName + ": " + result.getLogMessage());
48+
} catch (Exception e) {
49+
result = new RemoteOperationResult(e);
50+
Log_OC.e(TAG, "Remove " + this.albumName + ": " + result.getLogMessage(), e);
51+
} finally {
52+
if (delete != null) {
53+
delete.releaseConnection();
54+
}
55+
56+
}
57+
58+
return result;
59+
}
60+
61+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2021 Tobias Kaminsky <tobias@kaminsky.me>
5+
* SPDX-FileCopyrightText: 2019 Andy Scherzinger <info@andy-scherzinger.de>
6+
* SPDX-FileCopyrightText: 2015 ownCloud Inc.
7+
* SPDX-FileCopyrightText: 2015 María Asensio Valverde <masensio@solidgear.es>
8+
* SPDX-FileCopyrightText: 2014 David A. Velasco <dvelasco@solidgear.es>
9+
* SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only)
10+
*/
11+
package com.owncloud.android.operations.albums;
12+
13+
import com.owncloud.android.datamodel.FileDataStorageManager;
14+
import com.owncloud.android.lib.common.OwnCloudClient;
15+
import com.owncloud.android.lib.common.network.WebdavUtils;
16+
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
17+
import com.owncloud.android.lib.common.utils.Log_OC;
18+
import com.owncloud.android.operations.common.SyncOperation;
19+
20+
import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
21+
22+
public class RenameAlbumOperation extends SyncOperation {
23+
private static final String TAG = RenameAlbumOperation.class.getSimpleName();
24+
private final String oldAlbumName;
25+
private final String newAlbumName;
26+
27+
public RenameAlbumOperation(String oldAlbumName, String newAlbumName, FileDataStorageManager storageManager) {
28+
super(storageManager);
29+
Log_OC.e(TAG, "Fetch albums remote operation");
30+
this.oldAlbumName = oldAlbumName;
31+
this.newAlbumName = newAlbumName;
32+
}
33+
34+
public String getNewAlbumName() {
35+
return newAlbumName;
36+
}
37+
38+
/**
39+
* Performs the operation.
40+
*
41+
* @param client Client object to communicate with the remote ownCloud server.
42+
*/
43+
@Override
44+
protected RemoteOperationResult run(OwnCloudClient client) {
45+
RemoteOperationResult result = null;
46+
MoveMethod move = null;
47+
String url = "https://pre1.next.magentacloud.de/remote.php/dav/photos/" + client.getUserId() + "/albums";
48+
try {
49+
if (!this.newAlbumName.equals(this.oldAlbumName)) {
50+
move = new MoveMethod(url + WebdavUtils.encodePath(oldAlbumName), url + WebdavUtils.encodePath(newAlbumName), true);
51+
client.executeMethod(move);
52+
result = new RemoteOperationResult(move.succeeded(), move);
53+
Log_OC.i(TAG, "Rename " + this.oldAlbumName + " to " + this.newAlbumName + ": " + result.getLogMessage());
54+
client.exhaustResponse(move.getResponseBodyAsStream());
55+
return result;
56+
}
57+
} catch (Exception e) {
58+
result = new RemoteOperationResult(e);
59+
Log_OC.e(TAG, "Rename " + this.oldAlbumName + " to " + this.newAlbumName + ": " + result.getLogMessage(), e);
60+
return result;
61+
} finally {
62+
if (move != null) {
63+
move.releaseConnection();
64+
}
65+
}
66+
67+
return result;
68+
}
69+
70+
}

app/src/main/java/com/owncloud/android/services/OperationsService.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
import com.owncloud.android.operations.UpdateShareViaLinkOperation;
6767
import com.owncloud.android.operations.albums.CopyFileToAlbumOperation;
6868
import com.owncloud.android.operations.albums.CreateNewAlbumOperation;
69+
import com.owncloud.android.operations.albums.RemoveAlbumOperation;
70+
import com.owncloud.android.operations.albums.RenameAlbumOperation;
6971

7072
import java.io.IOException;
7173
import java.util.Optional;
@@ -127,6 +129,8 @@ public class OperationsService extends Service {
127129
public static final String ACTION_CREATE_ALBUM = "CREATE_ALBUM";
128130
public static final String EXTRA_ALBUM_NAME = "ALBUM_NAME";
129131
public static final String ACTION_ALBUM_COPY_FILE = "ALBUM_COPY_FILE";
132+
public static final String ACTION_RENAME_ALBUM = "RENAME_ALBUM";
133+
public static final String ACTION_REMOVE_ALBUM = "REMOVE_ALBUM";
130134

131135
private ServiceHandler mOperationsHandler;
132136
private OperationsServiceBinder mOperationsBinder;
@@ -677,6 +681,12 @@ private Pair<Target, RemoteOperation> newOperation(Intent operationIntent) {
677681
operation = new RenameFileOperation(remotePath, newName, fileDataStorageManager);
678682
break;
679683

684+
case ACTION_RENAME_ALBUM:
685+
remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
686+
String newAlbumName = operationIntent.getStringExtra(EXTRA_NEWNAME);
687+
operation = new RenameAlbumOperation(remotePath, newAlbumName, fileDataStorageManager);
688+
break;
689+
680690
case ACTION_REMOVE:
681691
// Remove file or folder
682692
OCFile file = IntentExtensionsKt.getParcelableArgument(operationIntent, EXTRA_FILE, OCFile.class);
@@ -690,6 +700,12 @@ private Pair<Target, RemoteOperation> newOperation(Intent operationIntent) {
690700
fileDataStorageManager);
691701
break;
692702

703+
case ACTION_REMOVE_ALBUM:
704+
String albumNameToRemove = operationIntent.getStringExtra(EXTRA_ALBUM_NAME);
705+
operation = new RemoveAlbumOperation(albumNameToRemove,
706+
fileDataStorageManager);
707+
break;
708+
693709
case ACTION_CREATE_FOLDER:
694710
remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
695711
operation = new CreateFolderOperation(remotePath,

0 commit comments

Comments
 (0)