Skip to content
Snippets Groups Projects
Commit c8c52d52 authored by tda1009's avatar tda1009 Committed by Nicolas Pomepuy
Browse files

Implement Duplication Warning Dialog

Fixes #1363
parent e61280bb
No related branches found
No related tags found
No related merge requests found
......@@ -806,4 +806,18 @@
<string name="no_bookmark">No bookmark yet</string>
<string name="bookmark_name">Bookmark %s</string>
<!-- Duplication Warning Dialog -->
<string name="message_primary_default">Add duplicated item(s)?</string>
<string name="add_button">Add</string>
<string name="add_all_button">Add all</string>
<string name="add_new_only_button">Add new only</string>
<plurals name="duplication_two_options_secondary">
<item quantity="one">This item is already in this playlist.</item>
<item quantity="other">These items are already in this playlist.</item>
</plurals>
<plurals name="duplication_three_options_secondary">
<item quantity="one">1 of the selected items is already in this playlist.</item>
<item quantity="other">%d of the selected items are already in this playlist.</item>
</plurals>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/myTestRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/primary_textview"
style="@style/Theme.VLC.BottomSheetTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:text="@string/message_primary_default"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/secondary_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/primary_textview"
tools:text="This item is already in this playlist." />
<Button
android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@string/cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/add_new_button"
app:layout_constraintTop_toTopOf="@+id/add_all_button" />
<Button
android:id="@+id/add_new_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@string/add_new_only_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/add_all_button"
app:layout_constraintTop_toTopOf="@+id/add_all_button" />
<Button
android:id="@+id/add_all_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginEnd="8dp"
android:text="@string/add_all_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/secondary_textview" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
package org.videolan.vlc.gui.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.fragment.app.setFragmentResult
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
import org.videolan.vlc.R
import org.videolan.vlc.databinding.DialogDuplicationWarningBinding
class DuplicationWarningDialog : VLCBottomSheetDialogFragment(), View.OnClickListener {
override fun getDefaultState(): Int = STATE_EXPANDED
override fun needToManageOrientation(): Boolean = false
private lateinit var binding: DialogDuplicationWarningBinding
private var duplicatesCount: Int = 0
private var highlightsCount: Int = 0
override fun initialFocusedView(): View {
return if (shouldShowThreeOptions())
binding.addNewButton
else
binding.cancelButton
}
override fun onCreate(savedInstanceState: Bundle?) {
highlightsCount = arguments?.getInt(HIGHLIGHT_KEY)!!
duplicatesCount = arguments?.getInt(DUPLICATION_KEY)!!
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DialogDuplicationWarningBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (shouldShowThreeOptions()) {
binding.addAllButton.setOnClickListener(this)
binding.addNewButton.setOnClickListener(this)
binding.cancelButton.setOnClickListener(this)
setupSecondaryText(R.plurals.duplication_three_options_secondary)
} else {
binding.addNewButton.visibility = View.GONE
binding.addAllButton.text = resources.getString(R.string.add_button)
binding.addAllButton.setOnClickListener(this)
binding.cancelButton.setOnClickListener(this)
setupSecondaryText(R.plurals.duplication_two_options_secondary)
}
}
override fun onClick(view: View) {
val option = when (view.id) {
R.id.add_all_button -> {
ADD_ALL
}
R.id.add_new_button -> {
ADD_NEW
}
R.id.cancel_button -> {
CANCEL
}
else -> {
NO_OPTION
}
}
val bundle = bundleOf(OPTION_KEY to option)
setFragmentResult(REQUEST_KEY, bundle)
dismiss()
}
private fun setupSecondaryText(pluralSecondary: Int) {
val secondaryMessage = resources.getQuantityString(pluralSecondary, duplicatesCount, duplicatesCount)
binding.secondaryTextview.text = secondaryMessage
}
private fun shouldShowThreeOptions() = duplicatesCount < highlightsCount
companion object {
const val REQUEST_KEY = "REQUEST_KEY"
const val OPTION_KEY = "option"
const val NO_OPTION = -1
const val ADD_ALL = 0
const val ADD_NEW = 1
const val CANCEL = 2
private const val HIGHLIGHT_KEY = "highlighted_items_count"
private const val DUPLICATION_KEY = "duplicate_items_count"
fun newInstance(highlightedItemsCount: Int, duplicateItemsCount: Int) : DuplicationWarningDialog {
return DuplicationWarningDialog().apply {
val args = Bundle()
args.putInt(HIGHLIGHT_KEY, highlightedItemsCount)
args.putInt(DUPLICATION_KEY, duplicateItemsCount)
arguments = args
}
}
}
}
......@@ -45,6 +45,12 @@ import org.videolan.tools.DependencyProvider
import org.videolan.vlc.R
import org.videolan.vlc.databinding.DialogPlaylistBinding
import org.videolan.vlc.gui.SimpleAdapter
import org.videolan.vlc.gui.dialogs.DuplicationWarningDialog.Companion.ADD_ALL
import org.videolan.vlc.gui.dialogs.DuplicationWarningDialog.Companion.ADD_NEW
import org.videolan.vlc.gui.dialogs.DuplicationWarningDialog.Companion.CANCEL
import org.videolan.vlc.gui.dialogs.DuplicationWarningDialog.Companion.NO_OPTION
import org.videolan.vlc.gui.dialogs.DuplicationWarningDialog.Companion.OPTION_KEY
import org.videolan.vlc.gui.dialogs.DuplicationWarningDialog.Companion.REQUEST_KEY
import org.videolan.vlc.providers.FileBrowserProvider
import org.videolan.vlc.viewmodels.browser.TYPE_FILE
import org.videolan.vlc.viewmodels.browser.getBrowserModel
......@@ -59,6 +65,9 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
override fun needToManageOrientation(): Boolean = false
var selectedPlaylist: Playlist? = null
var nonDuplicateTracks: Array<MediaWrapper>? = null
private var isLoading: Boolean = false
set(value) {
field = value
......@@ -71,7 +80,7 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
}
private lateinit var binding: DialogPlaylistBinding
private lateinit var adapter: SimpleAdapter
private lateinit var newTrack: Array<MediaWrapper>
private lateinit var newTracks: Array<MediaWrapper>
private lateinit var medialibrary: Medialibrary
private val coroutineContextProvider: CoroutineContextProvider
......@@ -88,7 +97,7 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
super.onCreate(savedInstanceState)
medialibrary = Medialibrary.getInstance()
adapter = SimpleAdapter(this)
newTrack = try {
newTracks = try {
@Suppress("UNCHECKED_CAST")
val tracks = requireArguments().getParcelableArray(KEY_NEW_TRACKS) as Array<MediaWrapper>
filesText = resources.getQuantityString(R.plurals.media_quantity, tracks.size, tracks.size)
......@@ -101,15 +110,15 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
val viewModel = getBrowserModel(category = TYPE_FILE, url = folder, showHiddenFiles = false)
if (requireArguments().getBoolean(KEY_SUB_FOLDERS, false)) lifecycleScope.launchWhenStarted {
withContext(Dispatchers.IO) {
newTrack = (viewModel.provider as FileBrowserProvider).browseByUrl(folder).toTypedArray()
newTracks = (viewModel.provider as FileBrowserProvider).browseByUrl(folder).toTypedArray()
isLoading = false
filesText = resources.getQuantityString(R.plurals.media_quantity, newTrack.size, newTrack.size)
filesText = resources.getQuantityString(R.plurals.media_quantity, newTracks.size, newTracks.size)
}
} else {
viewModel.dataset.observe(this, { mediaLibraryItems ->
newTrack = mediaLibraryItems.asSequence().map { it as MediaWrapper }.filter { it.type != MediaWrapper.TYPE_DIR }.toList().toTypedArray()
newTracks = mediaLibraryItems.asSequence().map { it as MediaWrapper }.filter { it.type != MediaWrapper.TYPE_DIR }.toList().toTypedArray()
isLoading = false
filesText = resources.getQuantityString(R.plurals.media_quantity, newTrack.size, newTrack.size)
filesText = resources.getQuantityString(R.plurals.media_quantity, newTracks.size, newTracks.size)
})
}
}
......@@ -118,6 +127,8 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
emptyArray()
}
}
selectedPlaylist = savedInstanceState?.getParcelable(SELECTED_PLAYLIST)
nonDuplicateTracks = selectedPlaylist?.let { getNonDuplicateTracks(it.tracks, newTracks) }
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
......@@ -143,8 +154,28 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
binding.list.layoutManager = LinearLayoutManager(view.context)
binding.list.adapter = adapter
adapter.submitList(listOf<MediaLibraryItem>(*medialibrary.playlists.apply { forEach { it.description = resources.getQuantityString(R.plurals.media_quantity, it.tracksCount, it.tracksCount) } }))
if (!Tools.isArrayEmpty(newTrack)) binding.dialogPlaylistSave.setText(R.string.save)
if (!Tools.isArrayEmpty(newTracks)) binding.dialogPlaylistSave.setText(R.string.save)
updateEmptyView()
parentFragmentManager.setFragmentResultListener(
REQUEST_KEY,
viewLifecycleOwner) { s: String, bundle: Bundle ->
when (bundle.getInt(OPTION_KEY)) {
ADD_ALL -> {
savePlaylist(selectedPlaylist!!, newTracks)
}
ADD_NEW -> {
savePlaylist(selectedPlaylist!!, nonDuplicateTracks!!)
}
CANCEL, NO_OPTION -> {
// do nothing
}
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(SELECTED_PLAYLIST, selectedPlaylist)
}
private fun updateEmptyView() {
......@@ -171,15 +202,15 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
return@launch
}
dismiss()
savePlaylist(medialibrary.createPlaylist(name) ?: return@launch)
savePlaylist(medialibrary.createPlaylist(name) ?: return@launch, newTracks)
}
}
private fun savePlaylist(playlist: Playlist) {
private fun savePlaylist(playlist: Playlist, tracks: Array<MediaWrapper>) {
AppScope.launch(coroutineContextProvider.IO) {
if (newTrack.isEmpty()) return@launch
if (tracks.isEmpty()) return@launch
val ids = LinkedList<Long>()
for (mw in newTrack) {
for (mw in tracks) {
val id = mw.id
if (id == 0L) {
var media = medialibrary.getMedia(mw.uri)
......@@ -198,7 +229,24 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
}
override fun onClick(item: MediaLibraryItem) {
savePlaylist(item as Playlist)
selectedPlaylist = item as Playlist
nonDuplicateTracks = getNonDuplicateTracks(selectedPlaylist!!.tracks, newTracks)
val duplicateItemsCount = newTracks.size - nonDuplicateTracks!!.size
if (duplicateItemsCount == 0) {
savePlaylist(selectedPlaylist!!, newTracks)
} else {
val highlightedItemsCount = newTracks.size
val warningDialog = DuplicationWarningDialog.newInstance(highlightedItemsCount, duplicateItemsCount)
warningDialog.show(requireActivity().supportFragmentManager, "duplicationWarningDialog")
}
}
private fun getNonDuplicateTracks(currentTracks: Array<MediaWrapper>, newTracks: Array<MediaWrapper>): Array<MediaWrapper> {
return newTracks.filter { newItem ->
currentTracks.all { currentItem ->
!currentItem.equals(newItem)
}
}.toTypedArray()
}
companion object : DependencyProvider<Any>() {
......@@ -208,6 +256,8 @@ class SavePlaylistDialog : VLCBottomSheetDialogFragment(), View.OnClickListener,
const val KEY_NEW_TRACKS = "PLAYLIST_NEW_TRACKS"
const val KEY_FOLDER = "PLAYLIST_FROM_FOLDER"
const val KEY_SUB_FOLDERS = "PLAYLIST_FOLDER_ADD_SUBFOLDERS"
const val SELECTED_PLAYLIST = "SELECTED_PLAYLIST"
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment