Browse Source

Android TV: make it possible to ban/unban folders

Includes a new icon to ban / unban as the long click is already used for
the checkbox. It uses the focus to show/hide the icon
Fixes #2665
merge-requests/1535/head
Nicolas Pomepuy 4 years ago
parent
commit
28c041ca45
  1. 9
      application/resources/src/main/res/drawable/ic_ban.xml
  2. 9
      application/resources/src/main/res/drawable/ic_banned.xml
  3. 1
      application/resources/src/main/res/values/strings.xml
  4. 38
      application/vlc-android/res/layout/browser_item.xml
  5. 17
      application/vlc-android/res/layout/card_browser_item.xml
  6. 27
      application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt
  7. 9
      application/vlc-android/src/org/videolan/vlc/gui/browser/BrowserItemBindingContainer.kt
  8. 31
      application/vlc-android/src/org/videolan/vlc/gui/browser/StorageBrowserAdapter.kt

9
application/resources/src/main/res/drawable/ic_ban.xml

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z" />
</vector>

9
application/resources/src/main/res/drawable/ic_banned.xml

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/orange500"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z" />
</vector>

1
application/resources/src/main/res/values/strings.xml

@ -950,6 +950,7 @@
<string name="video_double_tap_jump_delay">Double tap time delay</string>
<string name="video_key_jump_delay">Navigation keys time delay</string>
<string name="banned">Banned folder (long tap to change)</string>
<string name="banned_tv">Banned folder</string>
<string name="banned_parent">Banned by parent folder</string>
<plurals name="duplication_two_options_secondary">
<item quantity="one">This item is already in this playlist.</item>

38
application/vlc-android/res/layout/browser_item.xml

@ -54,6 +54,10 @@
<variable
name="bgColor"
type="int" />
<variable
name="isTv"
type="boolean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
@ -64,6 +68,7 @@
android:id="@+id/browser_container"
android:contentDescription="@{TalkbackUtil.INSTANCE.getDir(context, item, favorite)}"
android:focusable="true"
android:nextFocusRight="@+id/item_ban"
android:foreground="?android:attr/selectableItemBackground"
android:longClickable="@{hasContextMenu || item.getItemType() == MediaLibraryItem.TYPE_STORAGE}"
android:minHeight="48dp"
@ -122,6 +127,7 @@
android:layout_marginStart="12dp"
android:layout_marginEnd="8dp"
android:ellipsize="marquee"
android:enabled="@{!isBanned}"
android:lineSpacingMultiplier="1.1"
android:marqueeRepeatLimit="1"
android:scrollHorizontally="true"
@ -130,9 +136,8 @@
android:textColor="?attr/list_title"
android:textSize="16sp"
app:ellipsizeMode="@{true}"
android:enabled="@{!isBanned}"
app:layout_constraintBottom_toTopOf="@+id/text"
app:layout_constraintEnd_toStartOf="@+id/item_more"
app:layout_constraintEnd_toStartOf="@+id/item_ban"
app:layout_constraintStart_toEndOf="@+id/item_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
@ -144,14 +149,15 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:enabled="@{!isBanned}"
android:maxLines="1"
android:textColor="?attr/list_subtitle"
android:textSize="12sp"
android:enabled="@{!isBanned}"
android:visibility="@{TextUtils.isEmpty(item.description) &amp;&amp; !isBanned ? View.GONE : View.VISIBLE, default=gone}"
app:browserDescription="@{isBannedParent ? @string/banned_parent : isBanned ? @string/banned : item.description}"
app:browserDescription="@{isBannedParent ? @string/banned_parent : isBanned ? isTv ? @string/banned_tv : @string/banned : item.description}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/item_more"
app:layout_constraintEnd_toStartOf="@+id/item_ban"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="@+id/title"
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Empty"
@ -172,5 +178,27 @@
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_more"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/item_ban"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:visibility="gone"
android:padding="2dp"
android:alpha="0"
android:contentDescription="@string/more_actions"
android:onClick="@{holder::onBanClick}"
android:nextFocusLeft="@+id/browser_container"
android:nextFocusRight="@+id/browser_container"
android:nextFocusUp="@+id/browser_container"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/item_more"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_ban"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

17
application/vlc-android/res/layout/card_browser_item.xml

