Merge pull request #1333 from Bnyro/master

Option to customize the order of navigation bar items
This commit is contained in:
Bnyro 2022-09-20 19:07:17 +02:00 committed by GitHub
commit 6951cf53cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 319 additions and 45 deletions

View File

@ -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 {

View File

@ -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)

View File

@ -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"

View File

@ -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()
}
}

View 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
)

View File

@ -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
}

View 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
}
}

View 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>

View 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>

View 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>

View File

@ -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>

View File

@ -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>