[Android] Allow renaming a file or a folder
Nicolas Pomepuy
git at videolan.org
Tue Jan 9 15:39:34 UTC 2024
vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Mon Jan 8 10:50:03 2024 +0100| [63ee26448b8076532d56f1ed05f4609014adb72d] | committer: Duncan McNamara
Allow renaming a file or a folder
Fixes #3018
> https://code.videolan.org/videolan/vlc-android/commit/63ee26448b8076532d56f1ed05f4609014adb72d
---
.../vlc/gui/browser/BaseBrowserFragment.kt | 66 ++++++++++++++++++++--
.../org/videolan/vlc/gui/dialogs/RenameDialog.kt | 16 ++++--
2 files changed, 73 insertions(+), 9 deletions(-)
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt
index dea76bb518..e17c522cd5 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt
@@ -26,7 +26,11 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
-import android.view.*
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
@@ -42,18 +46,49 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.Runnable
+import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import org.videolan.libvlc.MediaPlayer
import org.videolan.libvlc.interfaces.IMedia
import org.videolan.medialibrary.MLServiceLocator
import org.videolan.medialibrary.interfaces.media.MediaWrapper
import org.videolan.medialibrary.media.MediaLibraryItem
-import org.videolan.resources.*
+import org.videolan.resources.AndroidDevices
+import org.videolan.resources.CTX_ADD_FOLDER_AND_SUB_PLAYLIST
+import org.videolan.resources.CTX_ADD_FOLDER_PLAYLIST
+import org.videolan.resources.CTX_ADD_SCANNED
+import org.videolan.resources.CTX_ADD_TO_PLAYLIST
+import org.videolan.resources.CTX_APPEND
+import org.videolan.resources.CTX_DELETE
+import org.videolan.resources.CTX_DOWNLOAD_SUBTITLES
+import org.videolan.resources.CTX_FAV_ADD
+import org.videolan.resources.CTX_FAV_REMOVE
+import org.videolan.resources.CTX_FIND_METADATA
+import org.videolan.resources.CTX_INFORMATION
+import org.videolan.resources.CTX_PLAY
+import org.videolan.resources.CTX_PLAY_ALL
+import org.videolan.resources.CTX_PLAY_AS_AUDIO
+import org.videolan.resources.CTX_RENAME
+import org.videolan.resources.KEY_MRL
+import org.videolan.resources.MOVIEPEDIA_ACTIVITY
+import org.videolan.resources.MOVIEPEDIA_MEDIA
import org.videolan.resources.util.getFromMl
import org.videolan.resources.util.parcelable
-import org.videolan.tools.*
+import org.videolan.tools.BROWSER_SHOW_HIDDEN_FILES
+import org.videolan.tools.FORCE_PLAY_ALL_AUDIO
+import org.videolan.tools.FORCE_PLAY_ALL_VIDEO
+import org.videolan.tools.MultiSelectHelper
+import org.videolan.tools.Settings
+import org.videolan.tools.isStarted
+import org.videolan.tools.putSingle
+import org.videolan.tools.removeFileScheme
import org.videolan.vlc.BuildConfig
import org.videolan.vlc.PlaybackService
import org.videolan.vlc.R
@@ -61,6 +96,7 @@ import org.videolan.vlc.databinding.DirectoryBrowserBinding
import org.videolan.vlc.gui.AudioPlayerContainerActivity
import org.videolan.vlc.gui.dialogs.ConfirmDeleteDialog
import org.videolan.vlc.gui.dialogs.CtxActionReceiver
+import org.videolan.vlc.gui.dialogs.RenameDialog
import org.videolan.vlc.gui.dialogs.SavePlaylistDialog
import org.videolan.vlc.gui.dialogs.showContext
import org.videolan.vlc.gui.helpers.MedialibraryUtils
@@ -82,7 +118,8 @@ import org.videolan.vlc.util.SchedulerCallback
import org.videolan.vlc.util.isSchemeSupported
import org.videolan.vlc.util.isTalkbackIsEnabled
import org.videolan.vlc.viewmodels.browser.BrowserModel
-import java.util.*
+import java.io.File
+import java.util.LinkedList
private const val TAG = "VLC/BaseBrowserFragment"
@@ -583,6 +620,7 @@ abstract class BaseBrowserFragment : MediaBrowserFragment<BrowserModel>(), IRefr
if (viewModel.provider.hasSubfolders(mw)) flags = flags or CTX_ADD_FOLDER_AND_SUB_PLAYLIST
flags = flags or CTX_APPEND
}
+ flags = flags or CTX_RENAME
} else {
val isVideo = mw.type == MediaWrapper.TYPE_VIDEO
val isAudio = mw.type == MediaWrapper.TYPE_AUDIO
@@ -591,6 +629,7 @@ abstract class BaseBrowserFragment : MediaBrowserFragment<BrowserModel>(), IRefr
if (!isAudio && isMedia) flags = flags or CTX_PLAY_AS_AUDIO
if (!isMedia) flags = flags or CTX_PLAY
if (isVideo) flags = flags or CTX_DOWNLOAD_SUBTITLES
+ flags = flags or CTX_RENAME
}
if (flags != 0L) showContext(requireActivity(), this at BaseBrowserFragment, position, item, flags)
}
@@ -624,6 +663,23 @@ abstract class BaseBrowserFragment : MediaBrowserFragment<BrowserModel>(), IRefr
MediaUtils.appendMedia(activity, getMediaWithMeta(mw))
}
CTX_DELETE -> removeItem(mw)
+ CTX_RENAME -> {
+ val dialog = RenameDialog.newInstance(mw, true)
+ dialog.show(requireActivity().supportFragmentManager, RenameDialog::class.simpleName)
+ dialog.setListener { item, name ->
+ lifecycleScope.launch(Dispatchers.IO) {
+ (item as MediaWrapper).uri.path?.let { File(it) }?.let { file ->
+ if (file.exists()) {
+ file.parent?.let {
+ val newFile = File("$it/$name")
+ file.renameTo(newFile)
+ }
+ }
+ }
+ viewModel.refresh()
+ }
+ }
+ }
CTX_INFORMATION -> requireActivity().showMediaInfo(mw)
CTX_PLAY_AS_AUDIO -> lifecycleScope.launch {
mw.addFlags(MediaWrapper.MEDIA_FORCE_AUDIO)
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/RenameDialog.kt b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/RenameDialog.kt
index b62fae3de0..b17bbc0834 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/RenameDialog.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/RenameDialog.kt
@@ -63,6 +63,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import org.videolan.medialibrary.interfaces.media.MediaWrapper
import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.resources.util.parcelable
import org.videolan.vlc.R
@@ -70,6 +71,7 @@ import org.videolan.vlc.gui.helpers.UiTools
import org.videolan.vlc.gui.helpers.UiTools.showPinIfNeeded
const val RENAME_DIALOG_MEDIA = "RENAME_DIALOG_MEDIA"
+const val RENAME_DIALOG_FILE = "RENAME_DIALOG_FILE"
class RenameDialog : VLCBottomSheetDialogFragment() {
@@ -77,13 +79,14 @@ class RenameDialog : VLCBottomSheetDialogFragment() {
private lateinit var renameButton: Button
private lateinit var newNameInputtext: TextInputEditText
private lateinit var media: MediaLibraryItem
+ private var renameFile: Boolean = false
companion object {
- fun newInstance(media: MediaLibraryItem): RenameDialog {
+ fun newInstance(media: MediaLibraryItem, isFile:Boolean = false): RenameDialog {
return RenameDialog().apply {
- arguments = bundleOf(RENAME_DIALOG_MEDIA to media)
+ arguments = bundleOf(RENAME_DIALOG_MEDIA to media, RENAME_DIALOG_FILE to isFile)
}
}
}
@@ -95,16 +98,21 @@ class RenameDialog : VLCBottomSheetDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch { if (requireActivity().showPinIfNeeded()) dismiss() }
media = arguments?.parcelable(RENAME_DIALOG_MEDIA) ?: return
+ renameFile = arguments?.getBoolean(RENAME_DIALOG_FILE) ?: false
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.dialog_rename, container)
- val name = media.title
+ val name = if (renameFile && media is MediaWrapper) (media as MediaWrapper).fileName else media.title
newNameInputtext = view.findViewById(R.id.new_name)
renameButton = view.findViewById(R.id.rename_button)
- if (media.title.isNotEmpty()) newNameInputtext.setText(media.title)
+ if (media.title.isNotEmpty()) {
+ newNameInputtext.setText(name)
+ }
+ val extIndex = name.indexOfLast { it == '.' }
+ if (extIndex != -1 && renameFile) newNameInputtext.setSelection(0, extIndex) else newNameInputtext.setSelection(0, name.length)
renameButton.setOnClickListener {
performRename()
}
More information about the Android
mailing list