Aperture: Rework settings

More generic, fixed padding highlight, improved app bar layout

Change-Id: I2beeb5f3bcd119f2e40ba3cfd7fcf9521b5679ea
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d32f1e0..e74bedd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -153,7 +153,7 @@
             android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize|keyboardHidden"
             android:exported="false"
             android:label="@string/title_activity_settings"
-            android:theme="@style/Theme.Aperture.Settings" />
+            android:theme="@style/Theme.SettingsLib" />
 
     </application>
 
diff --git a/app/src/main/java/org/lineageos/aperture/SettingsActivity.kt b/app/src/main/java/org/lineageos/aperture/SettingsActivity.kt
index 967a5e6..5815491 100644
--- a/app/src/main/java/org/lineageos/aperture/SettingsActivity.kt
+++ b/app/src/main/java/org/lineageos/aperture/SettingsActivity.kt
@@ -5,8 +5,6 @@
 
 package org.lineageos.aperture
 
-import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.MenuItem
@@ -14,25 +12,37 @@
 import android.view.ViewGroup
 import android.widget.Toast
 import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.CallSuper
+import androidx.annotation.Px
+import androidx.annotation.XmlRes
 import androidx.appcompat.app.AppCompatActivity
+import androidx.coordinatorlayout.widget.CoordinatorLayout
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowCompat
 import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.updateLayoutParams
+import androidx.core.view.updatePadding
 import androidx.preference.ListPreference
 import androidx.preference.Preference
 import androidx.preference.PreferenceFragmentCompat
 import androidx.preference.SwitchPreference
+import com.google.android.material.appbar.AppBarLayout
 import com.google.android.material.appbar.MaterialToolbar
+import org.lineageos.aperture.ext.setOffset
 import org.lineageos.aperture.utils.CameraSoundsUtils
 import org.lineageos.aperture.utils.PermissionsUtils
+import kotlin.reflect.safeCast
 
 class SettingsActivity : AppCompatActivity(R.layout.activity_settings) {
+    private val appBarLayout by lazy { findViewById<AppBarLayout>(R.id.appBarLayout) }
+    private val coordinatorLayout by lazy { findViewById<CoordinatorLayout>(R.id.coordinatorLayout) }
     private val toolbar by lazy { findViewById<MaterialToolbar>(R.id.toolbar) }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        // Setup edge-to-edge
+        WindowCompat.setDecorFitsSystemWindows(window, false)
+
         if (savedInstanceState == null) {
             supportFragmentManager
                 .beginTransaction()
@@ -40,8 +50,6 @@
                 .commit()
         }
 
-        WindowCompat.setDecorFitsSystemWindows(window, false)
-
         setSupportActionBar(toolbar)
         supportActionBar?.apply {
             setDisplayHomeAsUpEnabled(true)
@@ -60,38 +68,76 @@
         }
     }
 
