diff --git a/components/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerFragment.kt b/components/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerFragment.kt index 0d7e0a5..b716c15 100644 --- a/components/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerFragment.kt +++ b/components/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerFragment.kt @@ -7,11 +7,13 @@ import android.graphics.drawable.ColorDrawable import android.os.Build import android.os.Bundle import android.util.TypedValue +import android.view.Gravity import android.view.KeyEvent import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.WindowManager +import android.widget.PopupMenu import android.widget.ScrollView import android.widget.TextView import android.widget.Toast @@ -43,12 +45,15 @@ import dev.arkbuilders.components.databinding.ArkFilePickerDialogNewFolderBindin import dev.arkbuilders.components.databinding.ArkFilePickerHostFragmentBinding import dev.arkbuilders.components.databinding.ArkFilePickerItemFileBinding import dev.arkbuilders.components.databinding.ArkFilePickerItemFilesRootsPageBinding +import dev.arkbuilders.components.filepicker.callback.OnFileItemLongClick +import dev.arkbuilders.components.filepicker.callback.PinFileCallback import dev.arkbuilders.components.folderstree.FolderTreeView import dev.arkbuilders.components.utils.args import dev.arkbuilders.components.utils.dpToPx import dev.arkbuilders.components.utils.formatSize import dev.arkbuilders.components.utils.iconForExtension import dev.arkbuilders.components.utils.setDragSensitivity +import dev.arkbuilders.components.utils.toast import java.io.File import java.lang.Exception import java.nio.file.Path @@ -87,6 +92,13 @@ open class ArkFilePickerFragment : } private val pagesAdapter = ItemAdapter() + private var mCurrentRootsWithFavorites: Map>? = null + + private val mPinFileCallback = object : PinFileCallback { + override fun onPinFileClick(file: Path) { + pinFile(file) + } + } open fun onFolderChanged(currentFolder: Path) {} open fun onPick(pickedPath: Path) {} @@ -162,6 +174,10 @@ open class ArkFilePickerFragment : } } + private fun isARKMode(): Boolean { + return binding.tabs.selectedTabPosition == 1 + } + private fun showCreateFolderDialog() { val builder = AlertDialog.Builder(activity, android.R.style.ThemeOverlay_Material_Dialog_Alert) builder.setTitle(R.string.ark_file_picker_new_folder) @@ -180,6 +196,9 @@ open class ArkFilePickerFragment : } if (newFolder.mkdirs()) { + if (isARKMode()) { + pinFile(newFolder.toPath()) + } //Reload current files tree currentFolder?.let { viewModel.onItemClick(it) } dialog.dismiss() @@ -189,6 +208,8 @@ open class ArkFilePickerFragment : } private fun render(state: FilePickerState) = binding.apply { + mCurrentRootsWithFavorites = state.rootsWithFavs + displayPath(state) val deviceText = if (state.currentDevice == INTERNAL_STORAGE) @@ -325,7 +346,8 @@ open class ArkFilePickerFragment : ) } else { listOf( - FilesPage(this, viewModel, itemsPluralId!!), + FilesPage(this, viewModel, itemsPluralId!!, + pinFileCallback = mPinFileCallback), RootsPage(this, viewModel) ) } @@ -363,6 +385,43 @@ open class ArkFilePickerFragment : private const val DIALOG_WIDTH = 300f private const val PATH_PART_PADDING = 4f } + + private fun pinFile(file: Path) { + val roots = mCurrentRootsWithFavorites?.keys + val root = roots?.find { root -> file.startsWith(root) } + val favorites = mCurrentRootsWithFavorites?.get(root)?.flatten() + + root?.let { + + //Make sure file isn't inside a root folder + if (root != file) { + var foundAsFavorite = false + favorites?.forEach { + if (file.endsWith(it)) { + foundAsFavorite = true + return@forEach + } + } + + if (!foundAsFavorite) { + viewModel.addFavorite(file) + activity?.toast(R.string.ark_file_picker_pinned_as_favorite) + } else { + activity?.toast(R.string.ark_file_picker_already_a_favorite) + } + } else { + activity?.toast(R.string.ark_file_picker_already_be_a_root) + } + } ?: let { + + if (mCurrentRootsWithFavorites?.keys?.contains(file) == true) { + activity?.toast(R.string.ark_file_picker_already_be_a_root) + } else { + viewModel.addRoot(file) + activity?.toast(R.string.ark_file_picker_pinned_as_root) + } + } + } } private fun pickerImageLoader(ctx: Context) = ImageLoader.Builder(ctx) @@ -386,7 +445,8 @@ private fun pickerImageLoader(ctx: Context) = ImageLoader.Builder(ctx) internal class FilesPage( private val fragment: Fragment, private val viewModel: ArkFilePickerViewModel, - private val itemsPluralId: Int + private val itemsPluralId: Int, + private val pinFileCallback: PinFileCallback? = null ) : AbstractBindingItem() { private val filesAdapter = ItemAdapter() private var currentFiles = emptyList() @@ -399,10 +459,13 @@ internal class FilesPage( parent: ViewGroup? ) = ArkFilePickerItemFilesRootsPageBinding.inflate(inflater, parent, false) + private var mBinding: ArkFilePickerItemFilesRootsPageBinding? = null + override fun bindView( binding: ArkFilePickerItemFilesRootsPageBinding, payloads: List ) = with(binding) { + mBinding = binding rvFiles.adapter = FastAdapter.with(filesAdapter) viewModel.observe(fragment, state = ::render) } @@ -411,7 +474,29 @@ internal class FilesPage( if (currentFiles == state.files) return filesAdapter.setNewList(state.files.map { - FileItem(it, viewModel, itemsPluralId, imageLoader) + FileItem(it, viewModel, itemsPluralId, imageLoader, + onLongClick = object : OnFileItemLongClick { + override fun onLongClick(file: Path) { + val currentItem = filesAdapter.itemList.items.firstOrNull { item -> + item.getFile().toString() == file.toString() + } + currentItem?.let { + val anchorView = mBinding?.rvFiles?.findViewHolderForAdapterPosition( + filesAdapter.getAdapterPosition(currentItem))?.itemView + anchorView?.let { anchor -> + val popupMenu = PopupMenu(fragment.activity, anchor, Gravity.END) + popupMenu.menuInflater.inflate(R.menu.file_select_menu, popupMenu.menu) + popupMenu.setOnMenuItemClickListener { + pinFileCallback?.onPinFileClick(file) + true + } + popupMenu.show() + } + + + } ?: let { return@let } + } + }) }) currentFiles = state.files @@ -423,6 +508,7 @@ internal class FileItem( private val viewModel: ArkFilePickerViewModel, private val itemsPluralId: Int, private val imageLoader: ImageLoader, + private val onLongClick: OnFileItemLongClick? = null ) : AbstractBindingItem() { override val type = 0 @@ -438,6 +524,11 @@ internal class FileItem( root.setOnClickListener { viewModel.onItemClick(file) } + + root.setOnLongClickListener { + onLongClick?.onLongClick(file) + true + } binding.tvName.text = file.name if (file.isDirectory()) bindFolder(file, this) else bindRegularFile(file, this) @@ -474,6 +565,10 @@ internal class FileItem( binding.iv.dispose() binding.iv.setImageResource(R.drawable.ark_file_picker_ic_folder) } + + fun getFile(): Path { + return file + } } internal class RootsPage( diff --git a/components/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt b/components/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt index 564f134..77a8cbc 100644 --- a/components/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt +++ b/components/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt @@ -49,11 +49,39 @@ internal class ArkFilePickerViewModel( init { viewModelScope.launch { - val rootsWithFavs = foldersRepo.provideFolders() - intent { - reduce { - state.copy(rootsWithFavs = rootsWithFavs) - } + refreshRootsWithFavs() + } + } + + fun addRoot(root:Path) { + viewModelScope.launch { + foldersRepo.addRoot(root) + refreshRootsWithFavs() + } + } + + fun addFavorite(favorite: Path) { + viewModelScope.launch { + val favoritePath = favorite.toRealPath() + val folders = foldersRepo.provideFolders() + val root = folders.keys.find { favoritePath.startsWith(it) } + ?: throw IllegalStateException( + "Can't add favorite if it's root is not added" + ) + val favoriteRelativePath = root.relativize(favoritePath) + if (folders[root]?.contains(favoriteRelativePath) == true) { + throw AssertionError("Path must be checked in RootPicker") + } + foldersRepo.addFavorite(root, favoriteRelativePath) + refreshRootsWithFavs() + } + } + + private suspend fun refreshRootsWithFavs() { + val rootsWithFavs = foldersRepo.provideFolders() + intent { + reduce { + state.copy(rootsWithFavs = rootsWithFavs) } } } @@ -77,7 +105,9 @@ internal class ArkFilePickerViewModel( onPathPicked(path) } - fun onPickBtnClick() = intent { onPathPicked(state.currentPath) } + fun onPickBtnClick() = intent { + onPathPicked(state.currentPath) + } fun onDeviceSelected(selectedDevicePos: Int) = intent { val selectedDevice = state.devices[selectedDevicePos] diff --git a/components/src/main/java/dev/arkbuilders/components/filepicker/callback/OnFileItemLongClick.kt b/components/src/main/java/dev/arkbuilders/components/filepicker/callback/OnFileItemLongClick.kt new file mode 100644 index 0000000..de35009 --- /dev/null +++ b/components/src/main/java/dev/arkbuilders/components/filepicker/callback/OnFileItemLongClick.kt @@ -0,0 +1,7 @@ +package dev.arkbuilders.components.filepicker.callback + +import java.nio.file.Path + +interface OnFileItemLongClick { + fun onLongClick(file: Path) +} \ No newline at end of file diff --git a/components/src/main/java/dev/arkbuilders/components/filepicker/callback/PinFileCallback.kt b/components/src/main/java/dev/arkbuilders/components/filepicker/callback/PinFileCallback.kt new file mode 100644 index 0000000..4b18fbe --- /dev/null +++ b/components/src/main/java/dev/arkbuilders/components/filepicker/callback/PinFileCallback.kt @@ -0,0 +1,7 @@ +package dev.arkbuilders.components.filepicker.callback + +import java.nio.file.Path + +interface PinFileCallback { + fun onPinFileClick(file: Path) +} \ No newline at end of file diff --git a/components/src/main/res/menu/file_select_menu.xml b/components/src/main/res/menu/file_select_menu.xml new file mode 100644 index 0000000..632912c --- /dev/null +++ b/components/src/main/res/menu/file_select_menu.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/components/src/main/res/values/strings.xml b/components/src/main/res/values/strings.xml index 68ec484..146f7ad 100644 --- a/components/src/main/res/values/strings.xml +++ b/components/src/main/res/values/strings.xml @@ -17,6 +17,11 @@ New folder Folder already exists! Name your folder + Pinned as a Favorite folder! + Pinned as a Root folder! + Already be a Favorite folder! + Already be a Root folder! + Pin %d item %d items