Skip to content

Commit 1bd4e7f

Browse files
Merge pull request #16987 from nextcloud/nmc/albumsAddOns
Albums Add Ons
2 parents 36ccb18 + 97c66a6 commit 1bd4e7f

33 files changed

Lines changed: 1184 additions & 129 deletions

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* Nextcloud - Android Client
33
*
4-
* SPDX-FileCopyrightText: 2024 TSI-mc <surinder.kumar@t-systems.com>
4+
* SPDX-FileCopyrightText: 2024-2026 TSI-mc <surinder.kumar@t-systems.com>
55
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
66
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
77
*/
@@ -117,6 +117,7 @@
117117
import com.owncloud.android.ui.fragment.SharedListFragment;
118118
import com.owncloud.android.ui.fragment.UnifiedSearchFragment;
119119
import com.owncloud.android.ui.fragment.albums.AlbumItemsFragment;
120+
import com.owncloud.android.ui.fragment.albums.AlbumSharingBottomSheet;
120121
import com.owncloud.android.ui.fragment.albums.AlbumsFragment;
121122
import com.owncloud.android.ui.fragment.community.CommunityFragment;
122123
import com.owncloud.android.ui.fragment.contactsbackup.BackupFragment;
@@ -532,4 +533,7 @@ abstract class ComponentsModule {
532533

533534
@ContributesAndroidInjector
534535
abstract AlbumItemActionsBottomSheet albumItemActionsBottomSheet();
536+
537+
@ContributesAndroidInjector
538+
abstract AlbumSharingBottomSheet albumSharingBottomSheet();
535539
}

app/src/main/java/com/nextcloud/client/utils/IntentUtil.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Nextcloud - Android Client
33
*
4+
* SPDX-FileCopyrightText: 2026 TSI-mc <surinder.kumar@t-systems.com>
45
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
56
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
67
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
@@ -10,7 +11,10 @@ package com.nextcloud.client.utils
1011
import android.content.Context
1112
import android.content.Intent
1213
import android.net.Uri
14+
import androidx.fragment.app.FragmentActivity
1315
import com.owncloud.android.datamodel.OCFile
16+
import com.owncloud.android.ui.activity.FileDisplayActivity
17+
import com.owncloud.android.ui.dialog.ShareLinkToDialog
1418

