Commit 366c0fa6 authored by Nicolas Pomepuy's avatar Nicolas Pomepuy
Browse files

Use a custom view for the card lists

parent f35eac4d
......@@ -117,6 +117,9 @@
<attr name="empty_text" format="reference|string" />
<attr name="show_no_media" format="boolean" />
</declare-styleable>
<declare-styleable name="TitleListView">
<attr name="title" format="reference|string" />
</declare-styleable>
<declare-styleable name="HalfCircleView">
<attr name="is_left" format="boolean"/>
......
......@@ -74,11 +74,7 @@
style="@style/VLCCardView"
android:longClickable="@{hasContextMenu || item.getItemType() == MediaLibraryItem.TYPE_STORAGE}"
android:onClick="@{holder::onClick}"
android:onLongClick="@{holder::onLongClick}"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp">
android:onLongClick="@{holder::onLongClick}">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
......
......@@ -53,9 +53,7 @@
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/VLCCardView"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp">
style="@style/VLCCardView">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
......
......@@ -32,27 +32,27 @@
android:layout_height="match_parent">
<include
<org.videolan.vlc.gui.view.TitleListView
android:id="@+id/fav_browser_entry"
layout="@layout/browser_entry"
app:title="@string/favorites"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include
<org.videolan.vlc.gui.view.TitleListView
android:id="@+id/local_browser_entry"
layout="@layout/browser_entry"
app:title="@string/browser_storages"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fav_browser_entry" />
<include
<org.videolan.vlc.gui.view.TitleListView
android:id="@+id/network_browser_entry"
layout="@layout/browser_entry"
app:title="@string/network_browsing"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
......
......@@ -74,28 +74,15 @@
app:layout_constraintStart_toEndOf="@+id/settingsButton"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/historyTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
style="@style/Theme.VLC.HorizontalListTitle"
android:text="@string/history"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settingsButton" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/historyList"
<org.videolan.vlc.gui.view.TitleListView
android:id="@+id/history_entry"
android:layout_width="0dp"
app:title="@string/history"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:clipToPadding="false"
android:paddingTop="10dp"
android:paddingBottom="@dimen/listview_bottom_padding"
android:layout_marginTop="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/historyTitle" />
app:layout_constraintTop_toBottomOf="@+id/settingsButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
......
......@@ -218,11 +218,11 @@
<item name="ariane_text_color">@color/grey50</item>
<item name="ariane_text_size">14sp</item>
<item name="player_icon_color">@color/white</item>
<item name="card_background">@color/grey900</item>
<item name="card_background">@color/black</item>
<item name="default_separator">@color/grey700</item>
<item name="empty_button">@style/Widget.MaterialComponents.Button.OutlinedButton</item>
<item name="card_border">@color/transparent</item>
<item name="card_border">@color/grey800</item>
</style>
<style name="Theme.VLC.Onboarding" parent="Theme.VLC.Black">
......
......@@ -33,7 +33,6 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import kotlinx.android.synthetic.main.more_fragment.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
......@@ -46,6 +45,8 @@ import org.videolan.vlc.R
import org.videolan.vlc.gui.SecondaryActivity.Companion.ABOUT
import org.videolan.vlc.gui.helpers.*
import org.videolan.vlc.gui.preferences.PreferencesActivity
import org.videolan.vlc.gui.view.EmptyLoadingState
import org.videolan.vlc.gui.view.TitleListView
import org.videolan.vlc.interfaces.IHistory
import org.videolan.vlc.interfaces.IRefreshable
import org.videolan.vlc.media.MediaUtils
......@@ -60,6 +61,7 @@ private const val KEY_SELECTION = "key_selection"
@ExperimentalCoroutinesApi
class MoreFragment : BaseFragment(), IRefreshable, IHistory, SwipeRefreshLayout.OnRefreshListener {
private lateinit var historyEntry: TitleListView
private lateinit var viewModel: HistoryModel
private lateinit var cleanMenuItem: MenuItem
private lateinit var multiSelectHelper: MultiSelectHelper<MediaWrapper>
......@@ -81,6 +83,7 @@ class MoreFragment : BaseFragment(), IRefreshable, IHistory, SwipeRefreshLayout.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
historyEntry = view.findViewById(R.id.history_entry)
viewModel = ViewModelProviders.of(requireActivity(), HistoryModel.Factory(requireContext())).get(HistoryModel::class.java)
viewModel.dataset.observe(viewLifecycleOwner, Observer<List<MediaWrapper>> { list ->
list?.let {
......@@ -88,12 +91,16 @@ class MoreFragment : BaseFragment(), IRefreshable, IHistory, SwipeRefreshLayout.
if (::cleanMenuItem.isInitialized) {
cleanMenuItem.isVisible = list.isNotEmpty()
}
if (list.isEmpty()) historyTitle.setGone() else historyTitle.setVisible()
if (list.isEmpty()) historyEntry.setGone() else {
historyEntry.setVisible()
historyEntry.loading.state = EmptyLoadingState.NONE
}
}
restoreMultiSelectHelper()
})
viewModel.loading.observe(viewLifecycleOwner) {
(activity as? MainActivity)?.refreshing = it
if (it) historyEntry.loading.state = EmptyLoadingState.LOADING
}
settingsButton.setOnClickListener {
......@@ -119,16 +126,15 @@ class MoreFragment : BaseFragment(), IRefreshable, IHistory, SwipeRefreshLayout.
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
historyList.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
historyList.adapter = historyAdapter
historyList.nextFocusUpId = R.id.ml_menu_search
historyList.nextFocusLeftId = android.R.id.list
historyList.nextFocusRightId = android.R.id.list
historyList.nextFocusForwardId = android.R.id.list
historyEntry.list.adapter = historyAdapter
historyEntry.list.nextFocusUpId = R.id.ml_menu_search
historyEntry.list.nextFocusLeftId = android.R.id.list
historyEntry.list.nextFocusRightId = android.R.id.list
historyEntry.list.nextFocusForwardId = android.R.id.list
multiSelectHelper = historyAdapter.multiSelectHelper
historyList.requestFocus()
registerForContextMenu(historyList)
historyEntry.list.requestFocus()
registerForContextMenu(historyEntry.list)
swipeRefreshLayout.setOnRefreshListener(this)
}
......
......@@ -27,14 +27,10 @@ package org.videolan.vlc.gui.browser
import android.content.Intent
import android.os.Bundle
import android.view.*
import android.widget.TextView
import androidx.appcompat.view.ActionMode
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.*
import org.videolan.medialibrary.interfaces.media.MediaWrapper
import org.videolan.medialibrary.media.MediaLibraryItem
......@@ -53,6 +49,7 @@ import org.videolan.vlc.gui.helpers.UiTools.showMediaInfo
import org.videolan.vlc.gui.helpers.hf.OTG_SCHEME
import org.videolan.vlc.gui.view.EmptyLoadingState
import org.videolan.vlc.gui.view.EmptyLoadingStateView
import org.videolan.vlc.gui.view.TitleListView
import org.videolan.vlc.media.MediaUtils
import org.videolan.vlc.repository.BrowserFavRepository
import org.videolan.vlc.viewmodels.browser.*
......@@ -64,16 +61,13 @@ class MainBrowserFragment : BaseFragment(), View.OnClickListener, CtxActionRecei
private lateinit var networkMonitor: NetworkMonitor
private var currentCtx: MainBrowserContainer? = null
private lateinit var browserFavRepository: BrowserFavRepository
private lateinit var localList: RecyclerView
private lateinit var localEntry: ConstraintLayout
private lateinit var localEntry: TitleListView
private lateinit var localViewModel: BrowserModel
private lateinit var favoritesList: RecyclerView
private lateinit var favoritesEntry: ConstraintLayout
private lateinit var favoritesEntry: TitleListView
private lateinit var favoritesViewModel: BrowserFavoritesModel
private lateinit var networkList: RecyclerView
private lateinit var networkEntry: ConstraintLayout
private lateinit var networkEntry: TitleListView
private lateinit var networkViewModel: BrowserModel
private var currentAdapterActionMode: BaseBrowserAdapter? = null
......@@ -130,23 +124,19 @@ class MainBrowserFragment : BaseFragment(), View.OnClickListener, CtxActionRecei
//local
localEntry = view.findViewById(R.id.local_browser_entry)
val localLoading: EmptyLoadingStateView = localEntry.findViewById(R.id.loading)
localEntry.findViewById<TextView>(R.id.title).text = getString(R.string.browser_storages)
localList = localEntry.findViewById(R.id.list)
localList.layoutManager = LinearLayoutManager(requireActivity(), RecyclerView.HORIZONTAL, false)
val storageBbrowserContainer = MainBrowserContainer(isNetwork = false, isFile = true)
val storageBrowserAdapter = BaseBrowserAdapter(storageBbrowserContainer)
localList.adapter = storageBrowserAdapter
localEntry.list.adapter = storageBrowserAdapter
localViewModel = getBrowserModel(category = TYPE_FILE, url = null, showHiddenFiles = false)
containerAdapterAssociation[storageBbrowserContainer] = Pair(storageBrowserAdapter, localViewModel)
localViewModel.dataset.observe(viewLifecycleOwner, Observer<List<MediaLibraryItem>> { list ->
list?.let {
storageBrowserAdapter.update(it)
localLoading.state = if (list.isEmpty()) EmptyLoadingState.EMPTY else EmptyLoadingState.NONE
localEntry.loading.state = if (list.isEmpty()) EmptyLoadingState.EMPTY else EmptyLoadingState.NONE
}
})
localViewModel.loading.observe(viewLifecycleOwner, Observer {
if (it) localLoading.state = EmptyLoadingState.LOADING
if (it) localEntry.loading.state = EmptyLoadingState.LOADING
})
localViewModel.browseRoot()
localViewModel.getDescriptionUpdate().observe(viewLifecycleOwner, Observer { pair ->
......@@ -156,44 +146,36 @@ class MainBrowserFragment : BaseFragment(), View.OnClickListener, CtxActionRecei
favoritesEntry = view.findViewById(R.id.fav_browser_entry)
val favoritesLoading: EmptyLoadingStateView = favoritesEntry.findViewById(R.id.loading)
favoritesEntry.findViewById<TextView>(R.id.title).text = getString(R.string.favorites)
favoritesList = favoritesEntry.findViewById(R.id.list)
favoritesList.layoutManager = LinearLayoutManager(requireActivity(), RecyclerView.HORIZONTAL, false)
val favoritesBrowserContainer = MainBrowserContainer(isNetwork = false, isFile = true)
val favoritesAdapter = BaseBrowserAdapter(favoritesBrowserContainer)
favoritesList.adapter = favoritesAdapter
favoritesEntry.list.adapter = favoritesAdapter
favoritesViewModel = BrowserFavoritesModel(requireContext())
containerAdapterAssociation[favoritesBrowserContainer] = Pair(favoritesAdapter, favoritesViewModel)
favoritesViewModel.updatedFavoriteList.observe(viewLifecycleOwner, Observer<List<MediaWrapper>> { list ->
list?.let {
favoritesAdapter.update(it)
favoritesLoading.state = if (list.isEmpty()) EmptyLoadingState.EMPTY else EmptyLoadingState.NONE
favoritesEntry.loading.state = if (list.isEmpty()) EmptyLoadingState.EMPTY else EmptyLoadingState.NONE
}
})
networkEntry = view.findViewById(R.id.network_browser_entry)
val networkLoading: EmptyLoadingStateView = networkEntry.findViewById(R.id.loading)
networkLoading.showNoMedia = false
networkLoading.emptyText = R.string.nomedia
networkEntry.findViewById<TextView>(R.id.title).text = getString(R.string.network_browsing)
networkList = networkEntry.findViewById(R.id.list)
networkList.layoutManager = LinearLayoutManager(requireActivity(), RecyclerView.HORIZONTAL, false)
networkEntry.loading.showNoMedia = false
networkEntry.loading.emptyText = R.string.nomedia
val networkBrowserContainer = MainBrowserContainer(isNetwork = true, isFile = false)
val networkAdapter = BaseBrowserAdapter(networkBrowserContainer)
networkList.adapter = networkAdapter
networkEntry.list.adapter = networkAdapter
networkViewModel = getBrowserModel(category = TYPE_NETWORK, url = null, showHiddenFiles = false)
containerAdapterAssociation[networkBrowserContainer] = Pair(networkAdapter, networkViewModel)
networkViewModel.dataset.observe(viewLifecycleOwner, Observer<List<MediaLibraryItem>> { list ->
list?.let {
networkAdapter.update(it)
updateNetworkEmptyView(networkLoading)
if (networkViewModel.loading.value == false) networkLoading.state = if (list.isEmpty()) EmptyLoadingState.EMPTY else EmptyLoadingState.NONE
updateNetworkEmptyView(networkEntry.loading)
if (networkViewModel.loading.value == false) networkEntry.loading.state = if (list.isEmpty()) EmptyLoadingState.EMPTY else EmptyLoadingState.NONE
}
})
networkViewModel.loading.observe(viewLifecycleOwner, Observer {
if (it) localLoading.state = EmptyLoadingState.LOADING
updateNetworkEmptyView(networkLoading)
if (it) networkEntry.loading.state = EmptyLoadingState.LOADING
updateNetworkEmptyView(networkEntry.loading)
})
networkViewModel.browseRoot()
}
......@@ -211,17 +193,17 @@ class MainBrowserFragment : BaseFragment(), View.OnClickListener, CtxActionRecei
emptyLoading.state = EmptyLoadingState.EMPTY
emptyLoading.emptyText = R.string.network_connection_needed
}
networkList.visibility = View.GONE
networkEntry.list.visibility = View.GONE
// handler.sendEmptyMessage(MSG_HIDE_LOADING)
}
} else {
emptyLoading.state = EmptyLoadingState.NONE
networkList.visibility = View.VISIBLE
networkEntry.list.visibility = View.VISIBLE
}
} else {
emptyLoading.state = EmptyLoadingState.EMPTY
emptyLoading.emptyText = R.string.network_connection_needed
networkList.visibility = View.GONE
networkEntry.list.visibility = View.GONE
}
}
......
/*
* ************************************************************************
* TitleListView.kt
* *************************************************************************
* Copyright © 2020 VLC authors and VideoLAN
* Author: Nicolas POMEPUY
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
* **************************************************************************
*
*
*/
package org.videolan.vlc.gui.view
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import org.videolan.vlc.R
class TitleListView : ConstraintLayout {
private val titleView: TextView by lazy {
findViewById<TextView>(R.id.title)
}
val list: RecyclerView by lazy {
findViewById<RecyclerView>(R.id.list)
}
val loading: EmptyLoadingStateView by lazy {
findViewById<EmptyLoadingStateView>(R.id.loading)
}
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initialize()
initAttributes(attrs, 0)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
initialize()
initAttributes(attrs, 0)
}
private fun initAttributes(attrs: AttributeSet, defStyle: Int) {
attrs.let {
val a = context.theme.obtainStyledAttributes(attrs, R.styleable.TitleListView, 0, defStyle)
try {
titleView.text = a.getString(R.styleable.TitleListView_title)
} catch (e: Exception) {
Log.w("", e.message, e)
} finally {
a.recycle()
}
}
list.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
list.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
outRect.left = resources.getDimension(R.dimen.kl_half).toInt()
outRect.right = resources.getDimension(R.dimen.kl_half).toInt()
}
})
}
private fun initialize() {
LayoutInflater.from(context).inflate(R.layout.title_list_view, this, true)
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment