diff --git a/app/src/main/java/com/github/libretube/activities/MainActivity.kt b/app/src/main/java/com/github/libretube/activities/MainActivity.kt index 4ab7c76a4..27c49c31a 100644 --- a/app/src/main/java/com/github/libretube/activities/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/activities/MainActivity.kt @@ -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 { diff --git a/app/src/main/java/com/github/libretube/adapters/NavBarOptionsAdapter.kt b/app/src/main/java/com/github/libretube/adapters/NavBarOptionsAdapter.kt new file mode 100644 index 000000000..a419cb466 --- /dev/null +++ b/app/src/main/java/com/github/libretube/adapters/NavBarOptionsAdapter.kt @@ -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 +) : RecyclerView.Adapter() { + + 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) diff --git a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt index 8d572348c..80f738b02 100644 --- a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt +++ b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt @@ -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" diff --git a/app/src/main/java/com/github/libretube/dialogs/NavBarOptionsDialog.kt b/app/src/main/java/com/github/libretube/dialogs/NavBarOptionsDialog.kt new file mode 100644 index 000000000..3eebad887 --- /dev/null +++ b/app/src/main/java/com/github/libretube/dialogs/NavBarOptionsDialog.kt @@ -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() + } +} diff --git a/app/src/main/java/com/github/libretube/obj/NavBarItem.kt b/app/src/main/java/com/github/libretube/obj/NavBarItem.kt new file mode 100644 index 000000000..f8b8eec14 --- /dev/null +++ b/app/src/main/java/com/github/libretube/obj/NavBarItem.kt @@ -0,0 +1,7 @@ +package com.github.libretube.obj + +data class NavBarItem( + val id: Int = 0, + val titleResource: Int = 0, + var isEnabled: Boolean = true +) diff --git a/app/src/main/java/com/github/libretube/preferences/GeneralSettings.kt b/app/src/main/java/com/github/libretube/preferences/GeneralSettings.kt index 6393dff6e..b9bc731a4 100644 --- a/app/src/main/java/com/github/libretube/preferences/GeneralSettings.kt +++ b/app/src/main/java/com/github/libretube/preferences/GeneralSettings.kt @@ -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(PreferenceKeys.HIDE_TRENDING_PAGE) - hideTrending?.setOnPreferenceChangeListener { _, _ -> - val restartDialog = RequireRestartDialog() - restartDialog.show(childFragmentManager, RequireRestartDialog::class.java.name) + val navBarOptions = findPreference(PreferenceKeys.NAVBAR_ITEMS) + navBarOptions?.setOnPreferenceClickListener { + NavBarOptionsDialog().show( + childFragmentManager, + null + ) true } diff --git a/app/src/main/java/com/github/libretube/util/NavBarHelper.kt b/app/src/main/java/com/github/libretube/util/NavBarHelper.kt new file mode 100644 index 000000000..c99906b88 --- /dev/null +++ b/app/src/main/java/com/github/libretube/util/NavBarHelper.kt @@ -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 { + return try { + val type = object : TypeReference>() {} + mapper.readValue( + PreferenceHelper.getString( + preferenceKey, + "" + ), + type + ) + } catch (e: Exception) { + return defaultNavBarItems + } + } + + fun setNavBarItems(items: List) { + 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() + // 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 + } +} diff --git a/app/src/main/res/drawable/ic_drag.xml b/app/src/main/res/drawable/ic_drag.xml new file mode 100644 index 000000000..0058d2571 --- /dev/null +++ b/app/src/main/res/drawable/ic_drag.xml @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_navbar_options.xml b/app/src/main/res/layout/dialog_navbar_options.xml new file mode 100644 index 000000000..9c7d17286 --- /dev/null +++ b/app/src/main/res/layout/dialog_navbar_options.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/nav_options_item.xml b/app/src/main/res/layout/nav_options_item.xml new file mode 100644 index 000000000..b0acb177a --- /dev/null +++ b/app/src/main/res/layout/nav_options_item.xml @@ -0,0 +1,50 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b49c26d4e..c9e82e5c2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -330,6 +330,8 @@ Load feed in background Load the subscription feed in the background and prevent it from being auto-refreshed. Play next + Navigation bar + Please select at least one item Download Service diff --git a/app/src/main/res/xml/general_settings.xml b/app/src/main/res/xml/general_settings.xml index 67649f076..dd9e7e7bb 100644 --- a/app/src/main/res/xml/general_settings.xml +++ b/app/src/main/res/xml/general_settings.xml @@ -43,21 +43,10 @@ - - - + app:key="nav_bar_items" + app:title="@string/navigation_bar" />