From 0992c64d268c185dd7719ed35aa9f2e58cd8abeb Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 10:41:32 -0500 Subject: [PATCH 01/16] Add new tools layout --- .../ExperimentationFragment.kt | 132 ++++++++++++++++++ .../quickactions/ToolsQuickActionBinder.kt | 36 +++++ .../kylecorry/trail_sense/tools/ui/Tools.kt | 7 +- app/src/main/res/layout/fragment_tools_2.xml | 97 +++++++++++++ app/src/main/res/navigation/nav_graph.xml | 4 + 5 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/kylecorry/trail_sense/experimentation/ExperimentationFragment.kt create mode 100644 app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt create mode 100644 app/src/main/res/layout/fragment_tools_2.xml diff --git a/app/src/main/java/com/kylecorry/trail_sense/experimentation/ExperimentationFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/experimentation/ExperimentationFragment.kt new file mode 100644 index 000000000..f770e3fab --- /dev/null +++ b/app/src/main/java/com/kylecorry/trail_sense/experimentation/ExperimentationFragment.kt @@ -0,0 +1,132 @@ +package com.kylecorry.trail_sense.experimentation + +import android.content.res.ColorStateList +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.TextView +import androidx.core.view.children +import androidx.core.view.setMargins +import androidx.gridlayout.widget.GridLayout +import androidx.navigation.fragment.findNavController +import com.kylecorry.andromeda.core.system.Resources +import com.kylecorry.andromeda.core.ui.setCompoundDrawables +import com.kylecorry.andromeda.fragments.BoundFragment +import com.kylecorry.trail_sense.R +import com.kylecorry.trail_sense.databinding.FragmentTools2Binding +import com.kylecorry.trail_sense.quickactions.ToolsQuickActionBinder +import com.kylecorry.trail_sense.shared.CustomUiUtils +import com.kylecorry.trail_sense.shared.extensions.setOnQueryTextListener +import com.kylecorry.trail_sense.shared.views.TileButton +import com.kylecorry.trail_sense.tools.ui.Tool +import com.kylecorry.trail_sense.tools.ui.Tools + +class ExperimentationFragment : BoundFragment() { + + private val tools by lazy { Tools.getTools(requireContext()).flatMap { it.tools } } + + override fun generateBinding( + layoutInflater: LayoutInflater, container: ViewGroup? + ): FragmentTools2Binding { + return FragmentTools2Binding.inflate(layoutInflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.quickActions.children.forEach { + if (it is ImageButton) { + CustomUiUtils.setButtonState(it, false) + } + } + + updatePinnedTools() + updateTools() + + updateQuickActions() + + binding.settingsBtn.setOnClickListener { + findNavController().navigate(R.id.action_settings) + } + + binding.searchbox.setOnQueryTextListener { _, _ -> + updateTools() + true + } + } + + private fun updateQuickActions(){ + ToolsQuickActionBinder(this, binding).bind() + } + + private fun updateTools() { + val filter = binding.searchbox.query + + val tools = if (filter.isNullOrBlank()) { + this.tools + } else { + this.tools.filter { + it.name.contains(filter, true) || it.description?.contains(filter, true) == true + } + }.sortedBy { it.name } + + populateTools(tools, binding.tools) + } + + private fun updatePinnedTools() { + val pinnedTools = listOf( + R.id.action_navigation, R.id.action_weather, R.id.action_astronomy, R.id.action_settings + ) + val pinned = tools.filter { + it.navAction in pinnedTools + } + + populateTools(pinned.sortedBy { it.name }, binding.pinned) + } + + private fun populateTools(tools: List, grid: GridLayout) { + grid.removeAllViews() + val iconSize = Resources.dp(requireContext(), 24f).toInt() + val iconPadding = Resources.dp(requireContext(), 16f).toInt() + val iconColor = Resources.androidTextColorPrimary(requireContext()) + val buttonHeight = Resources.dp(requireContext(), 64f).toInt() + val buttonMargins = Resources.dp(requireContext(), 8f).toInt() + val buttonPadding = Resources.dp(requireContext(), 16f).toInt() + val buttonBackgroundColor = Resources.getAndroidColorAttr( + requireContext(), android.R.attr.colorBackgroundFloating + ) + + val gridColumnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) + val gridRowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) + + tools.forEach { + val button = TextView(requireContext()) + button.text = it.name + button.setCompoundDrawables(iconSize, left = it.icon) + button.compoundDrawablePadding = iconPadding + CustomUiUtils.setImageColor(button, iconColor) + button.layoutParams = GridLayout.LayoutParams().apply { + width = 0 + height = buttonHeight + columnSpec = gridColumnSpec + rowSpec = gridRowSpec + setMargins(buttonMargins) + } + button.gravity = Gravity.CENTER_VERTICAL + button.setPadding(buttonPadding, 0, buttonPadding, 0) + + button.setBackgroundResource(R.drawable.rounded_rectangle) + button.backgroundTintList = ColorStateList.valueOf(buttonBackgroundColor) + button.isClickable = true + button.setOnClickListener { _ -> + findNavController().navigate(it.navAction) + } + + grid.addView(button) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt new file mode 100644 index 000000000..09e41f0ad --- /dev/null +++ b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt @@ -0,0 +1,36 @@ +package com.kylecorry.trail_sense.quickactions + +import android.widget.ImageButton +import androidx.core.view.setMargins +import com.google.android.flexbox.FlexboxLayout +import com.kylecorry.andromeda.core.system.Resources +import com.kylecorry.trail_sense.R +import com.kylecorry.trail_sense.databinding.FragmentTools2Binding +import com.kylecorry.trail_sense.experimentation.ExperimentationFragment + +class ToolsQuickActionBinder(private val fragment: ExperimentationFragment, private val binding: FragmentTools2Binding) : IQuickActionBinder { + + private fun createButton(): ImageButton { + val size = Resources.dp(fragment.requireContext(), 40f).toInt() + val margins = Resources.dp(fragment.requireContext(), 8f).toInt() + val button = ImageButton(fragment.requireContext()) + button.layoutParams = FlexboxLayout.LayoutParams(size, size).apply { + setMargins(margins) + } + button.background = Resources.drawable(fragment.requireContext(), R.drawable.rounded_rectangle) + button.elevation = 2f + + binding.quickActions.addView(button) + + return button + } + + override fun bind() { + binding.quickActions.removeAllViews() + QuickActionBacktrack(createButton(), fragment).bind(fragment.viewLifecycleOwner) + QuickActionFlashlight(createButton(), fragment).bind(fragment.viewLifecycleOwner) + QuickActionWhistle(createButton(), fragment).bind(fragment.viewLifecycleOwner) + LowPowerQuickAction(createButton(), fragment).bind(fragment.viewLifecycleOwner) + QuickActionSunsetAlert(createButton(), fragment).bind(fragment.viewLifecycleOwner) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt index 711b0cee6..6d1fad231 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt @@ -248,7 +248,12 @@ object Tools { R.drawable.ic_user_guide, R.id.action_action_experimental_tools_to_guideListFragment, context.getString(R.string.tool_user_guide_summary) - ) + ), + if (isDebug()) Tool( + "Experimentation", + R.drawable.ic_experimental, + R.id.experimentationFragment + ) else null ) ) diff --git a/app/src/main/res/layout/fragment_tools_2.xml b/app/src/main/res/layout/fragment_tools_2.xml new file mode 100644 index 000000000..1285840e8 --- /dev/null +++ b/app/src/main/res/layout/fragment_tools_2.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 7b1ec868d..d627b2bd1 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -412,6 +412,10 @@ app:popEnterAnim="@anim/slide_in_left" app:popExitAnim="@anim/slide_out_right" /> + Date: Sat, 11 Nov 2023 10:53:29 -0500 Subject: [PATCH 02/16] Remove settings from bottom nav --- .../quickactions/ToolsQuickActionBinder.kt | 11 +- .../trail_sense/tools/ui/ToolsFragment.kt | 203 +++++++++--------- app/src/main/res/drawable/apps.xml | 10 + .../main/res/menu/bottom_navigation_menu.xml | 9 +- 4 files changed, 116 insertions(+), 117 deletions(-) create mode 100644 app/src/main/res/drawable/apps.xml diff --git a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt index 09e41f0ad..187cd90ea 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt @@ -4,11 +4,14 @@ import android.widget.ImageButton import androidx.core.view.setMargins import com.google.android.flexbox.FlexboxLayout import com.kylecorry.andromeda.core.system.Resources +import com.kylecorry.andromeda.fragments.AndromedaFragment import com.kylecorry.trail_sense.R import com.kylecorry.trail_sense.databinding.FragmentTools2Binding -import com.kylecorry.trail_sense.experimentation.ExperimentationFragment -class ToolsQuickActionBinder(private val fragment: ExperimentationFragment, private val binding: FragmentTools2Binding) : IQuickActionBinder { +class ToolsQuickActionBinder( + private val fragment: AndromedaFragment, + private val binding: FragmentTools2Binding +) : IQuickActionBinder { private fun createButton(): ImageButton { val size = Resources.dp(fragment.requireContext(), 40f).toInt() @@ -17,7 +20,8 @@ class ToolsQuickActionBinder(private val fragment: ExperimentationFragment, priv button.layoutParams = FlexboxLayout.LayoutParams(size, size).apply { setMargins(margins) } - button.background = Resources.drawable(fragment.requireContext(), R.drawable.rounded_rectangle) + button.background = + Resources.drawable(fragment.requireContext(), R.drawable.rounded_rectangle) button.elevation = 2f binding.quickActions.addView(button) @@ -32,5 +36,6 @@ class ToolsQuickActionBinder(private val fragment: ExperimentationFragment, priv QuickActionWhistle(createButton(), fragment).bind(fragment.viewLifecycleOwner) LowPowerQuickAction(createButton(), fragment).bind(fragment.viewLifecycleOwner) QuickActionSunsetAlert(createButton(), fragment).bind(fragment.viewLifecycleOwner) + QuickActionWhiteNoise(createButton(), fragment).bind(fragment.viewLifecycleOwner) } } \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index 502c7a1ec..b523b4228 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -1,140 +1,129 @@ package com.kylecorry.trail_sense.tools.ui +import android.content.res.ColorStateList import android.os.Bundle +import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.annotation.DrawableRes -import androidx.annotation.IdRes -import androidx.appcompat.widget.SearchView -import androidx.core.view.isVisible +import android.widget.ImageButton +import android.widget.TextView +import androidx.core.view.children +import androidx.core.view.setMargins +import androidx.gridlayout.widget.GridLayout import androidx.navigation.fragment.findNavController -import com.kylecorry.andromeda.core.capitalizeWords import com.kylecorry.andromeda.core.system.Resources -import com.kylecorry.andromeda.core.tryOrLog -import com.kylecorry.andromeda.core.tryOrNothing -import com.kylecorry.andromeda.core.ui.Colors +import com.kylecorry.andromeda.core.ui.setCompoundDrawables import com.kylecorry.andromeda.fragments.BoundFragment -import com.kylecorry.andromeda.list.ListView import com.kylecorry.trail_sense.R -import com.kylecorry.trail_sense.databinding.FragmentToolsBinding -import com.kylecorry.trail_sense.databinding.ListItemToolBinding +import com.kylecorry.trail_sense.databinding.FragmentTools2Binding +import com.kylecorry.trail_sense.quickactions.ToolsQuickActionBinder +import com.kylecorry.trail_sense.shared.CustomUiUtils +import com.kylecorry.trail_sense.shared.extensions.setOnQueryTextListener +class ToolsFragment : BoundFragment() { -class ToolsFragment : BoundFragment() { + private val tools by lazy { Tools.getTools(requireContext()).flatMap { it.tools } } - private lateinit var toolsList: ListView - private val tools by lazy { Tools.getTools(requireContext()) } + override fun generateBinding( + layoutInflater: LayoutInflater, container: ViewGroup? + ): FragmentTools2Binding { + return FragmentTools2Binding.inflate(layoutInflater, container, false) + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val primaryColor = Resources.getAndroidColorAttr(requireContext(), androidx.appcompat.R.attr.colorPrimary) - val textColor = Resources.androidTextColorPrimary(requireContext()) - val attrs = intArrayOf(android.R.attr.selectableItemBackground) - val typedArray = requireContext().obtainStyledAttributes(attrs) - val selectableBackground = typedArray.getResourceId(0, 0) - typedArray.recycle() - toolsList = ListView(binding.toolRecycler, R.layout.list_item_tool) { itemView, tool -> - val toolBinding = ListItemToolBinding.bind(itemView) - - if (tool.action != null && tool.icon != null) { - // Tool - toolBinding.root.setBackgroundResource(selectableBackground) - toolBinding.title.text = tool.name.capitalizeWords() - toolBinding.title.setTextColor(textColor) - toolBinding.description.text = tool.description - toolBinding.icon.isVisible = true - toolBinding.description.isVisible = tool.description != null - toolBinding.icon.setImageResource(tool.icon) - Colors.setImageColor(toolBinding.icon, Resources.androidTextColorSecondary(requireContext())) - toolBinding.root.setOnClickListener { - tryOrLog { - findNavController().navigate(tool.action) - } - } - } else { - // Tool group - toolBinding.root.setBackgroundResource(0) - toolBinding.title.text = tool.name - toolBinding.title.setTextColor(primaryColor) - toolBinding.description.text = "" - toolBinding.icon.isVisible = false - toolBinding.description.isVisible = false - toolBinding.root.setOnClickListener(null) - } + binding.quickActions.children.forEach { + if (it is ImageButton) { + CustomUiUtils.setButtonState(it, false) + } } - binding.searchbox.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String?): Boolean { - updateToolList() - return true - } + updatePinnedTools() + updateTools() - override fun onQueryTextChange(newText: String?): Boolean { - updateToolList() - return true - } + updateQuickActions() - }) + binding.settingsBtn.setOnClickListener { + findNavController().navigate(R.id.action_settings) + } - updateToolList() + binding.searchbox.setOnQueryTextListener { _, _ -> + updateTools() + true + } } - private fun updateToolList() { - val toolListItems = mutableListOf() - val search = binding.searchbox.query - - if (search.isNullOrBlank()) { - for (group in tools) { - toolListItems.add(ToolListItem(group.name, null, null, null)) - for (tool in group.tools) { - toolListItems.add( - ToolListItem( - tool.name, - tool.description, - tool.icon, - tool.navAction - ) - ) - } - } + private fun updateQuickActions(){ + ToolsQuickActionBinder(this, binding).bind() + } + + private fun updateTools() { + val filter = binding.searchbox.query + + val tools = if (filter.isNullOrBlank()) { + this.tools } else { - for (group in tools) { - for (tool in group.tools) { - if (tool.name.contains(search, true) || tool.description?.contains( - search, - true - ) == true - ) { - toolListItems.add( - ToolListItem( - tool.name, - tool.description, - tool.icon, - tool.navAction - ) - ) - } - } + this.tools.filter { + it.name.contains(filter, true) || it.description?.contains(filter, true) == true } - } + }.sortedBy { it.name } - toolsList.setData(toolListItems) + populateTools(tools, binding.tools) } - override fun generateBinding( - layoutInflater: LayoutInflater, - container: ViewGroup? - ): FragmentToolsBinding { - return FragmentToolsBinding.inflate(layoutInflater, container, false) + private fun updatePinnedTools() { + val pinnedTools = listOf( + R.id.action_navigation, R.id.action_weather, R.id.action_astronomy, R.id.action_settings + ) + val pinned = tools.filter { + it.navAction in pinnedTools + } + + populateTools(pinned.sortedBy { it.name }, binding.pinned) } - internal data class ToolListItem( - val name: String, - val description: String?, - @DrawableRes val icon: Int?, - @IdRes val action: Int? - ) + private fun populateTools(tools: List, grid: GridLayout) { + grid.removeAllViews() + val iconSize = Resources.dp(requireContext(), 24f).toInt() + val iconPadding = Resources.dp(requireContext(), 16f).toInt() + val iconColor = Resources.androidTextColorPrimary(requireContext()) + val buttonHeight = Resources.dp(requireContext(), 64f).toInt() + val buttonMargins = Resources.dp(requireContext(), 8f).toInt() + val buttonPadding = Resources.dp(requireContext(), 16f).toInt() + val buttonBackgroundColor = Resources.getAndroidColorAttr( + requireContext(), android.R.attr.colorBackgroundFloating + ) + + val gridColumnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) + val gridRowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) + + tools.forEach { + val button = TextView(requireContext()) + button.text = it.name + button.setCompoundDrawables(iconSize, left = it.icon) + button.compoundDrawablePadding = iconPadding + CustomUiUtils.setImageColor(button, iconColor) + button.layoutParams = GridLayout.LayoutParams().apply { + width = 0 + height = buttonHeight + columnSpec = gridColumnSpec + rowSpec = gridRowSpec + setMargins(buttonMargins) + } + button.gravity = Gravity.CENTER_VERTICAL + button.setPadding(buttonPadding, 0, buttonPadding, 0) + + button.setBackgroundResource(R.drawable.rounded_rectangle) + button.backgroundTintList = ColorStateList.valueOf(buttonBackgroundColor) + button.isClickable = true + button.setOnClickListener { _ -> + findNavController().navigate(it.navAction) + } + + grid.addView(button) + } + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/apps.xml b/app/src/main/res/drawable/apps.xml new file mode 100644 index 000000000..485e83439 --- /dev/null +++ b/app/src/main/res/drawable/apps.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_navigation_menu.xml b/app/src/main/res/menu/bottom_navigation_menu.xml index 6d3017b26..8845221ab 100644 --- a/app/src/main/res/menu/bottom_navigation_menu.xml +++ b/app/src/main/res/menu/bottom_navigation_menu.xml @@ -21,16 +21,11 @@ android:icon="@drawable/ic_astronomy" app:showAsAction="ifRoom" /> + - - From 679084cf6f611d01ee4657a379246a8959e90ea7 Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 10:56:01 -0500 Subject: [PATCH 03/16] Extract pinned title --- .../ExperimentationFragment.kt | 125 +----------------- .../res/layout/fragment_experimentation.xml | 7 + app/src/main/res/layout/fragment_tools_2.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 4 files changed, 13 insertions(+), 122 deletions(-) create mode 100644 app/src/main/res/layout/fragment_experimentation.xml diff --git a/app/src/main/java/com/kylecorry/trail_sense/experimentation/ExperimentationFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/experimentation/ExperimentationFragment.kt index f770e3fab..6f7265de1 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/experimentation/ExperimentationFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/experimentation/ExperimentationFragment.kt @@ -1,132 +1,15 @@ package com.kylecorry.trail_sense.experimentation -import android.content.res.ColorStateList -import android.os.Bundle -import android.view.Gravity import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup -import android.widget.ImageButton -import android.widget.TextView -import androidx.core.view.children -import androidx.core.view.setMargins -import androidx.gridlayout.widget.GridLayout -import androidx.navigation.fragment.findNavController -import com.kylecorry.andromeda.core.system.Resources -import com.kylecorry.andromeda.core.ui.setCompoundDrawables import com.kylecorry.andromeda.fragments.BoundFragment -import com.kylecorry.trail_sense.R -import com.kylecorry.trail_sense.databinding.FragmentTools2Binding -import com.kylecorry.trail_sense.quickactions.ToolsQuickActionBinder -import com.kylecorry.trail_sense.shared.CustomUiUtils -import com.kylecorry.trail_sense.shared.extensions.setOnQueryTextListener -import com.kylecorry.trail_sense.shared.views.TileButton -import com.kylecorry.trail_sense.tools.ui.Tool -import com.kylecorry.trail_sense.tools.ui.Tools +import com.kylecorry.trail_sense.databinding.FragmentExperimentationBinding -class ExperimentationFragment : BoundFragment() { - - private val tools by lazy { Tools.getTools(requireContext()).flatMap { it.tools } } +class ExperimentationFragment : BoundFragment() { override fun generateBinding( layoutInflater: LayoutInflater, container: ViewGroup? - ): FragmentTools2Binding { - return FragmentTools2Binding.inflate(layoutInflater, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.quickActions.children.forEach { - if (it is ImageButton) { - CustomUiUtils.setButtonState(it, false) - } - } - - updatePinnedTools() - updateTools() - - updateQuickActions() - - binding.settingsBtn.setOnClickListener { - findNavController().navigate(R.id.action_settings) - } - - binding.searchbox.setOnQueryTextListener { _, _ -> - updateTools() - true - } - } - - private fun updateQuickActions(){ - ToolsQuickActionBinder(this, binding).bind() - } - - private fun updateTools() { - val filter = binding.searchbox.query - - val tools = if (filter.isNullOrBlank()) { - this.tools - } else { - this.tools.filter { - it.name.contains(filter, true) || it.description?.contains(filter, true) == true - } - }.sortedBy { it.name } - - populateTools(tools, binding.tools) - } - - private fun updatePinnedTools() { - val pinnedTools = listOf( - R.id.action_navigation, R.id.action_weather, R.id.action_astronomy, R.id.action_settings - ) - val pinned = tools.filter { - it.navAction in pinnedTools - } - - populateTools(pinned.sortedBy { it.name }, binding.pinned) - } - - private fun populateTools(tools: List, grid: GridLayout) { - grid.removeAllViews() - val iconSize = Resources.dp(requireContext(), 24f).toInt() - val iconPadding = Resources.dp(requireContext(), 16f).toInt() - val iconColor = Resources.androidTextColorPrimary(requireContext()) - val buttonHeight = Resources.dp(requireContext(), 64f).toInt() - val buttonMargins = Resources.dp(requireContext(), 8f).toInt() - val buttonPadding = Resources.dp(requireContext(), 16f).toInt() - val buttonBackgroundColor = Resources.getAndroidColorAttr( - requireContext(), android.R.attr.colorBackgroundFloating - ) - - val gridColumnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) - val gridRowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) - - tools.forEach { - val button = TextView(requireContext()) - button.text = it.name - button.setCompoundDrawables(iconSize, left = it.icon) - button.compoundDrawablePadding = iconPadding - CustomUiUtils.setImageColor(button, iconColor) - button.layoutParams = GridLayout.LayoutParams().apply { - width = 0 - height = buttonHeight - columnSpec = gridColumnSpec - rowSpec = gridRowSpec - setMargins(buttonMargins) - } - button.gravity = Gravity.CENTER_VERTICAL - button.setPadding(buttonPadding, 0, buttonPadding, 0) - - button.setBackgroundResource(R.drawable.rounded_rectangle) - button.backgroundTintList = ColorStateList.valueOf(buttonBackgroundColor) - button.isClickable = true - button.setOnClickListener { _ -> - findNavController().navigate(it.navAction) - } - - grid.addView(button) - } + ): FragmentExperimentationBinding { + return FragmentExperimentationBinding.inflate(layoutInflater, container, false) } - } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_experimentation.xml b/app/src/main/res/layout/fragment_experimentation.xml new file mode 100644 index 000000000..d829e291c --- /dev/null +++ b/app/src/main/res/layout/fragment_experimentation.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_tools_2.xml b/app/src/main/res/layout/fragment_tools_2.xml index 1285840e8..9f1a7e3f3 100644 --- a/app/src/main/res/layout/fragment_tools_2.xml +++ b/app/src/main/res/layout/fragment_tools_2.xml @@ -52,7 +52,7 @@ android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" - android:text="Pinned" + android:text="@string/pinned" android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" /> View nearby beacons and the sun / moon with your camera pref_enable_augmented_reality_tool Magnetic North + Pinned %d map %d maps From 7af52cf8460c95ccd78a10b3c9956d2b3461ab8b Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 11:15:03 -0500 Subject: [PATCH 04/16] Add temporary pinning support --- .../trail_sense/tools/ui/ToolsFragment.kt | 56 +++++++++++++++++-- app/src/main/res/layout/fragment_tools_2.xml | 1 + app/src/main/res/values/strings.xml | 2 + 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index b523b4228..31618857e 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -9,12 +9,15 @@ import android.view.ViewGroup import android.widget.ImageButton import android.widget.TextView import androidx.core.view.children +import androidx.core.view.isVisible import androidx.core.view.setMargins import androidx.gridlayout.widget.GridLayout import androidx.navigation.fragment.findNavController +import com.kylecorry.andromeda.alerts.dialog import com.kylecorry.andromeda.core.system.Resources import com.kylecorry.andromeda.core.ui.setCompoundDrawables import com.kylecorry.andromeda.fragments.BoundFragment +import com.kylecorry.andromeda.pickers.Pickers import com.kylecorry.trail_sense.R import com.kylecorry.trail_sense.databinding.FragmentTools2Binding import com.kylecorry.trail_sense.quickactions.ToolsQuickActionBinder @@ -25,6 +28,13 @@ class ToolsFragment : BoundFragment() { private val tools by lazy { Tools.getTools(requireContext()).flatMap { it.tools } } + // TODO: Give each tool a unique ID + private val pinnedIds = mutableSetOf( + R.id.action_navigation, + R.id.action_weather, + R.id.action_astronomy + ) + override fun generateBinding( layoutInflater: LayoutInflater, container: ViewGroup? ): FragmentTools2Binding { @@ -55,7 +65,7 @@ class ToolsFragment : BoundFragment() { } } - private fun updateQuickActions(){ + private fun updateQuickActions() { ToolsQuickActionBinder(this, binding).bind() } @@ -74,11 +84,17 @@ class ToolsFragment : BoundFragment() { } private fun updatePinnedTools() { - val pinnedTools = listOf( - R.id.action_navigation, R.id.action_weather, R.id.action_astronomy, R.id.action_settings - ) + // TODO: Load pinned list val pinned = tools.filter { - it.navAction in pinnedTools + it.navAction in pinnedIds + } + + if (pinned.isEmpty()) { + binding.pinned.isVisible = false + binding.pinnedTitle.isVisible = false + } else { + binding.pinned.isVisible = true + binding.pinnedTitle.isVisible = true } populateTools(pinned.sortedBy { it.name }, binding.pinned) @@ -117,11 +133,39 @@ class ToolsFragment : BoundFragment() { button.setBackgroundResource(R.drawable.rounded_rectangle) button.backgroundTintList = ColorStateList.valueOf(buttonBackgroundColor) - button.isClickable = true button.setOnClickListener { _ -> findNavController().navigate(it.navAction) } + button.setOnLongClickListener { view -> + Pickers.menu( + view, listOf( + if (it.description != null) getString(R.string.pref_category_about) else null, + if (pinnedIds.contains(it.navAction)) { + getString(R.string.unpin) + } else { + getString(R.string.pin) + } + // TODO: Guide + ) + ) { selectedIdx -> + when (selectedIdx) { + 0 -> dialog(it.name, it.description, cancelText = null) + 1 -> { + // TODO: Save this + if (pinnedIds.contains(it.navAction)) { + pinnedIds.remove(it.navAction) + } else { + pinnedIds.add(it.navAction) + } + updatePinnedTools() + } + } + true + } + true + } + grid.addView(button) } } diff --git a/app/src/main/res/layout/fragment_tools_2.xml b/app/src/main/res/layout/fragment_tools_2.xml index 9f1a7e3f3..67875eda5 100644 --- a/app/src/main/res/layout/fragment_tools_2.xml +++ b/app/src/main/res/layout/fragment_tools_2.xml @@ -48,6 +48,7 @@ android:orientation="vertical"> pref_enable_augmented_reality_tool Magnetic North Pinned + Unpin + Pin %d map %d maps From c148e4b7683b2ecbed078531522ce82c04ef8d9b Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 11:26:52 -0500 Subject: [PATCH 05/16] Add edit button to pinned tools --- .../quickactions/ToolsQuickActionBinder.kt | 1 + .../trail_sense/tools/ui/ToolsFragment.kt | 47 ++++++++++++++++--- app/src/main/res/layout/fragment_tools_2.xml | 37 ++++++++++----- 3 files changed, 65 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt index 187cd90ea..02d89e711 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt @@ -31,6 +31,7 @@ class ToolsQuickActionBinder( override fun bind() { binding.quickActions.removeAllViews() + // TODO: Weather monitor QuickActionBacktrack(createButton(), fragment).bind(fragment.viewLifecycleOwner) QuickActionFlashlight(createButton(), fragment).bind(fragment.viewLifecycleOwner) QuickActionWhistle(createButton(), fragment).bind(fragment.viewLifecycleOwner) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index 31618857e..840893d69 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -63,8 +63,46 @@ class ToolsFragment : BoundFragment() { updateTools() true } + + binding.pinnedEditBtn.setOnClickListener { + // Sort alphabetically, but if the tool is already pinned, put it first + val sorted = tools.sortedBy { tool -> + if (pinnedIds.contains(tool.navAction)) { + "0${tool.name}" + } else { + tool.name + } + } + val toolNames = sorted.map { it.name } + val defaultSelected = sorted.mapIndexedNotNull { index, tool -> + if (pinnedIds.contains(tool.navAction)) { + index + } else { + null + } + } + + Pickers.items( + requireContext(), + getString(R.string.pinned), + toolNames, + defaultSelected + ) { selected -> + if (selected != null) { + // TODO: Save this + pinnedIds.clear() + selected.forEach { + val tool = sorted[it] + pinnedIds.add(tool.navAction) + } + } + + updatePinnedTools() + } + } } + // TODO: Add a way to customize this private fun updateQuickActions() { ToolsQuickActionBinder(this, binding).bind() } @@ -89,13 +127,8 @@ class ToolsFragment : BoundFragment() { it.navAction in pinnedIds } - if (pinned.isEmpty()) { - binding.pinned.isVisible = false - binding.pinnedTitle.isVisible = false - } else { - binding.pinned.isVisible = true - binding.pinnedTitle.isVisible = true - } + // TODO: Show an option to pin tools + binding.pinned.isVisible = pinned.isNotEmpty() populateTools(pinned.sortedBy { it.name }, binding.pinned) } diff --git a/app/src/main/res/layout/fragment_tools_2.xml b/app/src/main/res/layout/fragment_tools_2.xml index 67875eda5..61b16d0f4 100644 --- a/app/src/main/res/layout/fragment_tools_2.xml +++ b/app/src/main/res/layout/fragment_tools_2.xml @@ -11,14 +11,6 @@ android:layout_gravity="end" android:orientation="horizontal"> - - - + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + + Date: Sat, 11 Nov 2023 12:17:29 -0500 Subject: [PATCH 06/16] Improve tool model --- .../kylecorry/trail_sense/tools/ui/Tools.kt | 541 ++++++++++-------- .../trail_sense/tools/ui/ToolsFragment.kt | 43 +- 2 files changed, 323 insertions(+), 261 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt index 6d1fad231..17ef9a0ed 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt @@ -7,266 +7,319 @@ import androidx.annotation.IdRes import com.kylecorry.andromeda.sense.Sensors import com.kylecorry.trail_sense.R import com.kylecorry.trail_sense.shared.UserPreferences +import com.kylecorry.trail_sense.shared.database.Identifiable import com.kylecorry.trail_sense.shared.extensions.isDebug import com.kylecorry.trail_sense.shared.sensors.SensorService -data class ToolGroup(val name: String, val tools: List) data class Tool( + override val id: Long, val name: String, @DrawableRes val icon: Int, @IdRes val navAction: Int, - val description: String? = null -) + val category: ToolCategory, + val description: String? = null, + val guideId: Int? = null, + val isExperimental: Boolean = false +) : Identifiable + +enum class ToolCategory { + Signaling, + Distance, + Location, + Angles, + Time, + Power, + Weather, + Other +} object Tools { - fun getTools(context: Context): List { + fun getTools(context: Context): List { val hasLightMeter = Sensors.hasSensor(context, Sensor.TYPE_LIGHT) val hasPedometer = Sensors.hasSensor(context, Sensor.TYPE_STEP_COUNTER) val hasCompass = SensorService(context).hasCompass() val hasBarometer = Sensors.hasBarometer(context) val prefs = UserPreferences(context) - val signaling = ToolGroup( - context.getString(R.string.tool_category_signaling), listOf( - Tool( - context.getString(R.string.flashlight_title), - R.drawable.flashlight, - R.id.action_action_experimental_tools_to_fragmentToolFlashlight, - ), - Tool( - context.getString(R.string.tool_whistle_title), - R.drawable.ic_tool_whistle, - R.id.action_action_experimental_tools_to_toolWhistleFragment, - ) - ) - ) - val distance = ToolGroup( - context.getString(R.string.distance), listOfNotNull( - Tool( - context.getString(R.string.tool_ruler_title), - R.drawable.ruler, - R.id.action_action_experimental_tools_to_rulerFragment - ), - if (hasPedometer) Tool( - context.getString(R.string.pedometer), - R.drawable.steps, - R.id.action_tools_to_pedometer - ) else null, - if (prefs.isCliffHeightEnabled) Tool( - context.getString(R.string.tool_cliff_height_title), - R.drawable.ic_tool_cliff_height, - R.id.action_action_experimental_tools_to_toolCliffHeightFragment, - context.getString(R.string.experimental) + " - " + context.getString(R.string.tool_cliff_height_description) - ) else null - ) - ) - - val location = ToolGroup( - context.getString(R.string.location), listOfNotNull( - Tool( - context.getString(R.string.navigation), - R.drawable.ic_compass_icon, - R.id.action_navigation - ), - Tool( - context.getString(R.string.beacons), - R.drawable.ic_location, - R.id.action_tools_to_beacons - ), - Tool( - context.getString(R.string.photo_maps), - R.drawable.maps, - R.id.action_tools_to_maps_list, - context.getString(R.string.photo_map_summary) - ), - Tool( - context.getString(R.string.paths), - R.drawable.ic_tool_backtrack, - R.id.action_action_experimental_tools_to_fragmentBacktrack - ), - Tool( - context.getString(R.string.tool_triangulate_title), - R.drawable.ic_tool_triangulate, - R.id.action_action_experimental_tools_to_fragmentToolTriangulate - ), - ) - ) - - val angles = ToolGroup( - context.getString(R.string.tool_category_angles), listOfNotNull( - Tool( - context.getString(R.string.clinometer_title), - R.drawable.clinometer, - R.id.action_toolsFragment_to_clinometerFragment, - context.getString(R.string.tool_clinometer_summary) - ), - Tool( - context.getString(R.string.tool_bubble_level_title), - R.drawable.level, - R.id.action_action_experimental_tools_to_levelFragment, - context.getString(R.string.tool_bubble_level_summary) - ) - ) - ) - val time = ToolGroup( - context.getString(R.string.time), listOfNotNull( - Tool( - context.getString(R.string.tool_clock_title), - R.drawable.ic_tool_clock, - R.id.action_action_experimental_tools_to_toolClockFragment - ), - Tool( - context.getString(R.string.astronomy), - R.drawable.ic_astronomy, - R.id.action_astronomy - ), - Tool( - context.getString(R.string.water_boil_timer), - R.drawable.ic_tool_boil, - R.id.action_action_experimental_tools_to_waterPurificationFragment, - context.getString(R.string.tool_boil_summary) - ), - Tool( - context.getString(R.string.tides), - R.drawable.ic_tide_table, - R.id.action_toolsFragment_to_tidesFragment - ) - ) - ) - - val power = ToolGroup( - context.getString(R.string.power), listOfNotNull( - Tool( - context.getString(R.string.tool_battery_title), - R.drawable.ic_tool_battery, - R.id.action_action_experimental_tools_to_fragmentToolBattery - ), - if (hasCompass) Tool( - context.getString(R.string.tool_solar_panel_title), - R.drawable.ic_tool_solar_panel, - R.id.action_action_experimental_tools_to_fragmentToolSolarPanel, - context.getString(R.string.tool_solar_panel_summary) - ) else null, - if (hasLightMeter) Tool( - context.getString(R.string.tool_light_meter_title), - R.drawable.flashlight, - R.id.action_toolsFragment_to_toolLightFragment, - context.getString(R.string.guide_light_meter_description) - ) else null - ) - ) - - val weather = ToolGroup( - context.getString(R.string.weather), listOfNotNull( - if (hasBarometer) Tool( - context.getString(R.string.weather), - R.drawable.cloud, - R.id.action_weather - ) else null, - Tool( - context.getString(R.string.tool_climate), - R.drawable.ic_temperature_range, - R.id.action_toolsFragment_to_toolClimate, - context.getString(R.string.tool_climate_summary) - ), - Tool( - context.getString(R.string.tool_temperature_estimation_title), - R.drawable.thermometer, - R.id.action_tools_to_temperature_estimation, - context.getString(R.string.tool_temperature_estimation_description) - ), - Tool( - context.getString(R.string.clouds), - R.drawable.ic_tool_clouds, - R.id.action_action_experimental_tools_to_cloudFragment - ), - Tool( - context.getString(R.string.tool_lightning_title), - R.drawable.ic_torch_on, - R.id.action_action_experimental_tools_to_fragmentToolLightning, - context.getString(R.string.tool_lightning_description) - ) - ) - ) - - val other = ToolGroup( - context.getString(R.string.other), listOfNotNull( - if (prefs.isAugmentedRealityEnabled && hasCompass) Tool( - context.getString(R.string.augmented_reality), - R.drawable.ic_camera, - R.id.action_tools_to_augmented_reality, - context.getString(R.string.augmented_reality_description) - ) else null, - Tool( - context.getString(R.string.convert), - R.drawable.ic_tool_distance_convert, - R.id.action_toolsFragment_to_toolConvertFragment - ), - Tool( - context.getString(R.string.packing_lists), - R.drawable.ic_tool_pack, - R.id.action_action_experimental_tools_to_action_inventory - ), - if (hasCompass) Tool( - context.getString(R.string.tool_metal_detector_title), - R.drawable.ic_tool_metal_detector, - R.id.action_action_experimental_tools_to_fragmentToolMetalDetector - ) else null, - Tool( - context.getString(R.string.tool_white_noise_title), - R.drawable.ic_tool_white_noise, - R.id.action_action_experimental_tools_to_fragmentToolWhiteNoise, - context.getString(R.string.tool_white_noise_summary) - ), - Tool( - context.getString(R.string.tool_notes_title), - R.drawable.ic_tool_notes, - R.id.action_action_experimental_tools_to_fragmentToolNotes - ), - Tool( - context.getString(R.string.qr_code_scanner), - R.drawable.ic_qr_code, - R.id.action_tools_to_qr - ), - Tool( - context.getString(R.string.sensors), - R.drawable.ic_sensors, - R.id.sensorDetailsFragment - ), - Tool( - context.getString(R.string.diagnostics), - R.drawable.ic_alert, - R.id.action_tools_to_diagnostics - ), - Tool( - context.getString(R.string.settings), - R.drawable.ic_settings, - R.id.action_settings - ), - Tool( - context.getString(R.string.tool_user_guide_title), - R.drawable.ic_user_guide, - R.id.action_action_experimental_tools_to_guideListFragment, - context.getString(R.string.tool_user_guide_summary) - ), - if (isDebug()) Tool( - "Experimentation", - R.drawable.ic_experimental, - R.id.experimentationFragment - ) else null - ) - ) +// val groupNameMap = mapOf( +// ToolCategory.Signaling to context.getString(R.string.tool_category_signaling), +// ToolCategory.Distance to context.getString(R.string.distance), +// ToolCategory.Location to context.getString(R.string.location), +// ToolCategory.Angles to context.getString(R.string.tool_category_angles), +// ToolCategory.Time to context.getString(R.string.time), +// ToolCategory.Power to context.getString(R.string.power), +// ToolCategory.Weather to context.getString(R.string.weather), +// ToolCategory.Other to context.getString(R.string.other) +// ) - return listOf( - signaling, - distance, - location, - angles, - time, - power, - weather, - other + return listOfNotNull( + Tool( + 1, + context.getString(R.string.flashlight_title), + R.drawable.flashlight, + R.id.action_action_experimental_tools_to_fragmentToolFlashlight, + ToolCategory.Signaling, + ), + Tool( + 2, + context.getString(R.string.tool_whistle_title), + R.drawable.ic_tool_whistle, + R.id.action_action_experimental_tools_to_toolWhistleFragment, + ToolCategory.Signaling, + ), + Tool( + 3, + context.getString(R.string.tool_ruler_title), + R.drawable.ruler, + R.id.action_action_experimental_tools_to_rulerFragment, + ToolCategory.Distance, + ), + if (hasPedometer) Tool( + 4, + context.getString(R.string.pedometer), + R.drawable.steps, + R.id.action_tools_to_pedometer, + ToolCategory.Distance, + ) else null, + if (prefs.isCliffHeightEnabled) Tool( + 5, + context.getString(R.string.tool_cliff_height_title), + R.drawable.ic_tool_cliff_height, + R.id.action_action_experimental_tools_to_toolCliffHeightFragment, + ToolCategory.Distance, + context.getString(R.string.experimental) + " - " + context.getString(R.string.tool_cliff_height_description), + isExperimental = true + ) else null, + Tool( + 6, + context.getString(R.string.navigation), + R.drawable.ic_compass_icon, + R.id.action_navigation, + ToolCategory.Location, + ), + Tool( + 7, + context.getString(R.string.beacons), + R.drawable.ic_location, + R.id.action_tools_to_beacons, + ToolCategory.Location, + ), + Tool( + 8, + context.getString(R.string.photo_maps), + R.drawable.maps, + R.id.action_tools_to_maps_list, + ToolCategory.Location, + context.getString(R.string.photo_map_summary), + guideId = R.raw.importing_maps + ), + Tool( + 9, + context.getString(R.string.paths), + R.drawable.ic_tool_backtrack, + R.id.action_action_experimental_tools_to_fragmentBacktrack, + ToolCategory.Location, + ), + Tool( + 10, + context.getString(R.string.tool_triangulate_title), + R.drawable.ic_tool_triangulate, + R.id.action_action_experimental_tools_to_fragmentToolTriangulate, + ToolCategory.Location, + ), + Tool( + 11, + context.getString(R.string.clinometer_title), + R.drawable.clinometer, + R.id.action_toolsFragment_to_clinometerFragment, + ToolCategory.Angles, + context.getString(R.string.tool_clinometer_summary) + ), + Tool( + 12, + context.getString(R.string.tool_bubble_level_title), + R.drawable.level, + R.id.action_action_experimental_tools_to_levelFragment, + ToolCategory.Angles, + context.getString(R.string.tool_bubble_level_summary) + ), + Tool( + 13, + context.getString(R.string.tool_clock_title), + R.drawable.ic_tool_clock, + R.id.action_action_experimental_tools_to_toolClockFragment, + ToolCategory.Time, + ), + Tool( + 14, + context.getString(R.string.astronomy), + R.drawable.ic_astronomy, + R.id.action_astronomy, + ToolCategory.Time, + ), + Tool( + 15, + context.getString(R.string.water_boil_timer), + R.drawable.ic_tool_boil, + R.id.action_action_experimental_tools_to_waterPurificationFragment, + ToolCategory.Time, + context.getString(R.string.tool_boil_summary) + ), + Tool( + 16, + context.getString(R.string.tides), + R.drawable.ic_tide_table, + R.id.action_toolsFragment_to_tidesFragment, + ToolCategory.Time, + ), + Tool( + 17, + context.getString(R.string.tool_battery_title), + R.drawable.ic_tool_battery, + R.id.action_action_experimental_tools_to_fragmentToolBattery, + ToolCategory.Power, + ), + if (hasCompass) Tool( + 18, + context.getString(R.string.tool_solar_panel_title), + R.drawable.ic_tool_solar_panel, + R.id.action_action_experimental_tools_to_fragmentToolSolarPanel, + ToolCategory.Power, + context.getString(R.string.tool_solar_panel_summary) + ) else null, + if (hasLightMeter) Tool( + 19, + context.getString(R.string.tool_light_meter_title), + R.drawable.flashlight, + R.id.action_toolsFragment_to_toolLightFragment, + ToolCategory.Power, + context.getString(R.string.guide_light_meter_description) + ) else null, + if (hasBarometer) Tool( + 20, + context.getString(R.string.weather), + R.drawable.cloud, + R.id.action_weather, + ToolCategory.Weather, + ) else null, + Tool( + 21, + context.getString(R.string.tool_climate), + R.drawable.ic_temperature_range, + R.id.action_toolsFragment_to_toolClimate, + ToolCategory.Weather, + context.getString(R.string.tool_climate_summary) + ), + Tool( + 22, + context.getString(R.string.tool_temperature_estimation_title), + R.drawable.thermometer, + R.id.action_tools_to_temperature_estimation, + ToolCategory.Weather, + context.getString(R.string.tool_temperature_estimation_description) + ), + Tool( + 23, + context.getString(R.string.clouds), + R.drawable.ic_tool_clouds, + R.id.action_action_experimental_tools_to_cloudFragment, + ToolCategory.Weather, + ), + Tool( + 24, + context.getString(R.string.tool_lightning_title), + R.drawable.ic_torch_on, + R.id.action_action_experimental_tools_to_fragmentToolLightning, + ToolCategory.Weather, + context.getString(R.string.tool_lightning_description) + ), + if (prefs.isAugmentedRealityEnabled && hasCompass) Tool( + 25, + context.getString(R.string.augmented_reality), + R.drawable.ic_camera, + R.id.action_tools_to_augmented_reality, + ToolCategory.Other, + context.getString(R.string.augmented_reality_description) + ) else null, + Tool( + 26, + context.getString(R.string.convert), + R.drawable.ic_tool_distance_convert, + R.id.action_toolsFragment_to_toolConvertFragment, + ToolCategory.Other, + ), + Tool( + 27, + context.getString(R.string.packing_lists), + R.drawable.ic_tool_pack, + R.id.action_action_experimental_tools_to_action_inventory, + ToolCategory.Other, + ), + if (hasCompass) Tool( + 28, + context.getString(R.string.tool_metal_detector_title), + R.drawable.ic_tool_metal_detector, + R.id.action_action_experimental_tools_to_fragmentToolMetalDetector, + ToolCategory.Other, + ) else null, + Tool( + 29, + context.getString(R.string.tool_white_noise_title), + R.drawable.ic_tool_white_noise, + R.id.action_action_experimental_tools_to_fragmentToolWhiteNoise, + ToolCategory.Other, + context.getString(R.string.tool_white_noise_summary) + ), + Tool( + 30, + context.getString(R.string.tool_notes_title), + R.drawable.ic_tool_notes, + R.id.action_action_experimental_tools_to_fragmentToolNotes, + ToolCategory.Other, + ), + Tool( + 31, + context.getString(R.string.qr_code_scanner), + R.drawable.ic_qr_code, + R.id.action_tools_to_qr, + ToolCategory.Other, + ), + Tool( + 32, + context.getString(R.string.sensors), + R.drawable.ic_sensors, + R.id.sensorDetailsFragment, + ToolCategory.Other, + ), + Tool( + 33, + context.getString(R.string.diagnostics), + R.drawable.ic_alert, + R.id.action_tools_to_diagnostics, + ToolCategory.Other, + ), + Tool( + 34, + context.getString(R.string.settings), + R.drawable.ic_settings, + R.id.action_settings, + ToolCategory.Other, + ), + Tool( + 35, + context.getString(R.string.tool_user_guide_title), + R.drawable.ic_user_guide, + R.id.action_action_experimental_tools_to_guideListFragment, + ToolCategory.Other, + context.getString(R.string.tool_user_guide_summary) + ), + if (isDebug()) Tool( + 36, + "Experimentation", + R.drawable.ic_experimental, + R.id.experimentationFragment, + ToolCategory.Other, + ) else null ) - } } diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index 840893d69..e4fc76092 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -23,16 +23,16 @@ import com.kylecorry.trail_sense.databinding.FragmentTools2Binding import com.kylecorry.trail_sense.quickactions.ToolsQuickActionBinder import com.kylecorry.trail_sense.shared.CustomUiUtils import com.kylecorry.trail_sense.shared.extensions.setOnQueryTextListener +import com.kylecorry.trail_sense.tools.guide.infrastructure.UserGuideUtils class ToolsFragment : BoundFragment() { - private val tools by lazy { Tools.getTools(requireContext()).flatMap { it.tools } } + private val tools by lazy { Tools.getTools(requireContext()) } - // TODO: Give each tool a unique ID private val pinnedIds = mutableSetOf( - R.id.action_navigation, - R.id.action_weather, - R.id.action_astronomy + 6L, // Navigation + 20L, // Weather + 14L, // Astronomy ) override fun generateBinding( @@ -67,7 +67,7 @@ class ToolsFragment : BoundFragment() { binding.pinnedEditBtn.setOnClickListener { // Sort alphabetically, but if the tool is already pinned, put it first val sorted = tools.sortedBy { tool -> - if (pinnedIds.contains(tool.navAction)) { + if (pinnedIds.contains(tool.id)) { "0${tool.name}" } else { tool.name @@ -75,7 +75,7 @@ class ToolsFragment : BoundFragment() { } val toolNames = sorted.map { it.name } val defaultSelected = sorted.mapIndexedNotNull { index, tool -> - if (pinnedIds.contains(tool.navAction)) { + if (pinnedIds.contains(tool.id)) { index } else { null @@ -93,7 +93,7 @@ class ToolsFragment : BoundFragment() { pinnedIds.clear() selected.forEach { val tool = sorted[it] - pinnedIds.add(tool.navAction) + pinnedIds.add(tool.id) } } @@ -116,20 +116,21 @@ class ToolsFragment : BoundFragment() { this.tools.filter { it.name.contains(filter, true) || it.description?.contains(filter, true) == true } - }.sortedBy { it.name } + } - populateTools(tools, binding.tools) + populateTools(sortTools(tools), binding.tools) } private fun updatePinnedTools() { // TODO: Load pinned list val pinned = tools.filter { - it.navAction in pinnedIds + it.id in pinnedIds } // TODO: Show an option to pin tools binding.pinned.isVisible = pinned.isNotEmpty() + // Always sort pinned tools alphabetically populateTools(pinned.sortedBy { it.name }, binding.pinned) } @@ -174,25 +175,28 @@ class ToolsFragment : BoundFragment() { Pickers.menu( view, listOf( if (it.description != null) getString(R.string.pref_category_about) else null, - if (pinnedIds.contains(it.navAction)) { + if (pinnedIds.contains(it.id)) { getString(R.string.unpin) } else { getString(R.string.pin) - } - // TODO: Guide + }, + if (it.guideId != null ) getString(R.string.tool_user_guide_title) else null, ) ) { selectedIdx -> when (selectedIdx) { 0 -> dialog(it.name, it.description, cancelText = null) 1 -> { // TODO: Save this - if (pinnedIds.contains(it.navAction)) { - pinnedIds.remove(it.navAction) + if (pinnedIds.contains(it.id)) { + pinnedIds.remove(it.id) } else { - pinnedIds.add(it.navAction) + pinnedIds.add(it.id) } updatePinnedTools() } + 2 -> { + UserGuideUtils.openGuide(this, it.guideId!!) + } } true } @@ -203,4 +207,9 @@ class ToolsFragment : BoundFragment() { } } + private fun sortTools(tools: List): List { + // Sort by category, then by name + return tools.sortedWith(compareBy({ it.category.ordinal }, { it.name })) + } + } \ No newline at end of file From e0e24f284f44424baf5292c27121a8a2ef29014e Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 12:21:37 -0500 Subject: [PATCH 07/16] Show tool guides --- .../trail_sense/tools/maps/ui/MapsFragment.kt | 2 +- .../kylecorry/trail_sense/tools/ui/Tools.kt | 29 ++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt index 37d8e5177..bbd505fd5 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt @@ -107,7 +107,7 @@ class MapsFragment : BoundFragment() { } private fun openGuide() { - UserGuideUtils.openGuide(this, R.raw.importing_maps) + UserGuideUtils.showGuide(this, R.raw.importing_maps) } private fun calibrate() { diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt index 17ef9a0ed..3d74cda93 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt @@ -88,7 +88,8 @@ object Tools { R.id.action_action_experimental_tools_to_toolCliffHeightFragment, ToolCategory.Distance, context.getString(R.string.experimental) + " - " + context.getString(R.string.tool_cliff_height_description), - isExperimental = true + isExperimental = true, + guideId = R.raw.cliff_height ) else null, Tool( 6, @@ -96,6 +97,7 @@ object Tools { R.drawable.ic_compass_icon, R.id.action_navigation, ToolCategory.Location, + guideId = R.raw.navigate ), Tool( 7, @@ -103,6 +105,7 @@ object Tools { R.drawable.ic_location, R.id.action_tools_to_beacons, ToolCategory.Location, + guideId = R.raw.navigate ), Tool( 8, @@ -119,6 +122,7 @@ object Tools { R.drawable.ic_tool_backtrack, R.id.action_action_experimental_tools_to_fragmentBacktrack, ToolCategory.Location, + guideId = R.raw.navigate ), Tool( 10, @@ -126,6 +130,7 @@ object Tools { R.drawable.ic_tool_triangulate, R.id.action_action_experimental_tools_to_fragmentToolTriangulate, ToolCategory.Location, + guideId = R.raw.determine_location_without_gps ), Tool( 11, @@ -133,7 +138,8 @@ object Tools { R.drawable.clinometer, R.id.action_toolsFragment_to_clinometerFragment, ToolCategory.Angles, - context.getString(R.string.tool_clinometer_summary) + context.getString(R.string.tool_clinometer_summary), + guideId = R.raw.clinometer ), Tool( 12, @@ -155,7 +161,7 @@ object Tools { context.getString(R.string.astronomy), R.drawable.ic_astronomy, R.id.action_astronomy, - ToolCategory.Time, + ToolCategory.Time ), Tool( 15, @@ -163,7 +169,8 @@ object Tools { R.drawable.ic_tool_boil, R.id.action_action_experimental_tools_to_waterPurificationFragment, ToolCategory.Time, - context.getString(R.string.tool_boil_summary) + context.getString(R.string.tool_boil_summary), + guideId = R.raw.making_water_potable ), Tool( 16, @@ -171,6 +178,7 @@ object Tools { R.drawable.ic_tide_table, R.id.action_toolsFragment_to_tidesFragment, ToolCategory.Time, + guideId = R.raw.tides ), Tool( 17, @@ -178,6 +186,7 @@ object Tools { R.drawable.ic_tool_battery, R.id.action_action_experimental_tools_to_fragmentToolBattery, ToolCategory.Power, + guideId = R.raw.conserving_battery ), if (hasCompass) Tool( 18, @@ -193,7 +202,8 @@ object Tools { R.drawable.flashlight, R.id.action_toolsFragment_to_toolLightFragment, ToolCategory.Power, - context.getString(R.string.guide_light_meter_description) + context.getString(R.string.guide_light_meter_description), + guideId = R.raw.flashlight_testing ) else null, if (hasBarometer) Tool( 20, @@ -201,6 +211,7 @@ object Tools { R.drawable.cloud, R.id.action_weather, ToolCategory.Weather, + guideId = R.raw.weather ) else null, Tool( 21, @@ -208,7 +219,8 @@ object Tools { R.drawable.ic_temperature_range, R.id.action_toolsFragment_to_toolClimate, ToolCategory.Weather, - context.getString(R.string.tool_climate_summary) + context.getString(R.string.tool_climate_summary), + guideId = R.raw.weather ), Tool( 22, @@ -216,7 +228,8 @@ object Tools { R.drawable.thermometer, R.id.action_tools_to_temperature_estimation, ToolCategory.Weather, - context.getString(R.string.tool_temperature_estimation_description) + context.getString(R.string.tool_temperature_estimation_description), + guideId = R.raw.weather ), Tool( 23, @@ -224,6 +237,7 @@ object Tools { R.drawable.ic_tool_clouds, R.id.action_action_experimental_tools_to_cloudFragment, ToolCategory.Weather, + guideId = R.raw.weather ), Tool( 24, @@ -254,6 +268,7 @@ object Tools { R.drawable.ic_tool_pack, R.id.action_action_experimental_tools_to_action_inventory, ToolCategory.Other, + guideId = R.raw.packing_lists ), if (hasCompass) Tool( 28, From a33422e2887c6e0dac580c1a4a40aecfd275cf2a Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 12:22:12 -0500 Subject: [PATCH 08/16] Open guide in bottom sheet --- .../com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt | 2 +- .../java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt index bbd505fd5..37d8e5177 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/maps/ui/MapsFragment.kt @@ -107,7 +107,7 @@ class MapsFragment : BoundFragment() { } private fun openGuide() { - UserGuideUtils.showGuide(this, R.raw.importing_maps) + UserGuideUtils.openGuide(this, R.raw.importing_maps) } private fun calibrate() { diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index e4fc76092..8dc9b6e80 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -195,7 +195,7 @@ class ToolsFragment : BoundFragment() { updatePinnedTools() } 2 -> { - UserGuideUtils.openGuide(this, it.guideId!!) + UserGuideUtils.showGuide(this, it.guideId!!) } } true From ba5cbc55b59bdc8b43ab91635604ecf80d66bdce Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 12:40:15 -0500 Subject: [PATCH 09/16] Categorize tools --- .../trail_sense/tools/ui/ToolsFragment.kt | 158 ++++++++++++------ 1 file changed, 106 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index 8dc9b6e80..b5c1e44a3 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -22,6 +22,7 @@ import com.kylecorry.trail_sense.R import com.kylecorry.trail_sense.databinding.FragmentTools2Binding import com.kylecorry.trail_sense.quickactions.ToolsQuickActionBinder import com.kylecorry.trail_sense.shared.CustomUiUtils +import com.kylecorry.trail_sense.shared.colors.AppColor import com.kylecorry.trail_sense.shared.extensions.setOnQueryTextListener import com.kylecorry.trail_sense.tools.guide.infrastructure.UserGuideUtils @@ -118,7 +119,7 @@ class ToolsFragment : BoundFragment() { } } - populateTools(sortTools(tools), binding.tools) + populateCategorizedTools(sortTools(tools), binding.tools) } private fun updatePinnedTools() { @@ -127,15 +128,63 @@ class ToolsFragment : BoundFragment() { it.id in pinnedIds } - // TODO: Show an option to pin tools binding.pinned.isVisible = pinned.isNotEmpty() // Always sort pinned tools alphabetically populateTools(pinned.sortedBy { it.name }, binding.pinned) } - private fun populateTools(tools: List, grid: GridLayout) { + private fun populateTools(tools: List, grid: GridLayout){ grid.removeAllViews() + tools.forEach { + grid.addView(createToolButton(it)) + } + } + + private fun populateCategorizedTools(categories: List, grid: GridLayout) { + grid.removeAllViews() + + if (categories.size == 1){ + populateTools(categories.first().tools, grid) + return + } + + + categories.forEach { + grid.addView(createToolCategoryHeader(it.categoryName)) + it.tools.forEach { + grid.addView(createToolButton(it)) + } + } + } + + private fun createToolCategoryHeader(name: String): View { + // TODO: Move this to the class level + val headerMargins = Resources.dp(requireContext(), 8f).toInt() + + val gridColumnSpec = GridLayout.spec(GridLayout.UNDEFINED, 2, 1f) + val gridRowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) + + val header = TextView(requireContext()) + header.text = name + header.textSize = 14f + header.setTextColor(AppColor.Orange.color) + // Bold + header.paint.isFakeBoldText = true + header.layoutParams = GridLayout.LayoutParams().apply { + width = 0 + height = GridLayout.LayoutParams.WRAP_CONTENT + columnSpec = gridColumnSpec + rowSpec = gridRowSpec + setMargins(headerMargins, headerMargins * 2, headerMargins, headerMargins) + } + header.gravity = Gravity.CENTER_VERTICAL + + return header + } + + private fun createToolButton(tool: Tool): View { + // TODO: Move this to the class level val iconSize = Resources.dp(requireContext(), 24f).toInt() val iconPadding = Resources.dp(requireContext(), 16f).toInt() val iconColor = Resources.androidTextColorPrimary(requireContext()) @@ -149,67 +198,72 @@ class ToolsFragment : BoundFragment() { val gridColumnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) val gridRowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) - tools.forEach { - val button = TextView(requireContext()) - button.text = it.name - button.setCompoundDrawables(iconSize, left = it.icon) - button.compoundDrawablePadding = iconPadding - CustomUiUtils.setImageColor(button, iconColor) - button.layoutParams = GridLayout.LayoutParams().apply { - width = 0 - height = buttonHeight - columnSpec = gridColumnSpec - rowSpec = gridRowSpec - setMargins(buttonMargins) - } - button.gravity = Gravity.CENTER_VERTICAL - button.setPadding(buttonPadding, 0, buttonPadding, 0) + val button = TextView(requireContext()) + button.text = tool.name + button.setCompoundDrawables(iconSize, left = tool.icon) + button.compoundDrawablePadding = iconPadding + CustomUiUtils.setImageColor(button, iconColor) + button.layoutParams = GridLayout.LayoutParams().apply { + width = 0 + height = buttonHeight + columnSpec = gridColumnSpec + rowSpec = gridRowSpec + setMargins(buttonMargins) + } + button.gravity = Gravity.CENTER_VERTICAL + button.setPadding(buttonPadding, 0, buttonPadding, 0) - button.setBackgroundResource(R.drawable.rounded_rectangle) - button.backgroundTintList = ColorStateList.valueOf(buttonBackgroundColor) - button.setOnClickListener { _ -> - findNavController().navigate(it.navAction) - } + button.setBackgroundResource(R.drawable.rounded_rectangle) + button.backgroundTintList = ColorStateList.valueOf(buttonBackgroundColor) + button.setOnClickListener { _ -> + findNavController().navigate(tool.navAction) + } - button.setOnLongClickListener { view -> - Pickers.menu( - view, listOf( - if (it.description != null) getString(R.string.pref_category_about) else null, - if (pinnedIds.contains(it.id)) { - getString(R.string.unpin) + button.setOnLongClickListener { view -> + Pickers.menu( + view, listOf( + if (tool.description != null) getString(R.string.pref_category_about) else null, + if (pinnedIds.contains(tool.id)) { + getString(R.string.unpin) + } else { + getString(R.string.pin) + }, + if (tool.guideId != null ) getString(R.string.tool_user_guide_title) else null, + ) + ) { selectedIdx -> + when (selectedIdx) { + 0 -> dialog(tool.name, tool.description, cancelText = null) + 1 -> { + // TODO: Save this + if (pinnedIds.contains(tool.id)) { + pinnedIds.remove(tool.id) } else { - getString(R.string.pin) - }, - if (it.guideId != null ) getString(R.string.tool_user_guide_title) else null, - ) - ) { selectedIdx -> - when (selectedIdx) { - 0 -> dialog(it.name, it.description, cancelText = null) - 1 -> { - // TODO: Save this - if (pinnedIds.contains(it.id)) { - pinnedIds.remove(it.id) - } else { - pinnedIds.add(it.id) - } - updatePinnedTools() - } - 2 -> { - UserGuideUtils.showGuide(this, it.guideId!!) + pinnedIds.add(tool.id) } + updatePinnedTools() + } + 2 -> { + UserGuideUtils.showGuide(this, tool.guideId!!) } - true } true } - - grid.addView(button) + true } + + return button } - private fun sortTools(tools: List): List { + private fun sortTools(tools: List): List { // Sort by category, then by name - return tools.sortedWith(compareBy({ it.category.ordinal }, { it.name })) +// return tools.sortedWith(compareBy({ it.category.ordinal }, { it.name })) + + return tools.groupBy { it.category }.map { (category, tools) -> + CategorizedTools(category.name, tools.sortedBy { it.name }) + } + } + data class CategorizedTools(val categoryName: String, val tools: List) + } \ No newline at end of file From 1ff443ca9a890f19f54a97e012c3d04b6e8d81c2 Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 12:42:17 -0500 Subject: [PATCH 10/16] Use actual group header names --- .../kylecorry/trail_sense/tools/ui/Tools.kt | 11 ---------- .../trail_sense/tools/ui/ToolsFragment.kt | 21 +++++++++++++++---- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt index 3d74cda93..e90c9a0a6 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/Tools.kt @@ -41,17 +41,6 @@ object Tools { val hasBarometer = Sensors.hasBarometer(context) val prefs = UserPreferences(context) -// val groupNameMap = mapOf( -// ToolCategory.Signaling to context.getString(R.string.tool_category_signaling), -// ToolCategory.Distance to context.getString(R.string.distance), -// ToolCategory.Location to context.getString(R.string.location), -// ToolCategory.Angles to context.getString(R.string.tool_category_angles), -// ToolCategory.Time to context.getString(R.string.time), -// ToolCategory.Power to context.getString(R.string.power), -// ToolCategory.Weather to context.getString(R.string.weather), -// ToolCategory.Other to context.getString(R.string.other) -// ) - return listOfNotNull( Tool( 1, diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index b5c1e44a3..e04af2578 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -143,7 +143,7 @@ class ToolsFragment : BoundFragment() { private fun populateCategorizedTools(categories: List, grid: GridLayout) { grid.removeAllViews() - + if (categories.size == 1){ populateTools(categories.first().tools, grid) return @@ -158,7 +158,7 @@ class ToolsFragment : BoundFragment() { } } - private fun createToolCategoryHeader(name: String): View { + private fun createToolCategoryHeader(name: String?): View { // TODO: Move this to the class level val headerMargins = Resources.dp(requireContext(), 8f).toInt() @@ -254,16 +254,29 @@ class ToolsFragment : BoundFragment() { return button } + // TODO: Add other strategies and extract this private fun sortTools(tools: List): List { // Sort by category, then by name // return tools.sortedWith(compareBy({ it.category.ordinal }, { it.name })) + val groupNameMap = mapOf( + ToolCategory.Signaling to getString(R.string.tool_category_signaling), + ToolCategory.Distance to getString(R.string.distance), + ToolCategory.Location to getString(R.string.location), + ToolCategory.Angles to getString(R.string.tool_category_angles), + ToolCategory.Time to getString(R.string.time), + ToolCategory.Power to getString(R.string.power), + ToolCategory.Weather to getString(R.string.weather), + ToolCategory.Other to getString(R.string.other) + ) + return tools.groupBy { it.category }.map { (category, tools) -> - CategorizedTools(category.name, tools.sortedBy { it.name }) + CategorizedTools(groupNameMap[category], tools.sortedBy { it.name }) } } - data class CategorizedTools(val categoryName: String, val tools: List) + // TODO: Extract this + data class CategorizedTools(val categoryName: String?, val tools: List) } \ No newline at end of file From 149428afec0cb76965d70a473b00251260ffb347 Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 13:16:36 -0500 Subject: [PATCH 11/16] Extract sort and pinning --- .../trail_sense/tools/ui/PinnedToolManager.kt | 41 +++++++++ .../trail_sense/tools/ui/ToolsFragment.kt | 89 +++++++------------ .../tools/ui/sort/AlphabeticalToolSort.kt | 10 +++ .../tools/ui/sort/CategoricalToolSort.kt | 26 ++++++ .../tools/ui/sort/CategorizedTools.kt | 5 ++ .../trail_sense/tools/ui/sort/ToolSort.kt | 9 ++ 6 files changed, 123 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/com/kylecorry/trail_sense/tools/ui/PinnedToolManager.kt create mode 100644 app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/AlphabeticalToolSort.kt create mode 100644 app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/CategoricalToolSort.kt create mode 100644 app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/CategorizedTools.kt create mode 100644 app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/ToolSort.kt diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/PinnedToolManager.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/PinnedToolManager.kt new file mode 100644 index 000000000..d6f427d43 --- /dev/null +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/PinnedToolManager.kt @@ -0,0 +1,41 @@ +package com.kylecorry.trail_sense.tools.ui + +class PinnedToolManager { + + private val pinned = mutableSetOf() + private val lock = Any() + + // TODO: Keep in sync with user prefs + + fun getPinnedToolIds(): List { + return synchronized(lock) { + pinned.toList() + } + } + + fun setPinnedToolIds(toolIds: List) { + synchronized(lock) { + pinned.clear() + pinned.addAll(toolIds) + } + } + + fun pin(toolId: Long) { + synchronized(lock) { + pinned.add(toolId) + } + } + + fun unpin(toolId: Long) { + synchronized(lock) { + pinned.remove(toolId) + } + } + + fun isPinned(toolId: Long): Boolean { + return synchronized(lock) { + pinned.contains(toolId) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index e04af2578..461438ffa 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -25,16 +25,29 @@ import com.kylecorry.trail_sense.shared.CustomUiUtils import com.kylecorry.trail_sense.shared.colors.AppColor import com.kylecorry.trail_sense.shared.extensions.setOnQueryTextListener import com.kylecorry.trail_sense.tools.guide.infrastructure.UserGuideUtils +import com.kylecorry.trail_sense.tools.ui.sort.AlphabeticalToolSort +import com.kylecorry.trail_sense.tools.ui.sort.CategoricalToolSort +import com.kylecorry.trail_sense.tools.ui.sort.CategorizedTools class ToolsFragment : BoundFragment() { private val tools by lazy { Tools.getTools(requireContext()) } - private val pinnedIds = mutableSetOf( - 6L, // Navigation - 20L, // Weather - 14L, // Astronomy - ) + private val pinnedToolManager = PinnedToolManager() + + init { + // Navigation + pinnedToolManager.pin(6L) + + // Weather + pinnedToolManager.pin(20L) + + // Astronomy + pinnedToolManager.pin(14L) + } + + private val toolSorter by lazy { CategoricalToolSort(requireContext()) } + private val pinnedSorter = AlphabeticalToolSort() override fun generateBinding( layoutInflater: LayoutInflater, container: ViewGroup? @@ -68,7 +81,7 @@ class ToolsFragment : BoundFragment() { binding.pinnedEditBtn.setOnClickListener { // Sort alphabetically, but if the tool is already pinned, put it first val sorted = tools.sortedBy { tool -> - if (pinnedIds.contains(tool.id)) { + if (pinnedToolManager.isPinned(tool.id)) { "0${tool.name}" } else { tool.name @@ -76,7 +89,7 @@ class ToolsFragment : BoundFragment() { } val toolNames = sorted.map { it.name } val defaultSelected = sorted.mapIndexedNotNull { index, tool -> - if (pinnedIds.contains(tool.id)) { + if (pinnedToolManager.isPinned(tool.id)) { index } else { null @@ -90,12 +103,7 @@ class ToolsFragment : BoundFragment() { defaultSelected ) { selected -> if (selected != null) { - // TODO: Save this - pinnedIds.clear() - selected.forEach { - val tool = sorted[it] - pinnedIds.add(tool.id) - } + pinnedToolManager.setPinnedToolIds(selected.map { sorted[it].id }) } updatePinnedTools() @@ -119,33 +127,26 @@ class ToolsFragment : BoundFragment() { } } - populateCategorizedTools(sortTools(tools), binding.tools) + populateTools(toolSorter.sort(tools), binding.tools) } private fun updatePinnedTools() { - // TODO: Load pinned list val pinned = tools.filter { - it.id in pinnedIds + pinnedToolManager.isPinned(it.id) } binding.pinned.isVisible = pinned.isNotEmpty() - // Always sort pinned tools alphabetically - populateTools(pinned.sortedBy { it.name }, binding.pinned) - } - - private fun populateTools(tools: List, grid: GridLayout){ - grid.removeAllViews() - tools.forEach { - grid.addView(createToolButton(it)) - } + populateTools(pinnedSorter.sort(pinned), binding.pinned) } - private fun populateCategorizedTools(categories: List, grid: GridLayout) { + private fun populateTools(categories: List, grid: GridLayout) { grid.removeAllViews() if (categories.size == 1){ - populateTools(categories.first().tools, grid) + categories.first().tools.forEach { + grid.addView(createToolButton(it)) + } return } @@ -223,7 +224,7 @@ class ToolsFragment : BoundFragment() { Pickers.menu( view, listOf( if (tool.description != null) getString(R.string.pref_category_about) else null, - if (pinnedIds.contains(tool.id)) { + if (pinnedToolManager.isPinned(tool.id)) { getString(R.string.unpin) } else { getString(R.string.pin) @@ -234,11 +235,10 @@ class ToolsFragment : BoundFragment() { when (selectedIdx) { 0 -> dialog(tool.name, tool.description, cancelText = null) 1 -> { - // TODO: Save this - if (pinnedIds.contains(tool.id)) { - pinnedIds.remove(tool.id) + if (pinnedToolManager.isPinned(tool.id)) { + pinnedToolManager.unpin(tool.id) } else { - pinnedIds.add(tool.id) + pinnedToolManager.pin(tool.id) } updatePinnedTools() } @@ -254,29 +254,4 @@ class ToolsFragment : BoundFragment() { return button } - // TODO: Add other strategies and extract this - private fun sortTools(tools: List): List { - // Sort by category, then by name -// return tools.sortedWith(compareBy({ it.category.ordinal }, { it.name })) - - val groupNameMap = mapOf( - ToolCategory.Signaling to getString(R.string.tool_category_signaling), - ToolCategory.Distance to getString(R.string.distance), - ToolCategory.Location to getString(R.string.location), - ToolCategory.Angles to getString(R.string.tool_category_angles), - ToolCategory.Time to getString(R.string.time), - ToolCategory.Power to getString(R.string.power), - ToolCategory.Weather to getString(R.string.weather), - ToolCategory.Other to getString(R.string.other) - ) - - return tools.groupBy { it.category }.map { (category, tools) -> - CategorizedTools(groupNameMap[category], tools.sortedBy { it.name }) - } - - } - - // TODO: Extract this - data class CategorizedTools(val categoryName: String?, val tools: List) - } \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/AlphabeticalToolSort.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/AlphabeticalToolSort.kt new file mode 100644 index 000000000..194cdd275 --- /dev/null +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/AlphabeticalToolSort.kt @@ -0,0 +1,10 @@ +package com.kylecorry.trail_sense.tools.ui.sort + +import com.kylecorry.trail_sense.tools.ui.Tool + +class AlphabeticalToolSort : ToolSort { + override fun sort(tools: List): List { + return listOf(CategorizedTools(null, tools.sortedBy { it.name })) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/CategoricalToolSort.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/CategoricalToolSort.kt new file mode 100644 index 000000000..1eca64a1a --- /dev/null +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/CategoricalToolSort.kt @@ -0,0 +1,26 @@ +package com.kylecorry.trail_sense.tools.ui.sort + +import android.content.Context +import com.kylecorry.trail_sense.R +import com.kylecorry.trail_sense.tools.ui.Tool +import com.kylecorry.trail_sense.tools.ui.ToolCategory + +class CategoricalToolSort(context: Context) : ToolSort { + + private val groupNameMap = mapOf( + ToolCategory.Signaling to context.getString(R.string.tool_category_signaling), + ToolCategory.Distance to context.getString(R.string.distance), + ToolCategory.Location to context.getString(R.string.location), + ToolCategory.Angles to context.getString(R.string.tool_category_angles), + ToolCategory.Time to context.getString(R.string.time), + ToolCategory.Power to context.getString(R.string.power), + ToolCategory.Weather to context.getString(R.string.weather), + ToolCategory.Other to context.getString(R.string.other) + ) + + override fun sort(tools: List): List { + return tools.groupBy { it.category }.map { (category, tools) -> + CategorizedTools(groupNameMap[category], tools.sortedBy { it.name }) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/CategorizedTools.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/CategorizedTools.kt new file mode 100644 index 000000000..95b5d8d62 --- /dev/null +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/CategorizedTools.kt @@ -0,0 +1,5 @@ +package com.kylecorry.trail_sense.tools.ui.sort + +import com.kylecorry.trail_sense.tools.ui.Tool + +data class CategorizedTools(val categoryName: String?, val tools: List) \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/ToolSort.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/ToolSort.kt new file mode 100644 index 000000000..340cca30c --- /dev/null +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/sort/ToolSort.kt @@ -0,0 +1,9 @@ +package com.kylecorry.trail_sense.tools.ui.sort + +import com.kylecorry.trail_sense.tools.ui.Tool + +interface ToolSort { + + fun sort(tools: List): List + +} \ No newline at end of file From 22a27226cc691a35a170deb6b025731d500e6d4e Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 13:22:57 -0500 Subject: [PATCH 12/16] Save pinned tools --- .../trail_sense/tools/ui/PinnedToolManager.kt | 31 +++++++++++++++++-- .../trail_sense/tools/ui/ToolsFragment.kt | 26 ++++++---------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/PinnedToolManager.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/PinnedToolManager.kt index d6f427d43..a8c17a15d 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/PinnedToolManager.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/PinnedToolManager.kt @@ -1,11 +1,22 @@ package com.kylecorry.trail_sense.tools.ui -class PinnedToolManager { +import com.kylecorry.andromeda.preferences.IPreferences + +class PinnedToolManager(private val prefs: IPreferences) { private val pinned = mutableSetOf() private val lock = Any() - // TODO: Keep in sync with user prefs + private val key = "pinned_tools" + + init { + // TODO: Listen for changes + val all = readPrefs() + synchronized(lock) { + pinned.clear() + pinned.addAll(all) + } + } fun getPinnedToolIds(): List { return synchronized(lock) { @@ -18,18 +29,21 @@ class PinnedToolManager { pinned.clear() pinned.addAll(toolIds) } + writePrefs(getPinnedToolIds()) } fun pin(toolId: Long) { synchronized(lock) { pinned.add(toolId) } + writePrefs(getPinnedToolIds()) } fun unpin(toolId: Long) { synchronized(lock) { pinned.remove(toolId) } + writePrefs(getPinnedToolIds()) } fun isPinned(toolId: Long): Boolean { @@ -38,4 +52,17 @@ class PinnedToolManager { } } + private fun readPrefs(): List { + val str = prefs.getString(key) ?: return listOf( + 6L, // Navigation + 20L, // Weather + 14L // Astronomy + ) + return str.split(",").mapNotNull { it.toLongOrNull() } + } + + private fun writePrefs(toolIds: List) { + prefs.putString(key, toolIds.joinToString(",")) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index 461438ffa..53c03ce9b 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -24,6 +24,7 @@ import com.kylecorry.trail_sense.quickactions.ToolsQuickActionBinder import com.kylecorry.trail_sense.shared.CustomUiUtils import com.kylecorry.trail_sense.shared.colors.AppColor import com.kylecorry.trail_sense.shared.extensions.setOnQueryTextListener +import com.kylecorry.trail_sense.shared.preferences.PreferencesSubsystem import com.kylecorry.trail_sense.tools.guide.infrastructure.UserGuideUtils import com.kylecorry.trail_sense.tools.ui.sort.AlphabeticalToolSort import com.kylecorry.trail_sense.tools.ui.sort.CategoricalToolSort @@ -33,17 +34,10 @@ class ToolsFragment : BoundFragment() { private val tools by lazy { Tools.getTools(requireContext()) } - private val pinnedToolManager = PinnedToolManager() - - init { - // Navigation - pinnedToolManager.pin(6L) - - // Weather - pinnedToolManager.pin(20L) - - // Astronomy - pinnedToolManager.pin(14L) + private val pinnedToolManager by lazy { + PinnedToolManager( + PreferencesSubsystem.getInstance(requireContext()).preferences + ) } private val toolSorter by lazy { CategoricalToolSort(requireContext()) } @@ -97,10 +91,7 @@ class ToolsFragment : BoundFragment() { } Pickers.items( - requireContext(), - getString(R.string.pinned), - toolNames, - defaultSelected + requireContext(), getString(R.string.pinned), toolNames, defaultSelected ) { selected -> if (selected != null) { pinnedToolManager.setPinnedToolIds(selected.map { sorted[it].id }) @@ -143,7 +134,7 @@ class ToolsFragment : BoundFragment() { private fun populateTools(categories: List, grid: GridLayout) { grid.removeAllViews() - if (categories.size == 1){ + if (categories.size == 1) { categories.first().tools.forEach { grid.addView(createToolButton(it)) } @@ -229,7 +220,7 @@ class ToolsFragment : BoundFragment() { } else { getString(R.string.pin) }, - if (tool.guideId != null ) getString(R.string.tool_user_guide_title) else null, + if (tool.guideId != null) getString(R.string.tool_user_guide_title) else null, ) ) { selectedIdx -> when (selectedIdx) { @@ -242,6 +233,7 @@ class ToolsFragment : BoundFragment() { } updatePinnedTools() } + 2 -> { UserGuideUtils.showGuide(this, tool.guideId!!) } From cfcae686201dcd141ed3fc677e837288f05259b3 Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 13:29:00 -0500 Subject: [PATCH 13/16] Keep search bar pinned at the top of tools --- .../trail_sense/tools/ui/ToolsFragment.kt | 9 ++++++++ app/src/main/res/layout/fragment_tools_2.xml | 21 +++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index 53c03ce9b..4f284793a 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -110,6 +110,15 @@ class ToolsFragment : BoundFragment() { private fun updateTools() { val filter = binding.searchbox.query + // Hide pinned when searching + if (filter.isNullOrBlank()) { + binding.pinned.isVisible = true + binding.pinnedTitle.isVisible = true + } else { + binding.pinned.isVisible = false + binding.pinnedTitle.isVisible = false + } + val tools = if (filter.isNullOrBlank()) { this.tools } else { diff --git a/app/src/main/res/layout/fragment_tools_2.xml b/app/src/main/res/layout/fragment_tools_2.xml index 61b16d0f4..d5b9db025 100644 --- a/app/src/main/res/layout/fragment_tools_2.xml +++ b/app/src/main/res/layout/fragment_tools_2.xml @@ -9,12 +9,23 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" + android:layout_marginTop="16dp" android:orientation="horizontal"> + + @@ -25,6 +36,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" + android:layout_marginTop="16dp" app:alignItems="center" app:flexWrap="wrap" app:justifyContent="center" /> @@ -85,15 +97,6 @@ android:text="@string/tools" android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" /> - - Date: Sat, 11 Nov 2023 13:46:05 -0500 Subject: [PATCH 14/16] Add notice to long press tools --- .../quickactions/ToolsQuickActionBinder.kt | 3 +- .../trail_sense/shared/CustomUiUtils.kt | 13 ++ .../trail_sense/tools/ui/ToolsFragment.kt | 17 ++- app/src/main/res/layout/fragment_tools.xml | 125 +++++++++++++++--- app/src/main/res/layout/fragment_tools_2.xml | 112 ---------------- app/src/main/res/values/strings.xml | 1 + 6 files changed, 133 insertions(+), 138 deletions(-) delete mode 100644 app/src/main/res/layout/fragment_tools_2.xml diff --git a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt index 02d89e711..5125134fc 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt @@ -32,7 +32,8 @@ class ToolsQuickActionBinder( override fun bind() { binding.quickActions.removeAllViews() // TODO: Weather monitor - QuickActionBacktrack(createButton(), fragment).bind(fragment.viewLifecycleOwner) + // TODO: Backtrack quick action should be a toggle rather than opening the path +// QuickActionBacktrack(createButton(), fragment).bind(fragment.viewLifecycleOwner) QuickActionFlashlight(createButton(), fragment).bind(fragment.viewLifecycleOwner) QuickActionWhistle(createButton(), fragment).bind(fragment.viewLifecycleOwner) LowPowerQuickAction(createButton(), fragment).bind(fragment.viewLifecycleOwner) diff --git a/app/src/main/java/com/kylecorry/trail_sense/shared/CustomUiUtils.kt b/app/src/main/java/com/kylecorry/trail_sense/shared/CustomUiUtils.kt index 4371fb417..e9f273c37 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/shared/CustomUiUtils.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/shared/CustomUiUtils.kt @@ -237,6 +237,19 @@ object CustomUiUtils { } } + fun oneTimeToast( + context: Context, + message: String, + shownKey: String, + short: Boolean = true + ){ + val prefs = PreferencesSubsystem.getInstance(context).preferences + if (prefs.getBoolean(shownKey) != true) { + Alerts.toast(context, message, short) + prefs.putBoolean(shownKey, true) + } + } + fun disclaimer( context: Context, title: String, diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index 4f284793a..483255eac 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -19,7 +19,7 @@ import com.kylecorry.andromeda.core.ui.setCompoundDrawables import com.kylecorry.andromeda.fragments.BoundFragment import com.kylecorry.andromeda.pickers.Pickers import com.kylecorry.trail_sense.R -import com.kylecorry.trail_sense.databinding.FragmentTools2Binding +import com.kylecorry.trail_sense.databinding.FragmentToolsBinding import com.kylecorry.trail_sense.quickactions.ToolsQuickActionBinder import com.kylecorry.trail_sense.shared.CustomUiUtils import com.kylecorry.trail_sense.shared.colors.AppColor @@ -30,7 +30,7 @@ import com.kylecorry.trail_sense.tools.ui.sort.AlphabeticalToolSort import com.kylecorry.trail_sense.tools.ui.sort.CategoricalToolSort import com.kylecorry.trail_sense.tools.ui.sort.CategorizedTools -class ToolsFragment : BoundFragment() { +class ToolsFragment : BoundFragment() { private val tools by lazy { Tools.getTools(requireContext()) } @@ -45,8 +45,8 @@ class ToolsFragment : BoundFragment() { override fun generateBinding( layoutInflater: LayoutInflater, container: ViewGroup? - ): FragmentTools2Binding { - return FragmentTools2Binding.inflate(layoutInflater, container, false) + ): FragmentToolsBinding { + return FragmentToolsBinding.inflate(layoutInflater, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -100,6 +100,14 @@ class ToolsFragment : BoundFragment() { updatePinnedTools() } } + + CustomUiUtils.oneTimeToast( + requireContext(), + getString(R.string.tool_long_press_hint_toast), + "tools_long_press_notice_shown", + short = false + ) + } // TODO: Add a way to customize this @@ -203,6 +211,7 @@ class ToolsFragment : BoundFragment() { button.text = tool.name button.setCompoundDrawables(iconSize, left = tool.icon) button.compoundDrawablePadding = iconPadding + button.elevation = 2f CustomUiUtils.setImageColor(button, iconColor) button.layoutParams = GridLayout.LayoutParams().apply { width = 0 diff --git a/app/src/main/res/layout/fragment_tools.xml b/app/src/main/res/layout/fragment_tools.xml index 53e56e9b1..d5b9db025 100644 --- a/app/src/main/res/layout/fragment_tools.xml +++ b/app/src/main/res/layout/fragment_tools.xml @@ -1,29 +1,112 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> - - - + + + + + + + + app:alignItems="center" + app:flexWrap="wrap" + app:justifyContent="center" /> + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_tools_2.xml b/app/src/main/res/layout/fragment_tools_2.xml deleted file mode 100644 index d5b9db025..000000000 --- a/app/src/main/res/layout/fragment_tools_2.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 2b88a4397..ebb87cab2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1359,6 +1359,7 @@ Pinned Unpin Pin + Long press a tool to see more options %d map %d maps From cb2f9f4abc2c0e110cfed454b5caaf409a93795d Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 13:48:16 -0500 Subject: [PATCH 15/16] Fix binding name --- .../trail_sense/quickactions/ToolsQuickActionBinder.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt index 5125134fc..8112dbe75 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/quickactions/ToolsQuickActionBinder.kt @@ -6,11 +6,11 @@ import com.google.android.flexbox.FlexboxLayout import com.kylecorry.andromeda.core.system.Resources import com.kylecorry.andromeda.fragments.AndromedaFragment import com.kylecorry.trail_sense.R -import com.kylecorry.trail_sense.databinding.FragmentTools2Binding +import com.kylecorry.trail_sense.databinding.FragmentToolsBinding class ToolsQuickActionBinder( private val fragment: AndromedaFragment, - private val binding: FragmentTools2Binding + private val binding: FragmentToolsBinding ) : IQuickActionBinder { private fun createButton(): ImageButton { From 289db79476b4f5ef2d1d619f86d6de3907e0fbca Mon Sep 17 00:00:00 2001 From: Kyle Corry Date: Sat, 11 Nov 2023 13:51:56 -0500 Subject: [PATCH 16/16] Capitalize tool names --- .../kylecorry/trail_sense/tools/ui/ToolsFragment.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt index 483255eac..eddfda3d1 100644 --- a/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt +++ b/app/src/main/java/com/kylecorry/trail_sense/tools/ui/ToolsFragment.kt @@ -14,6 +14,7 @@ import androidx.core.view.setMargins import androidx.gridlayout.widget.GridLayout import androidx.navigation.fragment.findNavController import com.kylecorry.andromeda.alerts.dialog +import com.kylecorry.andromeda.core.capitalizeWords import com.kylecorry.andromeda.core.system.Resources import com.kylecorry.andromeda.core.ui.setCompoundDrawables import com.kylecorry.andromeda.fragments.BoundFragment @@ -52,12 +53,6 @@ class ToolsFragment : BoundFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.quickActions.children.forEach { - if (it is ImageButton) { - CustomUiUtils.setButtonState(it, false) - } - } - updatePinnedTools() updateTools() @@ -175,7 +170,7 @@ class ToolsFragment : BoundFragment() { val gridRowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) val header = TextView(requireContext()) - header.text = name + header.text = name?.capitalizeWords() header.textSize = 14f header.setTextColor(AppColor.Orange.color) // Bold @@ -208,7 +203,7 @@ class ToolsFragment : BoundFragment() { val gridRowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f) val button = TextView(requireContext()) - button.text = tool.name + button.text = tool.name.capitalizeWords() button.setCompoundDrawables(iconSize, left = tool.icon) button.compoundDrawablePadding = iconPadding button.elevation = 2f