@ -102,6 +102,23 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/item_ban"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="@{hasContextMenu}"
android:contentDescription="@string/more_actions"
android:onClick="@{holder::onMoreClick}"
android:padding="8dp"
android:visibility="@{hasContextMenu ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_more"
tools:visibility="visible" />
<!-- Image downloading is handled by org.videolan.vlc.gui.helpers.ImageLoader.loadImage() -->
<ImageView
android:id="@+id/item_icon"

27
application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt

@ -134,6 +134,7 @@ open class BaseBrowserAdapter(val browserContainer: BrowserContainer<MediaLibrar
val vh = holder as SeparatorViewHolder
vh.binding.title = dataset[position].title
}
itemFocusChanged(position, false, (holder as MediaViewHolder).bindingContainer)
}
override fun onBindViewHolder(holder: ViewHolder<ViewDataBinding>, position: Int, payloads: MutableList<Any>) {
@ -148,6 +149,7 @@ open class BaseBrowserAdapter(val browserContainer: BrowserContainer<MediaLibrar
val value = payloads[0] as Int
if (value == UPDATE_SELECTION) holder.selectView(multiSelectHelper.isSelected(position))
}
itemFocusChanged(position, false, (holder as MediaViewHolder).bindingContainer)
}
private fun onBindMediaViewHolder(vh: MediaViewHolder, position: Int) {
@ -164,6 +166,7 @@ open class BaseBrowserAdapter(val browserContainer: BrowserContainer<MediaLibrar
if (networkRoot || (isFavorite && getProtocol(media)?.contains("file") == false)) vh.bindingContainer.setProtocol(getProtocol(media))
vh.bindingContainer.setCover(getIcon(media, specialIcons))
vh.selectView(multiSelectHelper.isSelected(position))
itemFocusChanged(position, false, vh.bindingContainer)
}
override fun onViewRecycled(holder: ViewHolder<ViewDataBinding>) {
@ -190,8 +193,19 @@ open class BaseBrowserAdapter(val browserContainer: BrowserContainer<MediaLibrar
open fun onMoreClick(v: View) {}
open fun onBanClick(v: View) {}
}
/**
* Listener for the item focus. For now it's only used on TV to manage the ban icon visibility
*
* @param position the item position
* @param hasFocus true if the item has the focus
* @param bindingContainer the [BrowserItemBindingContainer] to be used
*/
open fun itemFocusChanged(position: Int, hasFocus: Boolean, bindingContainer: BrowserItemBindingContainer) {}
@TargetApi(Build.VERSION_CODES.M)
inner class MediaViewHolder(val bindingContainer: BrowserItemBindingContainer) : ViewHolder<ViewDataBinding>(bindingContainer.binding), MarqueeViewHolder {
override val titleView: TextView? = bindingContainer.title
@ -206,6 +220,14 @@ open class BaseBrowserAdapter(val browserContainer: BrowserContainer<MediaLibrar
if (this@BaseBrowserAdapter is FilePickerAdapter) {
bindingContainer.itemIcon.isFocusable = false
}
val focusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
itemFocusChanged(layoutPosition, hasFocus, bindingContainer)
}
bindingContainer.banIcon.onFocusChangeListener = focusChangeListener
bindingContainer.container.onFocusChangeListener = focusChangeListener
}
override fun selectView(selected: Boolean) {
@ -242,6 +264,11 @@ open class BaseBrowserAdapter(val browserContainer: BrowserContainer<MediaLibrar
browserContainer.onCtxClick(v, position, dataset[position])
}
override fun onBanClick(v: View) {
val position = layoutPosition
browserContainer.onLongClick(v, position, dataset[position])
}
override fun onLongClick(v: View): Boolean {
val position = layoutPosition
if (getItem(position).itemType == TYPE_STORAGE && Settings.showTvUi) {

9
application/vlc-android/src/org/videolan/vlc/gui/browser/BrowserItemBindingContainer.kt

@ -106,6 +106,12 @@ class BrowserItemBindingContainer(val binding: ViewDataBinding) {
}
}
fun setIsTv(isTv:Boolean) {
when (binding) {
is BrowserItemBinding -> binding.isTv = isTv
}
}
fun setHolder(holder: BaseBrowserAdapter.ViewHolder<ViewDataBinding>) {
when (binding) {
is CardBrowserItemBinding -> binding.holder = holder
@ -117,6 +123,7 @@ class BrowserItemBindingContainer(val binding: ViewDataBinding) {
var title: TextView
var itemIcon: ImageView
var browserCheckbox: ThreeStatesCheckbox
var banIcon: ImageView
var text: TextView
var container: View
var moreIcon: ImageView
@ -131,6 +138,7 @@ class BrowserItemBindingContainer(val binding: ViewDataBinding) {
browserCheckbox = binding.browserCheckbox
moreIcon = binding.itemMore
container = binding.browserContainer
banIcon = binding.itemBan
}
is BrowserItemBinding -> {
text = binding.text
@ -139,6 +147,7 @@ class BrowserItemBindingContainer(val binding: ViewDataBinding) {
browserCheckbox = binding.browserCheckbox
moreIcon = binding.itemMore
container = binding.browserContainer
banIcon = binding.itemBan
}
else -> throw IllegalStateException("Binding should be either a CardBrowserItemBinding or BrowserItemBinding")
}

31
application/vlc-android/src/org/videolan/vlc/gui/browser/StorageBrowserAdapter.kt

@ -26,6 +26,7 @@ package org.videolan.vlc.gui.browser
import android.content.Context
import android.net.Uri
import android.view.View
import androidx.core.content.ContextCompat
import androidx.databinding.ViewDataBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -35,9 +36,11 @@ import org.videolan.medialibrary.interfaces.Medialibrary
import org.videolan.medialibrary.interfaces.media.MediaWrapper
import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.medialibrary.media.Storage
import org.videolan.resources.AndroidDevices
import org.videolan.tools.containsPath
import org.videolan.tools.removeFileScheme
import org.videolan.vlc.MediaParsingService
import org.videolan.vlc.R
import org.videolan.vlc.gui.helpers.MedialibraryUtils
import org.videolan.vlc.gui.helpers.ThreeStatesCheckbox
import org.videolan.vlc.repository.DirectoryRepository
@ -66,6 +69,7 @@ class StorageBrowserAdapter(browserContainer: BrowserContainer<MediaLibraryItem>
if (!storagePath.endsWith("/")) storagePath += "/"
if (storage.title.isNullOrBlank()) storage.title = title
vh.bindingContainer.setItem(storage)
vh.bindingContainer.setIsTv(AndroidDevices.isTv)
updateJob?.join()
if (updateJob?.isCancelled == true) return@launch
val hasContextMenu = customDirsLocation.contains(storagePath) && !multiSelectHelper.inActionMode
@ -82,6 +86,7 @@ class StorageBrowserAdapter(browserContainer: BrowserContainer<MediaLibraryItem>
hasDiscoveredChildren(storagePath) -> vh.bindingContainer.browserCheckbox.state = ThreeStatesCheckbox.STATE_PARTIAL
else -> vh.bindingContainer.browserCheckbox.state = ThreeStatesCheckbox.STATE_UNCHECKED
}
if(AndroidDevices.isTv && !browserContainer.isRootDirectory) vh.bindingContainer.banIcon.visibility = View.VISIBLE
vh.bindingContainer.setCheckEnabled(!browserContainer.scannedDirectory)
}
}
@ -95,6 +100,32 @@ class StorageBrowserAdapter(browserContainer: BrowserContainer<MediaLibraryItem>
} else super.onBindViewHolder(holder, position, payloads)
}
/**
* Manages the item visibility on focus changes
*
* @param position the item position
* @param hasFocus true if the item has focus
* @param bindingContainer the [BrowserItemBindingContainer] associated with the item
*/
override fun itemFocusChanged(position: Int, hasFocus: Boolean, bindingContainer: BrowserItemBindingContainer) {
if (!AndroidDevices.isTv) return
if (browserContainer.isRootDirectory) return
if (position < 0 || position > itemCount - 1) return
val uri = (getItem(position) as Storage).uri.toString()
val banned = MedialibraryUtils.isBanned(uri, bannedFolders)
val context = bindingContainer.container.context
val bannedParent = banned && !MedialibraryUtils.isStrictlyBanned(uri, bannedFolders)
val alpha = when {
banned || bannedParent -> 1F
hasFocus -> 1F
else ->0F
}
bindingContainer.banIcon.animate().alpha(alpha)
bindingContainer.banIcon.setImageDrawable( ContextCompat.getDrawable(context, if (banned || bannedParent) R.drawable.ic_banned else R.drawable.ic_ban))
}
override fun onViewRecycled(holder: ViewHolder<ViewDataBinding>) {
(holder as MediaViewHolder).apply {
job?.cancel()

Loading…
Cancel
Save