-    abstract class SettingsFragment : PreferenceFragmentCompat() {
+    abstract class SettingsFragment(
+        @XmlRes private val preferencesResId: Int,
+    ) : PreferenceFragmentCompat() {
+        private val settingsActivity
+            get() = SettingsActivity::class.safeCast(activity)
+
+        @Px
+        private var appBarOffset = -1
+
+        private val offsetChangedListener = AppBarLayout.OnOffsetChangedListener { _, i ->
+            appBarOffset = -i
+        }
+
         override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
             super.onViewCreated(view, savedInstanceState)
 
-            setDivider(ColorDrawable(Color.TRANSPARENT))
-            setDividerHeight(0)
+            settingsActivity?.let { settingsActivity ->
+                val appBarLayout = settingsActivity.appBarLayout
 
-            listView?.let {
-                ViewCompat.setOnApplyWindowInsetsListener(it) { _, windowInsets ->
-                    val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
-
-                    it.updateLayoutParams<ViewGroup.MarginLayoutParams> {
-                        bottomMargin = insets.bottom
-                        leftMargin = insets.left
-                        rightMargin = insets.right
-                    }
-
-                    windowInsets
+                if (appBarOffset != -1) {
+                    appBarLayout.setOffset(appBarOffset, settingsActivity.coordinatorLayout)
+                } else {
+                    appBarLayout.setExpanded(true, false)
                 }
+
+                appBarLayout.setLiftOnScrollTargetView(listView)
+
+                appBarLayout.addOnOffsetChangedListener(offsetChangedListener)
             }
         }
 
+        override fun onDestroyView() {
+            settingsActivity?.appBarLayout?.apply {
+                removeOnOffsetChangedListener(offsetChangedListener)
+
+                setLiftOnScrollTargetView(null)
+            }
+
+            super.onDestroyView()
+        }
+
+        @CallSuper
+        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+            setPreferencesFromResource(preferencesResId, rootKey)
+        }
+
+        @CallSuper
         override fun onCreateRecyclerView(
             inflater: LayoutInflater,
             parent: ViewGroup,
             savedInstanceState: Bundle?
-        ) = super.onCreateRecyclerView(inflater, parent, savedInstanceState).also {
-            it.clipToPadding = false
+        ) = super.onCreateRecyclerView(inflater, parent, savedInstanceState).apply {
+            clipToPadding = false
+            isVerticalScrollBarEnabled = false
+
+            ViewCompat.setOnApplyWindowInsetsListener(this) { _, windowInsets ->
+                val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+
+                updatePadding(
+                    bottom = insets.bottom,
+                    left = insets.left,
+                    right = insets.right,
+                )
+
+                windowInsets
+            }
         }
     }
 
-    class RootSettingsFragment : SettingsFragment() {
+    class RootSettingsFragment : SettingsFragment(R.xml.root_preferences) {
         private val enableZsl by lazy { findPreference<SwitchPreference>("enable_zsl")!! }
         private val photoCaptureMode by lazy {
             findPreference<ListPreference>("photo_capture_mode")!!
@@ -129,7 +175,8 @@
 
         @Suppress("UnsafeOptInUsageError")
         override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
-            setPreferencesFromResource(R.xml.root_preferences, rootKey)
+            super.onCreatePreferences(savedInstanceState, rootKey)
+
             saveLocation?.let {
                 // Reset location back to off if permissions aren't granted
                 it.isChecked = it.isChecked && permissionsUtils.locationPermissionsGranted()
@@ -149,9 +196,5 @@
         }
     }
 
-    class ProcessingSettingsFragment : SettingsFragment() {
-        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
-            setPreferencesFromResource(R.xml.processing_preferences, rootKey)
-        }
-    }
+    class ProcessingSettingsFragment : SettingsFragment(R.xml.processing_preferences)
 }
diff --git a/app/src/main/java/org/lineageos/aperture/ext/AppBarLayout.kt b/app/src/main/java/org/lineageos/aperture/ext/AppBarLayout.kt
new file mode 100644
index 0000000..2376941
--- /dev/null
+++ b/app/src/main/java/org/lineageos/aperture/ext/AppBarLayout.kt
@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.aperture.ext
+
+import androidx.annotation.Px
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import com.google.android.material.appbar.AppBarLayout
+import kotlin.reflect.safeCast
+
+fun AppBarLayout.setOffset(@Px offsetPx: Int, coordinatorLayout: CoordinatorLayout) {
+    val params = CoordinatorLayout.LayoutParams::class.safeCast(layoutParams) ?: return
+    AppBarLayout.Behavior::class.safeCast(params.behavior)?.onNestedPreScroll(
+        coordinatorLayout,
+        this,
+        this,
+        0,
+        offsetPx,
+        intArrayOf(0, 0),
+        0
+    )
+}
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 1475a16..0765e3c 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -6,23 +6,26 @@
 <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/coordinatorLayout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     tools:context=".SettingsActivity">
 
     <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/appBarLayout"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:fitsSystemWindows="true">
+        android:fitsSystemWindows="true"
+        app:liftOnScrollTargetViewId="@+id/settings">
 
         <com.google.android.material.appbar.CollapsingToolbarLayout
             style="?attr/collapsingToolbarLayoutLargeStyle"
             android:layout_width="match_parent"
             android:layout_height="179dp"
-            app:collapsedTitleTextAppearance="@style/Theme.Aperture.Camera.ToolbarCollapsed"
+            app:collapsedTitleTextAppearance="@style/Theme.SettingsLib.ToolbarCollapsedTextAppearance"
             app:expandedTitleMarginBottom="31dp"
             app:expandedTitleMarginStart="24dp"
-            app:expandedTitleTextAppearance="@style/Theme.Aperture.Camera.ToolbarExpanded"
+            app:expandedTitleTextAppearance="@style/Theme.SettingsLib.ToolbarExpandedTextAppearance"
             app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
             app:titleCollapseMode="fade"
             app:toolbarId="@id/toolbar">
@@ -38,16 +41,10 @@
 
     </com.google.android.material.appbar.AppBarLayout>
 
-    <androidx.core.widget.NestedScrollView
+    <FrameLayout
+        android:id="@+id/settings"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior">
-
-        <FrameLayout
-            android:id="@+id/settings"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_marginStart="8dp" />
-    </androidx.core.widget.NestedScrollView>
+        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index c015a64..d84a99d 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -23,45 +23,6 @@
         <item name="android:windowLightStatusBar">?attr/isLightTheme</item>
     </style>
 
-    <!-- Settings activity theme. -->
-    <style name="Theme.Aperture.Settings">
-        <item name="android:switchStyle">@style/Theme.Aperture.Settings.Switch</item>
-        <item name="alertDialogTheme">@style/Theme.Aperture.Settings.AlertDialog</item>
-        <item name="materialAlertDialogTheme">@style/Theme.Aperture.Settings.AlertDialog</item>
-        <item name="preferenceTheme">@style/Theme.Aperture.Settings.Preference</item>
-    </style>
-
-    <!-- Settings alert dialog theme. -->
-    <style name="Theme.Aperture.Settings.AlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
-        <item name="alertDialogStyle">@style/Theme.Aperture.Settings.AlertDialogStyle</item>
-        <item name="dialogCornerRadius">28dp</item>
-        <item name="android:background">@drawable/dialog_preference_background</item>
-    </style>
-
-    <!-- Settings alert dialog style. -->
-    <style name="Theme.Aperture.Settings.AlertDialogStyle" parent="MaterialAlertDialog.Material3">
-        <item name="shapeAppearance">@style/ShapeAppearance.Material3.MediumComponent</item>
-        <item name="shapeAppearanceOverlay">@null</item>
-    </style>
-
-    <!-- Settings message box -->
-    <style name="Theme.Aperture.Settings.MessageBox">
-        <item name="selectable">false</item>
-    </style>
-
-    <!-- Settings preference style. -->
-    <style name="Theme.Aperture.Settings.Preference" parent="PreferenceThemeOverlay">
-        <item name="android:tint">?attr/colorOnSurface</item>
-    </style>
-
-    <!-- Settings switch theme. -->
-    <style name="Theme.Aperture.Settings.Switch" parent="@android:style/Widget.Material.CompoundButton.Switch">
-        <item name="android:switchMinWidth">52dp</item>
-        <item name="android:minHeight">48dp</item>
-        <item name="android:track">@drawable/settingslib_switch_track</item>
-        <item name="android:thumb">@drawable/settingslib_switch_thumb</item>
-    </style>
-
     <!-- Secondary bar icons base theme -->
     <style name="Theme.Aperture.Camera.SecondaryBarButton">
         <item name="tint">@color/secondary_bar_button_icon</item>
@@ -90,17 +51,6 @@
         <item name="android:layout_height">40dp</item>
     </style>
 
-    <!-- Collapsing toolbar style -->
-    <style name="Theme.Aperture.Camera.ToolbarCollapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
-        <item name="android:fontFamily">sans-serif</item>
-        <item name="android:textSize">20sp</item>
-    </style>
-
-    <style name="Theme.Aperture.Camera.ToolbarExpanded" parent="Theme.Aperture.Camera.ToolbarCollapsed">
-        <item name="android:textSize">36sp</item>
-        <item name="android:drawablePadding">10dp</item>
-    </style>
-
     <!-- Info chip -->
     <style name="Theme.Aperture.Camera.InfoChip" />
 
diff --git a/app/src/main/res/values/themes_settingslib.xml b/app/src/main/res/values/themes_settingslib.xml
new file mode 100644
index 0000000..5ea7e7c
--- /dev/null
+++ b/app/src/main/res/values/themes_settingslib.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     SPDX-FileCopyrightText: 2024 The LineageOS Project
+     SPDX-License-Identifier: Apache-2.0
+-->
+<resources>
+    <!-- Settings activity theme. -->
+    <style name="Theme.SettingsLib" parent="Theme.Material3.DayNight.NoActionBar">
+        <item name="android:listPreferredItemPaddingLeft">24dp</item>
+        <item name="android:listPreferredItemPaddingStart">24dp</item>
+        <item name="android:navigationBarColor">@android:color/transparent</item>
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <item name="android:switchStyle">@style/Theme.SettingsLib.Switch</item>
+        <item name="android:windowLightStatusBar">?attr/isLightTheme</item>
+        <item name="alertDialogTheme">@style/Theme.SettingsLib.AlertDialog</item>
+        <item name="materialAlertDialogTheme">@style/Theme.SettingsLib.AlertDialog</item>
+        <item name="preferenceTheme">@style/Theme.SettingsLib.PreferenceTheme</item>
+    </style>
+
+    <!-- Settings alert dialog theme. -->
+    <style name="Theme.SettingsLib.AlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
+        <item name="android:background">@drawable/dialog_preference_background</item>
+        <item name="dialogCornerRadius">28dp</item>
+    </style>
+
+    <!-- Settings switch theme. -->
+    <style name="Theme.SettingsLib.Switch" parent="@android:style/Widget.Material.CompoundButton.Switch">
+        <item name="android:switchMinWidth">52dp</item>
+        <item name="android:minHeight">48dp</item>
+        <item name="android:track">@drawable/settingslib_switch_track</item>
+        <item name="android:thumb">@drawable/settingslib_switch_thumb</item>
+    </style>
+
+    <!-- Settings collapsing toolbar style -->
+    <style name="Theme.SettingsLib.ToolbarCollapsedTextAppearance" parent="TextAppearance.Material3.ActionBar.Title" />
+
+    <style name="Theme.SettingsLib.ToolbarExpandedTextAppearance" parent="TextAppearance.Material3.HeadlineMedium">
+        <item name="android:drawablePadding">10dp</item>
+        <item name="android:textSize">36sp</item>
+    </style>
+
+    <!-- Settings preference style. -->
+    <style name="Theme.SettingsLib.PreferenceTheme" parent="PreferenceThemeOverlay">
+        <item name="android:divider">@android:color/transparent</item>
+        <item name="android:dividerHeight">0dp</item>
+        <item name="android:listPreferredItemPaddingLeft">32dp</item>
+        <item name="android:listPreferredItemPaddingStart">32dp</item>
+        <item name="android:tint">?attr/colorOnSurface</item>
+    </style>
+</resources>
diff --git a/app/src/main/res/xml/processing_preferences.xml b/app/src/main/res/xml/processing_preferences.xml
index ccccd5c..0f3b49e 100644
--- a/app/src/main/res/xml/processing_preferences.xml
+++ b/app/src/main/res/xml/processing_preferences.xml
@@ -10,8 +10,8 @@
         app:title="@string/processing_title">
 
         <Preference
-            style="@style/Theme.Aperture.Settings.MessageBox"
             app:icon="@drawable/ic_info"
+            app:selectable="false"
             app:summary="@string/processing_info" />
 
         <ListPreference