1519
object IntentUtil {
1620

@@ -39,4 +43,15 @@ object IntentUtil {
3943

4044
private fun getExposedFileUris(context: Context, files: Array<OCFile>): ArrayList<Uri> =
4145
ArrayList(files.map { it.getExposedFileUri(context) })
46+
47+
fun showShareLinkDialog(activity: FragmentActivity, link: String?) {
48+
val intent = Intent(Intent.ACTION_SEND).apply {
49+
putExtra(Intent.EXTRA_TEXT, link)
50+
setType("text/plain")
51+
}
52+
53+
ShareLinkToDialog.newInstance(intent, activity.packageName).run {
54+
show(activity.supportFragmentManager, FileDisplayActivity.FTAG_CHOOSER_DIALOG)
55+
}
56+
}
4257
}

app/src/main/java/com/nextcloud/ui/albumItemActions/AlbumItemAction.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* Nextcloud - Android Client
33
*
4-
* SPDX-FileCopyrightText: 2025 TSI-mc <surinder.kumar@t-systems.com>
4+
* SPDX-FileCopyrightText: 2025-2026 TSI-mc <surinder.kumar@t-systems.com>
55
* SPDX-License-Identifier: AGPL-3.0-or-later
66
*/
77

@@ -21,6 +21,7 @@ enum class AlbumItemAction(val id: Int, val titleId: Int, val iconId: Int) {
2121
R.drawable.file_image
2222
),
2323
RENAME_ALBUM(R.id.action_rename_file, R.string.album_rename, R.drawable.ic_edit),
24+
SHARE_ALBUM(R.id.action_share_album, R.string.album_share, R.drawable.ic_share),
2425
DELETE_ALBUM(R.id.action_delete, R.string.album_delete, R.drawable.ic_delete);
2526

2627
companion object {
@@ -29,6 +30,7 @@ enum class AlbumItemAction(val id: Int, val titleId: Int, val iconId: Int) {
2930
UPLOAD_FROM_CAMERA_ROLL,
3031
SELECT_IMAGES_FROM_ACCOUNT,
3132
RENAME_ALBUM,
33+
SHARE_ALBUM,
3234
DELETE_ALBUM
3335
)
3436
}

app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Nextcloud - Android Client
33
*
4+
* SPDX-FileCopyrightText: 2026 TSI-mc <surinder.kumar@t-systems.com>
45
* SPDX-FileCopyrightText: 2025 Alper Ozturk <alper.ozturk@nextcloud.com>
56
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
67
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
@@ -13,6 +14,7 @@ import androidx.annotation.IdRes
1314
import androidx.annotation.StringRes
1415
import com.owncloud.android.R
1516
import com.owncloud.android.datamodel.OCFile
17+
import com.owncloud.android.utils.MimeTypeUtil
1618

1719
enum class FileAction(
1820
@param:IdRes val id: Int,
@@ -64,7 +66,10 @@ enum class FileAction(
6466
PIN_TO_HOMESCREEN(R.id.action_pin_to_homescreen, R.string.pin_home, R.drawable.add_to_home_screen),
6567

6668
// Retry for offline operation
67-
RETRY(R.id.action_retry, R.string.retry, R.drawable.ic_retry);
69+
RETRY(R.id.action_retry, R.string.retry, R.drawable.ic_retry),
70+
71+
// Add to Album operation for image and video files
72+
ADD_TO_ALBUM(R.id.action_add_to_album, R.string.add_to_album, R.drawable.ic_album);
6873

6974
constructor(id: Int, title: Int) : this(id, title, null)
7075

@@ -81,6 +86,7 @@ enum class FileAction(
8186
SEE_DETAILS,
8287
LOCK_FILE,
8388
RENAME_FILE,
89+
ADD_TO_ALBUM,
8490
MOVE_OR_COPY,
8591
DOWNLOAD_FILE,
8692
EXPORT_FILE,
@@ -224,6 +230,10 @@ enum class FileAction(
224230
result.add(R.id.action_edit)
225231
}
226232

233+
if (files.any { !MimeTypeUtil.isImageOrVideo(it) }) {
234+
result.add(R.id.action_add_to_album)
235+
}
236+
227237
if (files.any { it.isRecommendedFile }) {
228238
val allowedForRecommended = setOf(
229239
R.id.action_see_details,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
package com.nextcloud.utils.extensions
8+
9+
import androidx.constraintlayout.widget.ConstraintSet
10+
11+
val PARENT_TOP: Pair<Int, Int> = ConstraintSet.PARENT_ID to ConstraintSet.TOP
12+
val PARENT_BOTTOM: Pair<Int, Int> = ConstraintSet.PARENT_ID to ConstraintSet.BOTTOM
13+
val PARENT_START: Pair<Int, Int> = ConstraintSet.PARENT_ID to ConstraintSet.START
14+
val PARENT_END: Pair<Int, Int> = ConstraintSet.PARENT_ID to ConstraintSet.END
15+
16+
fun topOf(viewId: Int): Pair<Int, Int> = viewId to ConstraintSet.TOP
17+
18+
fun bottomOf(viewId: Int): Pair<Int, Int> = viewId to ConstraintSet.BOTTOM
19+
20+
fun startOf(viewId: Int): Pair<Int, Int> = viewId to ConstraintSet.START
21+
22+
fun endOf(viewId: Int): Pair<Int, Int> = viewId to ConstraintSet.END
23+
24+
/**
25+
* Connects the given sides of [viewId] to the supplied anchors, skipping the ones left null.
26+
* An anchor is the target view id paired with the side of that view to attach to, so
27+
* `anchor(id, top = bottomOf(other))` reads as "put the top of id below other".
28+
*/
29+
fun ConstraintSet.anchor(
30+
viewId: Int,
31+
top: Pair<Int, Int>? = null,
32+
bottom: Pair<Int, Int>? = null,
33+
start: Pair<Int, Int>? = null,
34+
end: Pair<Int, Int>? = null
35+
) {
36+
top?.let { connect(viewId, ConstraintSet.TOP, it.first, it.second) }
37+
bottom?.let { connect(viewId, ConstraintSet.BOTTOM, it.first, it.second) }
38+
start?.let { connect(viewId, ConstraintSet.START, it.first, it.second) }
39+
end?.let { connect(viewId, ConstraintSet.END, it.first, it.second) }
40+
}
41+
42+
fun ConstraintSet.fillParent(viewId: Int) =
43+
anchor(viewId, top = PARENT_TOP, bottom = PARENT_BOTTOM, start = PARENT_START, end = PARENT_END)

app/src/main/java/com/owncloud/android/files/FileMenuFilter.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Nextcloud - Android Client
33
*
4+
* SPDX-FileCopyrightText: 2026 TSI-mc <surinder.kumar@t-systems.com>
45
* SPDX-FileCopyrightText: 2023 Alper Ozturk <alper.ozturk@nextcloud.com>
56
* SPDX-FileCopyrightText: 2019-2023 Tobias Kaminsky <tobias@kaminsky.me>
67
* SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas <alvaro@alvarobrey.com>
@@ -170,6 +171,7 @@ private List<Integer> filter(boolean inSingleFileFragment) {
170171
filterUnsetEncrypted(toHide, endToEndEncryptionEnabled);
171172
filterSetPictureAs(toHide);
172173
filterStream(toHide);
174+
filterAddToAlbum(toHide);
173175
filterLock(toHide, fileLockingEnabled);
174176
filterUnlock(toHide, fileLockingEnabled);
175177
filterPinToHome(toHide);
@@ -420,6 +422,17 @@ private void filterStream(List<Integer> toHide) {
420422
}
421423
}
422424

425+
private void filterAddToAlbum(List<Integer> toHide) {
426+
if (files.isEmpty() || containsEncryptedFile()) {
427+
toHide.add(R.id.action_add_to_album);
428+
return;
429+
}
430+
OCFile file = files.iterator().next();
431+
if (!MimeTypeUtil.isImageOrVideo(file)) {
432+
toHide.add(R.id.action_add_to_album);
433+
}
434+
}
435+
423436
private boolean anyFileSynchronizing() {
424437
boolean synchronizing = false;
425438
if (componentsGetter != null && !files.isEmpty() && user != null) {

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Nextcloud - Android Client
33
*
44
* SPDX-FileCopyrightText: 2018-2023 Tobias Kaminsky <tobias@kaminsky.me>
5-
* SPDX-FileCopyrightText: 2021-2025 TSI-mc <surinder.kumar@t-systems.com>
5+
* SPDX-FileCopyrightText: 2021-2026 TSI-mc <surinder.kumar@t-systems.com>
66
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
77
* SPDX-FileCopyrightText: 2017-2018 Andy Scherzinger <info@andy-scherzinger.de>
88
* SPDX-FileCopyrightText: 2015 ownCloud Inc.
@@ -43,6 +43,7 @@
4343
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
4444
import com.owncloud.android.lib.common.utils.Log_OC;
4545
import com.owncloud.android.lib.resources.albums.CreateNewAlbumRemoteOperation;
46+
import com.owncloud.android.lib.resources.albums.PublicShareLinkAlbumRemoteOperation;
4647
import com.owncloud.android.lib.resources.albums.RemoveAlbumRemoteOperation;
4748
import com.owncloud.android.lib.resources.albums.RenameAlbumRemoteOperation;
4849
import com.owncloud.android.lib.resources.files.RestoreFileVersionRemoteOperation;
@@ -110,6 +111,7 @@ public class OperationsService extends Service {
110111
public static final String EXTRA_IN_BACKGROUND = "IN_BACKGROUND";
111112
public static final String EXTRA_FILES_DOWNLOAD_LIMIT = "FILES_DOWNLOAD_LIMIT";
112113
public static final String EXTRA_SHARE_ATTRIBUTES = "SHARE_ATTRIBUTES";
114+
public static final String EXTRA_CREATE_ALBUM_SHARE = "CREATE_ALBUM_SHARE";
113115

114116
public static final String ACTION_CREATE_SHARE_VIA_LINK = "CREATE_SHARE_VIA_LINK";
115117
public static final String ACTION_CREATE_SECURE_FILE_DROP = "CREATE_SECURE_FILE_DROP";
@@ -136,6 +138,7 @@ public class OperationsService extends Service {
136138
public static final String ACTION_ALBUM_COPY_FILE = "ALBUM_COPY_FILE";
137139
public static final String ACTION_RENAME_ALBUM = "RENAME_ALBUM";
138140
public static final String ACTION_REMOVE_ALBUM = "REMOVE_ALBUM";
141+
public static final String ACTION_PUBLIC_SHARE_LINK_ALBUM = "PUBLIC_SHARE_LINK_ALBUM";
139142

140143
private ServiceHandler mOperationsHandler;
141144
private OperationsServiceBinder mOperationsBinder;
@@ -817,6 +820,12 @@ private Pair<Target, RemoteOperation> newOperation(Intent operationIntent) {
817820
operation = new RemoveAlbumRemoteOperation(albumNameToRemove);
818821
break;
819822

823+
case ACTION_PUBLIC_SHARE_LINK_ALBUM:
824+
String albmName = operationIntent.getStringExtra(EXTRA_ALBUM_NAME);
825+
boolean isCreateShare = operationIntent.getBooleanExtra(EXTRA_CREATE_ALBUM_SHARE, false);
826+
operation = new PublicShareLinkAlbumRemoteOperation(albmName, isCreateShare);
827+
break;
828+
820829
default:
821830
// do nothing
822831
break;

app/src/main/java/com/owncloud/android/ui/activity/AlbumsPickerActivity.kt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class AlbumsPickerActivity :
4343

4444
private lateinit var folderPickerBinding: FilesFolderPickerBinding
4545

46+
private var targetFilePaths: ArrayList<String>? = null
47+
private var albumName: String? = null
48+
4649
private fun initBinding() {
4750
folderPickerBinding = FilesFolderPickerBinding.inflate(layoutInflater)
4851
setContentView(folderPickerBinding.root)
@@ -57,6 +60,7 @@ class AlbumsPickerActivity :
5760
setupToolbar()
5861
setupAction()
5962
setupActionBar()
63+
initExtras()
6064

6165
if (savedInstanceState == null) {
6266
createFragments()
@@ -79,6 +83,11 @@ class AlbumsPickerActivity :
7983
}
8084
}
8185

86+
private fun initExtras() {
87+
targetFilePaths = intent.getStringArrayListExtra(EXTRA_FILE_PATHS)
88+
albumName = intent.getStringExtra(EXTRA_ALBUM_NAME)
89+
}
90+
8291
private fun setupAction() {
8392
action = intent.getStringExtra(EXTRA_ACTION)
8493
setupUIForChooseButton()
@@ -180,6 +189,19 @@ class AlbumsPickerActivity :
180189
}
181190
}
182191

192+
fun addFilesToAlbum(albumName: String?) {
193+
targetFilePaths?.let {
194+
fileOperationsHelper.albumCopyFiles(it, albumName)
195+
}
196+
}
197+
198+
fun addFilesToAlbum(files: Collection<OCFile>) {
199+
val paths: List<String> = files.map { it.remotePath }
200+
albumName?.let {
201+
fileOperationsHelper.albumCopyFiles(paths, it)
202+
}
203+
}
204+
183205
override fun showDetails(file: OCFile?) = Unit
184206

185207
override fun showDetails(file: OCFile?, activeTab: Int) = Unit
@@ -192,19 +214,22 @@ class AlbumsPickerActivity :
192214
private val EXTRA_ACTION = AlbumsPickerActivity::class.java.canonicalName?.plus(".EXTRA_ACTION")
193215
private val CHOOSE_ALBUM = AlbumsPickerActivity::class.java.canonicalName?.plus(".CHOOSE_ALBUM")
194216
private val CHOOSE_MEDIA_FILES = AlbumsPickerActivity::class.java.canonicalName?.plus(".CHOOSE_MEDIA_FILES")
217+
private val EXTRA_FILE_PATHS = FolderPickerActivity::class.java.canonicalName?.plus(".EXTRA_FILE_PATHS")
218+
private val EXTRA_ALBUM_NAME = FolderPickerActivity::class.java.canonicalName?.plus(".EXTRA_ALBUM_NAME")
195219
val EXTRA_FROM_ALBUM = AlbumsPickerActivity::class.java.canonicalName?.plus(".EXTRA_FROM_ALBUM")
196-
val EXTRA_MEDIA_FILES_PATH = AlbumsPickerActivity::class.java.canonicalName?.plus(".EXTRA_MEDIA_FILES_PATH")
197220

198221
private val TAG = AlbumsPickerActivity::class.java.simpleName
199222

200-
fun intentForPickingAlbum(context: FragmentActivity): Intent =
223+
fun intentForPickingAlbum(context: FragmentActivity, paths: ArrayList<String>): Intent =
201224
Intent(context, AlbumsPickerActivity::class.java).apply {
202225
putExtra(EXTRA_ACTION, CHOOSE_ALBUM)
226+
putStringArrayListExtra(EXTRA_FILE_PATHS, paths)
203227
}
204228

205-
fun intentForPickingMediaFiles(context: FragmentActivity): Intent =
229+
fun intentForPickingMediaFiles(context: FragmentActivity, albumName: String): Intent =
206230
Intent(context, AlbumsPickerActivity::class.java).apply {
207231
putExtra(EXTRA_ACTION, CHOOSE_MEDIA_FILES)
232+
putExtra(EXTRA_ALBUM_NAME, albumName)
208233
}
209234
}
210235

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperation
101101
import com.owncloud.android.lib.common.operations.RemoteOperationResult
102102
import com.owncloud.android.lib.common.utils.Log_OC
103103
import com.owncloud.android.lib.resources.albums.CreateNewAlbumRemoteOperation
104+
import com.owncloud.android.lib.resources.albums.PublicShareLinkAlbumRemoteOperation
104105
import com.owncloud.android.lib.resources.albums.RemoveAlbumRemoteOperation
105106
import com.owncloud.android.lib.resources.albums.RenameAlbumRemoteOperation
106107
import com.owncloud.android.lib.resources.files.RestoreFileVersionRemoteOperation
@@ -2179,6 +2180,10 @@ class FileDisplayActivity :
21792180
is RemoveAlbumRemoteOperation -> {
21802181
albumOperationListener.onRemoveAlbumOperationFinish(operation, result)
21812182
}
2183+
2184+
is PublicShareLinkAlbumRemoteOperation -> {
2185+
albumOperationListener.onAlbumPublicLinkOperationFinish(operation, result)
2186+
}
21822187
}
21832188
}
21842189

app/src/main/java/com/owncloud/android/ui/adapter/albums/AlbumsAdapter.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ class AlbumsAdapter(
6363
DisplayUtils.getDateByPattern(file.createdDate, DateFormatPattern.MonthWithYear.pattern)
6464
)
6565

66+
gridViewHolder.albumName.setCompoundDrawablesWithIntrinsicBounds(
67+
0,
68+
0,
69+
if (file.collaborators.isNotEmpty()) R.drawable.ic_share else 0,
70+
0
71+
)
72+
6673
if (file.lastPhoto > 0) {
6774
var ocLocal = storageManager?.getFileByLocalId(file.lastPhoto)
6875
if (ocLocal == null) {

0 commit comments

Comments
 (0)