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 @@ -598,6 +598,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,117 @@
/*
* 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.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.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
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) {
icon.setImageResource(action.icon)
}
}
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 @@ -42,6 +42,7 @@ fun DrawerActivity.getMenuItemIdFromTitle(): Int? {
getString(R.string.drawer_item_assistant) -> R.id.nav_assistant
getString(R.string.drawer_item_uploads_list) -> R.id.nav_uploads
getString(R.string.drawer_item_trashbin) -> R.id.nav_trashbin
getString(R.string.drawer_item_album) -> R.id.nav_album
else -> {
if (MainApp.isOnlyPersonFiles()) {
R.id.nav_personal_files
Expand Down
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,111 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2025 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.owncloud.android.operations

import android.content.Context
import com.nextcloud.client.account.User
import com.owncloud.android.MainApp
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.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
import com.owncloud.android.lib.resources.files.SearchRemoteOperation
import com.owncloud.android.lib.resources.files.model.RemoteFile
import com.owncloud.android.operations.common.SyncOperation
import com.owncloud.android.utils.FileStorageUtils

/**
* fetch OCFile meta data if not present in local db
*
* @see com.owncloud.android.ui.asynctasks.FetchRemoteFileTask reference for this operation
*
* @param ocFile file for which metadata has to retrieve
* @param removeFileFromDb if you want to remove ocFile from local db to avoid duplicate entries for same fileId
*/
class FetchRemoteFileOperation(
private val context: Context,
private val user: User,
private val ocFile: OCFile,
private val removeFileFromDb: Boolean = false,
storageManager: FileDataStorageManager,
) : SyncOperation(storageManager) {

@Deprecated("Deprecated in Java")
override fun run(client: OwnCloudClient?): RemoteOperationResult<*>? {
val searchRemoteOperation = SearchRemoteOperation(
ocFile.localId.toString(),
SearchRemoteOperation.SearchType.FILE_ID_SEARCH,
false,
storageManager.getCapability(user)
)
val remoteOperationResult: RemoteOperationResult<List<RemoteFile>> =
searchRemoteOperation.execute(user, context)

if (remoteOperationResult.isSuccess && remoteOperationResult.resultData != null) {
if (remoteOperationResult.resultData.isEmpty()) {
Log_OC.e(TAG, "No remote file found with id: ${ocFile.localId}.")
return remoteOperationResult
}
val remotePath = (remoteOperationResult.resultData[0]).remotePath

val operation = ReadFileRemoteOperation(remotePath)
val result = operation.execute(user, context)

if (!result.isSuccess) {
val exception = result.exception
val message =
"Fetching file " + remotePath + " fails with: " + result.getLogMessage(MainApp.getAppContext())
Log_OC.e(TAG, exception?.message ?: message)

return result
}

val remoteFile = result.data[0] as RemoteFile

// remove file from local db
if (removeFileFromDb) {
storageManager.removeFile(ocFile, true, true)
}

var ocFile = FileStorageUtils.fillOCFile(remoteFile)
FileStorageUtils.searchForLocalFileInDefaultPath(ocFile, user.accountName)
ocFile = storageManager.saveFileWithParent(ocFile, context)

// also sync folder content
val toSync: OCFile? = if (ocFile?.isFolder == true) {
ocFile
} else {
ocFile?.parentId?.let { storageManager.getFileById(it) }
}

val currentSyncTime = System.currentTimeMillis()
val refreshFolderOperation: RemoteOperation<Any> = RefreshFolderOperation(
toSync,
currentSyncTime,
true,
true,
storageManager,
user,
context
)
val refreshOperationResult = refreshFolderOperation.execute(user, context)

// set the fetched ocFile to resultData to be handled at ui end
refreshOperationResult.resultData = ocFile

return refreshOperationResult
}
return remoteOperationResult
}

companion object {
private val TAG = FetchRemoteFileOperation::class.java.simpleName
}
}
Loading