[Android] Fix TV browser focus when navigating with the breadcrumb

Nicolas Pomepuy git at videolan.org
Thu Sep 7 12:56:28 UTC 2023


vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Tue Sep  5 11:54:29 2023 +0200| [a4672c1d592e295a10084a6deec4971065361ffa] | committer: Duncan McNamara

Fix TV browser focus when navigating with the breadcrumb

> https://code.videolan.org/videolan/vlc-android/commit/a4672c1d592e295a10084a6deec4971065361ffa
---

 .../television/ui/browser/FileBrowserTvFragment.kt | 44 +++++++++++++++++++++-
 .../viewmodels/browser/PathOperationDelegate.kt    | 15 ++++++++
 2 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/application/television/src/main/java/org/videolan/television/ui/browser/FileBrowserTvFragment.kt b/application/television/src/main/java/org/videolan/television/ui/browser/FileBrowserTvFragment.kt
index ee5cdc1030..8f1d689dc2 100644
--- a/application/television/src/main/java/org/videolan/television/ui/browser/FileBrowserTvFragment.kt
+++ b/application/television/src/main/java/org/videolan/television/ui/browser/FileBrowserTvFragment.kt
@@ -44,12 +44,18 @@ import org.videolan.vlc.util.DialogDelegate
 import org.videolan.vlc.util.FileUtils
 import org.videolan.vlc.util.IDialogManager
 import org.videolan.vlc.util.isSchemeSupported
-import org.videolan.vlc.viewmodels.browser.*
+import org.videolan.vlc.util.onAnyChange
+import org.videolan.vlc.viewmodels.browser.BrowserModel
+import org.videolan.vlc.viewmodels.browser.IPathOperationDelegate
+import org.videolan.vlc.viewmodels.browser.TYPE_FILE
+import org.videolan.vlc.viewmodels.browser.TYPE_NETWORK
+import org.videolan.vlc.viewmodels.browser.getBrowserModel
 
 private const val TAG = "FileBrowserTvFragment"
 
 class FileBrowserTvFragment : BaseBrowserTvFragment<MediaLibraryItem>(), PathAdapterListener, IDialogManager {
 
+    private lateinit var dataObserver: RecyclerView.AdapterDataObserver
     private var favExists: Boolean = false
     private var isRootLevel = false
     private lateinit var browserFavRepository: BrowserFavRepository
@@ -68,7 +74,35 @@ class FileBrowserTvFragment : BaseBrowserTvFragment<MediaLibraryItem>(), PathAda
     override fun getColumnNumber() = resources.getInteger(R.integer.tv_songs_col_count)
 
     override fun provideAdapter(eventsHandler: IEventsHandler<MediaLibraryItem>, itemSize: Int): TvItemAdapter {
-        return FileTvItemAdapter(this, itemSize, isRootLevel && getCategory() == TYPE_NETWORK)
+        val fileTvItemAdapter = FileTvItemAdapter(this, itemSize, isRootLevel && getCategory() == TYPE_NETWORK)
+        // restore the position from the source when navigating from Arian
+        dataObserver = fileTvItemAdapter.onAnyChange {
+            val source = (viewModel as IPathOperationDelegate).getSource()
+            val selectedIndex = if (source != null) {
+                if (fileTvItemAdapter.dataset.contains(source)) {
+                    //the source has been found because we are on its direct parent
+                    fileTvItemAdapter.dataset.indexOf(source)
+                } else {
+                    // we look for the item included in the source path to find what item to focus
+                    var index: Int? = null
+                    fileTvItemAdapter.dataset.forEach {
+                        if ((source as? MediaWrapper)?.uri?.toString()?.startsWith(it.uri.toString()) == true) {
+                            index = fileTvItemAdapter.dataset.indexOf(it)
+                        }
+                    }
+                    index
+                }
+            } else null
+            if (selectedIndex != null) {
+                val lm = binding.list.layoutManager as LinearLayoutManager
+                lm.scrollToPosition(selectedIndex)
+                lm.getChildAt(selectedIndex)?.let {
+                    it.requestFocus()
+                    (viewModel as IPathOperationDelegate).consumeSource()
+                }
+            }
+        }
+        return fileTvItemAdapter
     }
 
     override fun getDisplayPrefId() = "display_tv_file_${getCategory()}"
@@ -183,6 +217,7 @@ class FileBrowserTvFragment : BaseBrowserTvFragment<MediaLibraryItem>(), PathAda
             if (supportFragmentManager.backStackEntryCount == 0) browse(MLServiceLocator.getAbstractMediaWrapper(tag.toUri()), false)
             else {
                 (viewModel as IPathOperationDelegate).setDestination(MLServiceLocator.getAbstractMediaWrapper(tag.toUri()))
+                (viewModel as IPathOperationDelegate).setSource(currentItem)
                 supportFragmentManager.popBackStack()
             }
         }
@@ -231,6 +266,11 @@ class FileBrowserTvFragment : BaseBrowserTvFragment<MediaLibraryItem>(), PathAda
         (viewModel as BrowserModel).stop()
     }
 
+    override fun onDestroy() {
+        super.onDestroy()
+        if (::dataObserver.isInitialized) (adapter as FileTvItemAdapter).unregisterAdapterDataObserver(dataObserver)
+    }
+
     override fun getCategory() = (viewModel as BrowserModel).type
 
     override fun onClick(v: View, position: Int, item: MediaLibraryItem) {
diff --git a/application/vlc-android/src/org/videolan/vlc/viewmodels/browser/PathOperationDelegate.kt b/application/vlc-android/src/org/videolan/vlc/viewmodels/browser/PathOperationDelegate.kt
index c0ef12b647..0f18af537a 100644
--- a/application/vlc-android/src/org/videolan/vlc/viewmodels/browser/PathOperationDelegate.kt
+++ b/application/vlc-android/src/org/videolan/vlc/viewmodels/browser/PathOperationDelegate.kt
@@ -4,6 +4,7 @@ import android.net.Uri
 import android.util.Base64
 import androidx.collection.SimpleArrayMap
 import org.videolan.medialibrary.interfaces.media.MediaWrapper
+import org.videolan.medialibrary.media.MediaLibraryItem
 
 interface IPathOperationDelegate {
 
@@ -14,6 +15,9 @@ interface IPathOperationDelegate {
     fun retrieveSafePath(encoded: String): String
     fun setDestination(media: MediaWrapper?)
     fun getAndRemoveDestination(): MediaWrapper?
+    fun setSource(currentItem: MediaLibraryItem?)
+    fun getSource(): MediaLibraryItem?
+    fun consumeSource()
 }
 
 class PathOperationDelegate : IPathOperationDelegate {
@@ -21,15 +25,26 @@ class PathOperationDelegate : IPathOperationDelegate {
         privateDestination = media
     }
 
+    override fun setSource(currentItem: MediaLibraryItem?) {
+        privateSource = currentItem
+    }
+
     override fun getAndRemoveDestination(): MediaWrapper? {
         val destination = privateDestination
         privateDestination = null
         return destination
     }
 
+    override fun consumeSource() {
+        privateSource = null
+    }
+
+    override fun getSource() = privateSource
+
     companion object {
         val storages = SimpleArrayMap<String, String>()
         private var privateDestination: MediaWrapper? = null
+        private var privateSource: MediaLibraryItem? = null
     }
 
 



More information about the Android mailing list