Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,9 @@
android:launchMode="singleTop"
android:theme="@style/Theme.ownCloud.Dialog.NoTitle"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ui.activity.AlbumsPickerActivity"
android:exported="false" />
<activity
android:name=".ui.activity.ShareActivity"
android:exported="false"
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/com/nextcloud/client/di/ComponentsModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.nextcloud.ui.ChooseStorageLocationDialogFragment;
import com.nextcloud.ui.ImageDetailFragment;
import com.nextcloud.ui.SetStatusDialogFragment;
import com.nextcloud.ui.albumItemActions.AlbumItemActionsBottomSheet;
import com.nextcloud.ui.composeActivity.ComposeActivity;
import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet;
Expand Down Expand Up @@ -81,6 +82,7 @@
import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
import com.owncloud.android.ui.dialog.CreateAlbumDialogFragment;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
Expand Down Expand Up @@ -114,6 +116,9 @@
import com.owncloud.android.ui.fragment.OCFileListFragment;
import com.owncloud.android.ui.fragment.SharedListFragment;
import com.owncloud.android.ui.fragment.UnifiedSearchFragment;
import com.owncloud.android.ui.fragment.albums.AlbumItemsFragment;
import com.owncloud.android.ui.fragment.albums.AlbumsFragment;
import com.owncloud.android.ui.activity.AlbumsPickerActivity;
import com.owncloud.android.ui.fragment.contactsbackup.BackupFragment;
import com.owncloud.android.ui.fragment.contactsbackup.BackupListFragment;
import com.owncloud.android.ui.preview.FileDownloadFragment;
Expand Down Expand Up @@ -505,4 +510,19 @@ abstract class ComponentsModule {

@ContributesAndroidInjector
abstract TermsOfServiceDialog termsOfServiceDialog();

@ContributesAndroidInjector
abstract AlbumsPickerActivity albumsPickerActivity();

@ContributesAndroidInjector
abstract CreateAlbumDialogFragment createAlbumDialogFragment();

@ContributesAndroidInjector
abstract AlbumsFragment albumsFragment();

@ContributesAndroidInjector
abstract AlbumItemsFragment albumItemsFragment();

@ContributesAndroidInjector
abstract AlbumItemActionsBottomSheet albumItemActionsBottomSheet();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2025 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.ui.albumItemActions

import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import com.owncloud.android.R

enum class AlbumItemAction(@IdRes val id: Int, @StringRes val title: Int, @DrawableRes val icon: Int? = null) {
RENAME_ALBUM(R.id.action_rename_file, R.string.album_rename, R.drawable.ic_edit),
DELETE_ALBUM(R.id.action_delete, R.string.album_delete, R.drawable.ic_delete);

companion object {
/**
* All file actions, in the order they should be displayed
*/
@JvmField
val SORTED_VALUES = listOf(
RENAME_ALBUM,
DELETE_ALBUM
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2025 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.ui.albumItemActions

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.IdRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.os.bundleOf
import androidx.core.view.isEmpty
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.LifecycleOwner
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.nextcloud.android.common.ui.theme.utils.ColorRole
import com.nextcloud.client.di.Injectable
import com.owncloud.android.databinding.FileActionsBottomSheetBinding
import com.owncloud.android.databinding.FileActionsBottomSheetItemBinding
import com.owncloud.android.utils.theme.ViewThemeUtils
import javax.inject.Inject

class AlbumItemActionsBottomSheet : BottomSheetDialogFragment(), Injectable {

@Inject
lateinit var viewThemeUtils: ViewThemeUtils

private var _binding: FileActionsBottomSheetBinding? = null
val binding
get() = _binding!!

fun interface ResultListener {
fun onResult(@IdRes actionId: Int)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FileActionsBottomSheetBinding.inflate(inflater, container, false)

val bottomSheetDialog = dialog as BottomSheetDialog
bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
bottomSheetDialog.behavior.skipCollapsed = true

viewThemeUtils.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE)

return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.bottomSheetHeader.visibility = View.GONE
binding.bottomSheetLoading.visibility = View.GONE
displayActions()
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

fun setResultListener(
fragmentManager: FragmentManager,
lifecycleOwner: LifecycleOwner,
listener: ResultListener
): AlbumItemActionsBottomSheet {
fragmentManager.setFragmentResultListener(REQUEST_KEY, lifecycleOwner) { _, result ->
@IdRes val actionId = result.getInt(RESULT_KEY_ACTION_ID, -1)
if (actionId != -1) {
listener.onResult(actionId)
}
}
return this
}

private fun displayActions() {
if (binding.fileActionsList.isEmpty()) {
AlbumItemAction.SORTED_VALUES.forEach { action ->
val view = inflateActionView(action)
binding.fileActionsList.addView(view)
}
}
}

private fun inflateActionView(action: AlbumItemAction): View {
val itemBinding = FileActionsBottomSheetItemBinding.inflate(layoutInflater, binding.fileActionsList, false)
.apply {
root.setOnClickListener {
dispatchActionClick(action.id)
}
text.setText(action.title)
if (action.icon != null) {
val drawable =
viewThemeUtils.platform.tintDrawable(
requireContext(),
AppCompatResources.getDrawable(requireContext(), action.icon)!!
)
icon.setImageDrawable(drawable)
}
}
return itemBinding.root
}

private fun dispatchActionClick(id: Int?) {
if (id != null) {
setFragmentResult(REQUEST_KEY, bundleOf(RESULT_KEY_ACTION_ID to id))
parentFragmentManager.clearFragmentResultListener(REQUEST_KEY)
dismiss()
}
}

companion object {
private const val REQUEST_KEY = "REQUEST_KEY_ACTION"
private const val RESULT_KEY_ACTION_ID = "RESULT_KEY_ACTION_ID"

@JvmStatic
fun newInstance(): AlbumItemActionsBottomSheet {
return AlbumItemActionsBottomSheet()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
* Type for virtual folders
*/
public enum VirtualFolderType {
FAVORITE, GALLERY, NONE
FAVORITE, GALLERY, ALBUM, NONE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2025 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.operations.albums

import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.owncloud.android.operations.UploadFileOperation
import com.owncloud.android.operations.common.SyncOperation

/**
* Constructor
*
* @param srcPath Remote path of the [OCFile] to move.
* @param targetParentPath Path to the folder where the file will be copied into.
*/
class CopyFileToAlbumOperation(
private val srcPath: String,
private var targetParentPath: String,
storageManager: FileDataStorageManager
) :
SyncOperation(storageManager) {
init {
if (!targetParentPath.endsWith(OCFile.PATH_SEPARATOR)) {
this.targetParentPath += OCFile.PATH_SEPARATOR
}
}

/**
* Performs the operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Deprecated("Deprecated in Java")
@Suppress("NestedBlockDepth")
override fun run(client: OwnCloudClient): RemoteOperationResult<Any> {
/** 1. check copy validity */
val result: RemoteOperationResult<Any>

if (targetParentPath.startsWith(srcPath)) {
result = RemoteOperationResult<Any>(ResultCode.INVALID_COPY_INTO_DESCENDANT)
} else {
val file = storageManager.getFileByPath(srcPath)
if (file == null) {
result = RemoteOperationResult(ResultCode.FILE_NOT_FOUND)
} else {
/** 2. remote copy */
var targetPath = "$targetParentPath${file.fileName}"
if (file.isFolder) {
targetPath += OCFile.PATH_SEPARATOR
}

// auto rename, to allow copy
if (targetPath == srcPath) {
if (file.isFolder) {
targetPath = "$targetParentPath${file.fileName}"
}
targetPath = UploadFileOperation.getNewAvailableRemotePath(client, targetPath, null, false)

if (file.isFolder) {
targetPath += OCFile.PATH_SEPARATOR
}
}

result = CopyFileToAlbumRemoteOperation(srcPath, targetPath).execute(client)
}
}
return result
}
}
Loading