From fcc28f41db18c49a46711ece9a5c61963a0682ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BE=B7=E7=94=9F?= <111033412+KoukeNeko@users.noreply.github.com> Date: Sun, 14 Dec 2025 11:47:36 +0800 Subject: [PATCH 01/15] Refactor: Extract PolicyAccessDialog into its own file The `PolicyAccessDialog` `DialogFragment` has been moved from `MainActivity.kt` into its own dedicated file, `PolicyAccessDialog.kt`, to improve code organization. In `MainActivity.kt`, `enableEdgeToEdge()` is now used, and the insets handling method has been renamed from `handleEdgeToEdgeInsets` to `setupWindowInsets` for clarity. --- .../com/klee/volumelockr/ui/MainActivity.kt | 53 ++++++++++--------- .../klee/volumelockr/ui/PolicyAccessDialog.kt | 27 ++++++++++ 2 files changed, 54 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/klee/volumelockr/ui/PolicyAccessDialog.kt diff --git a/app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt b/app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt index 1c882c1..eabb90d 100644 --- a/app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt +++ b/app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt @@ -1,17 +1,16 @@ package com.klee.volumelockr.ui -import android.app.AlertDialog -import android.app.Dialog import android.app.NotificationManager -import android.content.Intent import android.os.Build import android.os.Bundle -import android.provider.Settings +import androidx.activity.enableEdgeToEdge import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat -import androidx.fragment.app.DialogFragment +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.ui.AppBarConfiguration +import androidx.navigation.ui.setupWithNavController import com.klee.volumelockr.R import com.klee.volumelockr.databinding.ActivityMainBinding @@ -20,10 +19,12 @@ class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - handleEdgeToEdgeInsets() + setupToolbar() + setupWindowInsets() } override fun onResume() { @@ -34,6 +35,15 @@ class MainActivity : AppCompatActivity() { } } + private fun setupToolbar() { + setSupportActionBar(binding.toolbar) + val navHostFragment = supportFragmentManager + .findFragmentById(R.id.fragment_container_view) as NavHostFragment + val navController = navHostFragment.navController + val appBarConfiguration = AppBarConfiguration(navController.graph) + binding.toolbar.setupWithNavController(navController, appBarConfiguration) + } + @RequiresApi(Build.VERSION_CODES.M) private fun checkDoNotDisturbPermission() { val notificationManager = @@ -44,31 +54,22 @@ class MainActivity : AppCompatActivity() { } } - class PolicyAccessDialog : DialogFragment() { - companion object { - const val TAG = "PolicyAccessDialog" - } - - @RequiresApi(Build.VERSION_CODES.M) - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = - AlertDialog.Builder(requireContext()) - .setMessage(getString(R.string.dialog_policy_access_title)) - .setCancelable(false) - .setPositiveButton(getString(R.string.dialog_allow)) { _, _ -> - startActivity(Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)) - } - .create() - } - - private fun handleEdgeToEdgeInsets() { - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets -> + private fun setupWindowInsets() { + ViewCompat.setOnApplyWindowInsetsListener(binding.appBarLayout) { v, windowInsets -> val bars = windowInsets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) + v.setPadding(bars.left, bars.top, bars.right, 0) + WindowInsetsCompat.CONSUMED + } - v.setPadding(bars.left, bars.top, bars.right, bars.bottom) - + ViewCompat.setOnApplyWindowInsetsListener(binding.fragmentContainerView) { v, windowInsets -> + val bars = windowInsets.getInsets( + WindowInsetsCompat.Type.systemBars() + or WindowInsetsCompat.Type.displayCutout() + ) + v.setPadding(bars.left, 0, bars.right, bars.bottom) WindowInsetsCompat.CONSUMED } } diff --git a/app/src/main/java/com/klee/volumelockr/ui/PolicyAccessDialog.kt b/app/src/main/java/com/klee/volumelockr/ui/PolicyAccessDialog.kt new file mode 100644 index 0000000..4664c0e --- /dev/null +++ b/app/src/main/java/com/klee/volumelockr/ui/PolicyAccessDialog.kt @@ -0,0 +1,27 @@ +package com.klee.volumelockr.ui + +import android.app.AlertDialog +import android.app.Dialog +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import androidx.annotation.RequiresApi +import androidx.fragment.app.DialogFragment +import com.klee.volumelockr.R + +class PolicyAccessDialog : DialogFragment() { + companion object { + const val TAG = "PolicyAccessDialog" + } + + @RequiresApi(Build.VERSION_CODES.M) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = + AlertDialog.Builder(requireContext()) + .setMessage(getString(R.string.dialog_policy_access_title)) + .setCancelable(false) + .setPositiveButton(getString(R.string.dialog_allow)) { _, _ -> + startActivity(Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)) + } + .create() +} From 6fbb698865c86da8bd488ea9fcb1aae1592f47e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BE=B7=E7=94=9F?= <111033412+KoukeNeko@users.noreply.github.com> Date: Sun, 14 Dec 2025 11:58:17 +0800 Subject: [PATCH 02/15] feat: Add bottom navigation Adds a `BottomNavigationView` to the main screen for easier navigation between the main volume sliders, settings, and the about page. This includes: - Adding a `MaterialToolbar` and `BottomNavigationView` to `activity_main.xml`. - Creating a new `bottom_nav_menu.xml` with icons for home, settings, and info. - Updating `MainActivity.kt` to set up the toolbar and bottom navigation with the navigation controller. - Removing the now-redundant options menu from `VolumeSliderFragment`. - Applying a `NoActionBar` theme to the activity. --- .../com/klee/volumelockr/ui/MainActivity.kt | 11 ++-- .../volumelockr/ui/VolumeSliderFragment.kt | 50 ------------------- app/src/main/res/drawable/ic_home.xml | 9 ++++ app/src/main/res/drawable/ic_info.xml | 9 ++++ app/src/main/res/drawable/ic_settings.xml | 9 ++++ app/src/main/res/layout/activity_main.xml | 30 ++++++++++- app/src/main/res/menu/bottom_nav_menu.xml | 17 +++++++ app/src/main/res/values/themes.xml | 3 +- 8 files changed, 81 insertions(+), 57 deletions(-) create mode 100644 app/src/main/res/drawable/ic_home.xml create mode 100644 app/src/main/res/drawable/ic_info.xml create mode 100644 app/src/main/res/drawable/ic_settings.xml create mode 100644 app/src/main/res/menu/bottom_nav_menu.xml diff --git a/app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt b/app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt index eabb90d..ff6c0a1 100644 --- a/app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt +++ b/app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt @@ -23,7 +23,7 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - setupToolbar() + setupNavigation() setupWindowInsets() } @@ -35,13 +35,16 @@ class MainActivity : AppCompatActivity() { } } - private fun setupToolbar() { + private fun setupNavigation() { setSupportActionBar(binding.toolbar) val navHostFragment = supportFragmentManager .findFragmentById(R.id.fragment_container_view) as NavHostFragment val navController = navHostFragment.navController - val appBarConfiguration = AppBarConfiguration(navController.graph) + val appBarConfiguration = AppBarConfiguration( + setOf(R.id.volumeSliderFragment, R.id.settingsFragment, R.id.about_libraries) + ) binding.toolbar.setupWithNavController(navController, appBarConfiguration) + binding.bottomNavigation.setupWithNavController(navController) } @RequiresApi(Build.VERSION_CODES.M) @@ -64,7 +67,7 @@ class MainActivity : AppCompatActivity() { WindowInsetsCompat.CONSUMED } - ViewCompat.setOnApplyWindowInsetsListener(binding.fragmentContainerView) { v, windowInsets -> + ViewCompat.setOnApplyWindowInsetsListener(binding.bottomNavigation) { v, windowInsets -> val bars = windowInsets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() diff --git a/app/src/main/java/com/klee/volumelockr/ui/VolumeSliderFragment.kt b/app/src/main/java/com/klee/volumelockr/ui/VolumeSliderFragment.kt index 9b60e35..fe2fd36 100644 --- a/app/src/main/java/com/klee/volumelockr/ui/VolumeSliderFragment.kt +++ b/app/src/main/java/com/klee/volumelockr/ui/VolumeSliderFragment.kt @@ -9,17 +9,10 @@ import android.os.Handler import android.os.IBinder import android.os.Looper import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager -import com.klee.volumelockr.R import com.klee.volumelockr.databinding.FragmentVolumeSliderBinding import com.klee.volumelockr.service.VolumeService @@ -36,16 +29,10 @@ class VolumeSliderFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - setHasOptionsMenu(true) _binding = FragmentVolumeSliderBinding.inflate(inflater, container, false) return binding.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - handleEdgeToEdgeInsets() - } - override fun onResume() { super.onResume() mService?.let { @@ -67,49 +54,12 @@ class VolumeSliderFragment : Fragment() { super.onDestroyView() } - override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) { - val inflater: MenuInflater = menuInflater - inflater.inflate(R.menu.options, menu) - super.onCreateOptionsMenu(menu, menuInflater) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.about -> findNavController().navigate(R.id.action_x_to_about_libs) - R.id.options -> findNavController().navigate(R.id.action_sliders_to_settings) - } - return true - } - private fun setupRecyclerView(service: VolumeService) { binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) mAdapter = VolumeAdapter(service.getVolumes(), service, requireContext()) binding.recyclerView.adapter = mAdapter } - private fun handleEdgeToEdgeInsets() { - val recyclerView = binding.recyclerView - val startPadding = recyclerView.paddingLeft - val topPadding = recyclerView.paddingTop - val endPadding = recyclerView.paddingRight - val bottomPadding = recyclerView.paddingBottom - - ViewCompat.setOnApplyWindowInsetsListener(recyclerView) { v, insets -> - val bars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() - ) - - v.setPadding( - startPadding + bars.left, - topPadding + bars.top, - endPadding + bars.right, - bottomPadding + bars.bottom - ) - - WindowInsetsCompat.CONSUMED - } - } - private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName?, service: IBinder?) { diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml new file mode 100644 index 0000000..58cc391 --- /dev/null +++ b/app/src/main/res/drawable/ic_home.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_info.xml b/app/src/main/res/drawable/ic_info.xml new file mode 100644 index 0000000..8cdd57f --- /dev/null +++ b/app/src/main/res/drawable/ic_info.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000..c5d4ef6 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index cd093ae..a620d88 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,6 +6,22 @@ android:layout_height="match_parent" tools:context=".ui.MainActivity"> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml new file mode 100644 index 0000000..14151d0 --- /dev/null +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index efd56ba..c10beda 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,7 +1,8 @@ -