mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 06:10:31 +05:30
Merge pull request #1333 from Bnyro/master
Option to customize the order of navigation bar items
This commit is contained in:
commit
6951cf53cd
@ -33,12 +33,12 @@ import com.github.libretube.models.PlayerViewModel
|
||||
import com.github.libretube.models.SearchViewModel
|
||||
import com.github.libretube.models.SubscriptionsViewModel
|
||||
import com.github.libretube.services.ClosingService
|
||||
import com.github.libretube.util.NavBarHelper
|
||||
import com.github.libretube.util.NetworkHelper
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.elevation.SurfaceColors
|
||||
import com.google.android.material.navigation.NavigationBarView
|
||||
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
@ -92,22 +92,8 @@ class MainActivity : BaseActivity() {
|
||||
// sets the navigation bar color to the previously calculated color
|
||||
window.navigationBarColor = color
|
||||
|
||||
// hide the trending page if enabled
|
||||
val hideTrendingPage =
|
||||
PreferenceHelper.getBoolean(PreferenceKeys.HIDE_TRENDING_PAGE, false)
|
||||
if (hideTrendingPage) {
|
||||
binding.bottomNav.menu.findItem(R.id.homeFragment).isVisible =
|
||||
false
|
||||
}
|
||||
|
||||
// save start tab fragment id
|
||||
startFragmentId =
|
||||
when (PreferenceHelper.getString(PreferenceKeys.DEFAULT_TAB, "home")) {
|
||||
"home" -> R.id.homeFragment
|
||||
"subscriptions" -> R.id.subscriptionsFragment
|
||||
"library" -> R.id.libraryFragment
|
||||
else -> R.id.homeFragment
|
||||
}
|
||||
startFragmentId = NavBarHelper.applyNavBarStyle(binding.bottomNav)
|
||||
|
||||
// set default tab as start fragment
|
||||
navController.graph.setStartDestination(startFragmentId)
|
||||
@ -115,16 +101,6 @@ class MainActivity : BaseActivity() {
|
||||
// navigate to the default fragment
|
||||
navController.navigate(startFragmentId)
|
||||
|
||||
val labelVisibilityMode = when (
|
||||
PreferenceHelper.getString(PreferenceKeys.LABEL_VISIBILITY, "always")
|
||||
) {
|
||||
"always" -> NavigationBarView.LABEL_VISIBILITY_LABELED
|
||||
"selected" -> NavigationBarView.LABEL_VISIBILITY_SELECTED
|
||||
"never" -> NavigationBarView.LABEL_VISIBILITY_UNLABELED
|
||||
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
|
||||
}
|
||||
binding.bottomNav.labelVisibilityMode = labelVisibilityMode
|
||||
|
||||
binding.bottomNav.setOnApplyWindowInsetsListener(null)
|
||||
|
||||
binding.bottomNav.setOnItemSelectedListener {
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.github.libretube.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.NavOptionsItemBinding
|
||||
import com.github.libretube.obj.NavBarItem
|
||||
|
||||
class NavBarOptionsAdapter(
|
||||
val items: MutableList<NavBarItem>
|
||||
) : RecyclerView.Adapter<NavBarOptionsViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NavBarOptionsViewHolder {
|
||||
val binding = NavOptionsItemBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return NavBarOptionsViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: NavBarOptionsViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
holder.binding.apply {
|
||||
title.text = root.context.getString(item.titleResource)
|
||||
checkbox.isChecked = item.isEnabled
|
||||
checkbox.setOnClickListener {
|
||||
if (!checkbox.isChecked && getEnabledItemsCount() < 2) {
|
||||
checkbox.isChecked = true
|
||||
Toast.makeText(
|
||||
root.context,
|
||||
R.string.select_at_least_one,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
items[position].isEnabled = checkbox.isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEnabledItemsCount(): Int {
|
||||
return items.filter { it.isEnabled }.size
|
||||
}
|
||||
}
|
||||
|
||||
class NavBarOptionsViewHolder(
|
||||
val binding: NavOptionsItemBinding
|
||||
) : RecyclerView.ViewHolder(binding.root)
|
@ -20,6 +20,7 @@ object PreferenceKeys {
|
||||
const val BREAK_REMINDER_TOGGLE = "break_reminder_toggle"
|
||||
const val BREAK_REMINDER = "break_reminder"
|
||||
const val SAVE_FEED = "save_feed"
|
||||
const val NAVBAR_ITEMS = "nav_bar_items"
|
||||
|
||||
/**
|
||||
* Appearance
|
||||
@ -30,7 +31,6 @@ object PreferenceKeys {
|
||||
const val GRID_COLUMNS = "grid"
|
||||
const val DEFAULT_TAB = "default_tab"
|
||||
const val LABEL_VISIBILITY = "label_visibility"
|
||||
const val HIDE_TRENDING_PAGE = "hide_trending_page"
|
||||
const val APP_ICON = "icon_change"
|
||||
const val LEGACY_SUBSCRIPTIONS = "legacy_subscriptions"
|
||||
const val LEGACY_SUBSCRIPTIONS_COLUMNS = "legacy_subscriptions_columns"
|
||||
|
@ -0,0 +1,72 @@
|
||||
package com.github.libretube.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.adapters.NavBarOptionsAdapter
|
||||
import com.github.libretube.databinding.DialogNavbarOptionsBinding
|
||||
import com.github.libretube.util.NavBarHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class NavBarOptionsDialog : DialogFragment() {
|
||||
private lateinit var binding: DialogNavbarOptionsBinding
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogNavbarOptionsBinding.inflate(layoutInflater)
|
||||
|
||||
val options = NavBarHelper.getNavBarItems()
|
||||
|
||||
val adapter = NavBarOptionsAdapter(options.toMutableList())
|
||||
|
||||
val itemTouchCallback = object : ItemTouchHelper.Callback() {
|
||||
override fun getMovementFlags(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int {
|
||||
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
|
||||
return makeMovementFlags(dragFlags, 0)
|
||||
}
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
val itemToMove = adapter.items[viewHolder.absoluteAdapterPosition]
|
||||
adapter.items.remove(itemToMove)
|
||||
adapter.items.add(target.absoluteAdapterPosition, itemToMove)
|
||||
|
||||
adapter.notifyItemMoved(
|
||||
viewHolder.absoluteAdapterPosition,
|
||||
target.absoluteAdapterPosition
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
binding.optionsRecycler.layoutManager = LinearLayoutManager(context)
|
||||
binding.optionsRecycler.adapter = adapter
|
||||
|
||||
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
|
||||
itemTouchHelper.attachToRecyclerView(binding.optionsRecycler)
|
||||
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.navigation_bar)
|
||||
.setView(binding.root)
|
||||
.setPositiveButton(R.string.okay) { _, _ ->
|
||||
NavBarHelper.setNavBarItems(adapter.items)
|
||||
RequireRestartDialog()
|
||||
.show(requireParentFragment().childFragmentManager, null)
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
}
|
7
app/src/main/java/com/github/libretube/obj/NavBarItem.kt
Normal file
7
app/src/main/java/com/github/libretube/obj/NavBarItem.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package com.github.libretube.obj
|
||||
|
||||
data class NavBarItem(
|
||||
val id: Int = 0,
|
||||
val titleResource: Int = 0,
|
||||
var isEnabled: Boolean = true
|
||||
)
|
@ -3,10 +3,12 @@ package com.github.libretube.preferences
|
||||
import android.os.Bundle
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.SettingsActivity
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.dialogs.NavBarOptionsDialog
|
||||
import com.github.libretube.dialogs.RequireRestartDialog
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.views.MaterialPreferenceFragment
|
||||
@ -33,10 +35,12 @@ class GeneralSettings : MaterialPreferenceFragment() {
|
||||
true
|
||||
}
|
||||
|
||||
val hideTrending = findPreference<SwitchPreferenceCompat>(PreferenceKeys.HIDE_TRENDING_PAGE)
|
||||
hideTrending?.setOnPreferenceChangeListener { _, _ ->
|
||||
val restartDialog = RequireRestartDialog()
|
||||
restartDialog.show(childFragmentManager, RequireRestartDialog::class.java.name)
|
||||
val navBarOptions = findPreference<Preference>(PreferenceKeys.NAVBAR_ITEMS)
|
||||
navBarOptions?.setOnPreferenceClickListener {
|
||||
NavBarOptionsDialog().show(
|
||||
childFragmentManager,
|
||||
null
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
|
95
app/src/main/java/com/github/libretube/util/NavBarHelper.kt
Normal file
95
app/src/main/java/com/github/libretube/util/NavBarHelper.kt
Normal file
@ -0,0 +1,95 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.obj.NavBarItem
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.navigation.NavigationBarView
|
||||
|
||||
object NavBarHelper {
|
||||
val preferenceKey = "nav_bar_items"
|
||||
|
||||
val defaultNavBarItems = listOf(
|
||||
NavBarItem(
|
||||
R.id.homeFragment,
|
||||
R.string.startpage
|
||||
),
|
||||
NavBarItem(
|
||||
R.id.subscriptionsFragment,
|
||||
R.string.subscriptions
|
||||
),
|
||||
NavBarItem(
|
||||
R.id.libraryFragment,
|
||||
R.string.library
|
||||
)
|
||||
)
|
||||
|
||||
val mapper = ObjectMapper()
|
||||
|
||||
fun getNavBarItems(): List<NavBarItem> {
|
||||
return try {
|
||||
val type = object : TypeReference<List<NavBarItem>>() {}
|
||||
mapper.readValue(
|
||||
PreferenceHelper.getString(
|
||||
preferenceKey,
|
||||
""
|
||||
),
|
||||
type
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return defaultNavBarItems
|
||||
}
|
||||
}
|
||||
|
||||
fun setNavBarItems(items: List<NavBarItem>) {
|
||||
PreferenceHelper.putString(
|
||||
preferenceKey,
|
||||
mapper.writeValueAsString(items)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the bottom navigation style configured in the preferences
|
||||
* @return Id of the start fragment
|
||||
*/
|
||||
fun applyNavBarStyle(bottomNav: BottomNavigationView): Int {
|
||||
val labelVisibilityMode = when (
|
||||
PreferenceHelper.getString(PreferenceKeys.LABEL_VISIBILITY, "always")
|
||||
) {
|
||||
"always" -> NavigationBarView.LABEL_VISIBILITY_LABELED
|
||||
"selected" -> NavigationBarView.LABEL_VISIBILITY_SELECTED
|
||||
"never" -> NavigationBarView.LABEL_VISIBILITY_UNLABELED
|
||||
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
|
||||
}
|
||||
bottomNav.labelVisibilityMode = labelVisibilityMode
|
||||
|
||||
val navBarItems = getNavBarItems()
|
||||
|
||||
val menuItems = mutableListOf<MenuItem>()
|
||||
// remove the old items
|
||||
navBarItems.forEach {
|
||||
menuItems.add(
|
||||
bottomNav.menu.findItem(it.id)
|
||||
)
|
||||
bottomNav.menu.removeItem(it.id)
|
||||
}
|
||||
|
||||
navBarItems.forEach { navBarItem ->
|
||||
if (navBarItem.isEnabled) {
|
||||
val menuItem = menuItems.filter { it.itemId == navBarItem.id }[0]
|
||||
|
||||
bottomNav.menu.add(
|
||||
menuItem.groupId,
|
||||
menuItem.itemId,
|
||||
Menu.NONE,
|
||||
menuItem.title
|
||||
).icon = menuItem.icon
|
||||
}
|
||||
}
|
||||
return navBarItems[0].id
|
||||
}
|
||||
}
|
12
app/src/main/res/drawable/ic_drag.xml
Normal file
12
app/src/main/res/drawable/ic_drag.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,20Q8.175,20 7.588,19.413Q7,18.825 7,18Q7,17.175 7.588,16.587Q8.175,16 9,16Q9.825,16 10.413,16.587Q11,17.175 11,18Q11,18.825 10.413,19.413Q9.825,20 9,20ZM15,20Q14.175,20 13.588,19.413Q13,18.825 13,18Q13,17.175 13.588,16.587Q14.175,16 15,16Q15.825,16 16.413,16.587Q17,17.175 17,18Q17,18.825 16.413,19.413Q15.825,20 15,20ZM9,14Q8.175,14 7.588,13.412Q7,12.825 7,12Q7,11.175 7.588,10.587Q8.175,10 9,10Q9.825,10 10.413,10.587Q11,11.175 11,12Q11,12.825 10.413,13.412Q9.825,14 9,14ZM15,14Q14.175,14 13.588,13.412Q13,12.825 13,12Q13,11.175 13.588,10.587Q14.175,10 15,10Q15.825,10 16.413,10.587Q17,11.175 17,12Q17,12.825 16.413,13.412Q15.825,14 15,14ZM9,8Q8.175,8 7.588,7.412Q7,6.825 7,6Q7,5.175 7.588,4.588Q8.175,4 9,4Q9.825,4 10.413,4.588Q11,5.175 11,6Q11,6.825 10.413,7.412Q9.825,8 9,8ZM15,8Q14.175,8 13.588,7.412Q13,6.825 13,6Q13,5.175 13.588,4.588Q14.175,4 15,4Q15.825,4 16.413,4.588Q17,5.175 17,6Q17,6.825 16.413,7.412Q15.825,8 15,8Z"
|
||||
tools:ignore="VectorPath" />
|
||||
</vector>
|
12
app/src/main/res/layout/dialog_navbar_options.xml
Normal file
12
app/src/main/res/layout/dialog_navbar_options.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/options_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
50
app/src/main/res/layout/nav_options_item.xml
Normal file
50
app/src/main/res/layout/nav_options_item.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
tools:gravity="start|center_vertical">
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/checkbox"
|
||||
style="@style/Widget.Material3.CompoundButton.CheckBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:gravity="center_vertical" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="18sp"
|
||||
tools:text="Item" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/drag_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:padding="16dp"
|
||||
android:tintMode="src_in"
|
||||
app:srcCompat="@drawable/ic_drag"
|
||||
app:tint="?attr/colorControlNormal"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</LinearLayout>
|
@ -330,6 +330,8 @@
|
||||
<string name="save_feed">Load feed in background</string>
|
||||
<string name="save_feed_summary">Load the subscription feed in the background and prevent it from being auto-refreshed.</string>
|
||||
<string name="play_next">Play next</string>
|
||||
<string name="navigation_bar">Navigation bar</string>
|
||||
<string name="select_at_least_one">Please select at least one item</string>
|
||||
|
||||
<!-- Notification channel strings -->
|
||||
<string name="download_channel_name">Download Service</string>
|
||||
|
@ -43,21 +43,10 @@
|
||||
|
||||
<PreferenceCategory app:title="@string/behavior">
|
||||
|
||||
<ListPreference
|
||||
<Preference
|
||||
android:icon="@drawable/ic_home"
|
||||
app:defaultValue="home"
|
||||
app:entries="@array/tabs"
|
||||
app:entryValues="@array/tabsValue"
|
||||
app:key="default_tab"
|
||||
app:title="@string/defaultTab"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:icon="@drawable/ic_trending"
|
||||
app:defaultValue="false"
|
||||
app:key="hide_trending_page"
|
||||
app:title="@string/hideTrendingPage"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:key="nav_bar_items"
|
||||
app:title="@string/navigation_bar" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user