Twelve: Import settings activity from Aperture

Change-Id: Ic0ce8bb09b61b6297212e3d975eef4d1c0c8c956
diff --git a/app/Android.bp b/app/Android.bp
index a9cc7dd..48ceb96 100644
--- a/app/Android.bp
+++ b/app/Android.bp
@@ -61,6 +61,7 @@
         "Twelve_androidx.media3_media3-ui",
         "androidx.navigation_navigation-fragment-ktx",
         "androidx.navigation_navigation-ui-ktx",
+        "androidx.preference_preference",
         "androidx.recyclerview_recyclerview",
         "androidx.room_room-ktx",
         "androidx.room_room-runtime",
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 23cf2b1..5c26ffe 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -93,6 +93,7 @@
     implementation(libs.androidx.media3.ui)
     implementation(libs.androidx.navigation.fragment.ktx)
     implementation(libs.androidx.navigation.ui.ktx)
+    implementation(libs.androidx.preference)
     implementation(libs.androidx.recyclerview)
     implementation(libs.androidx.room.runtime)
     annotationProcessor(libs.androidx.room.compiler)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0361f73..e00670f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -60,6 +60,13 @@
         </activity>
 
         <activity
+            android:name=".SettingsActivity"
+            android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize|keyboardHidden"
+            android:exported="false"
+            android:label="@string/title_activity_settings"
+            android:theme="@style/Theme.SettingsLib" />
+
+        <activity
             android:name=".ViewActivity"
             android:excludeFromRecents="true"
             android:exported="true"
diff --git a/app/src/main/java/org/lineageos/twelve/SettingsActivity.kt b/app/src/main/java/org/lineageos/twelve/SettingsActivity.kt
new file mode 100644
index 0000000..13d9a2a
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/SettingsActivity.kt
@@ -0,0 +1,134 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+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.updatePadding
+import androidx.preference.PreferenceFragmentCompat
+import com.google.android.material.appbar.AppBarLayout
+import com.google.android.material.appbar.MaterialToolbar
+import org.lineageos.twelve.ext.setOffset
+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()
+                .replace(R.id.settings, RootSettingsFragment())
+                .commit()
+        }
+
+        setSupportActionBar(toolbar)
+        supportActionBar?.apply {
+            setDisplayHomeAsUpEnabled(true)
+            setDisplayShowHomeEnabled(true)
+        }
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
+        android.R.id.home -> {
+            onBackPressedDispatcher.onBackPressed()
+            true
+        }
+
+        else -> {
+            super.onOptionsItemSelected(item)
+        }
+    }
+
+    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)
+
+            settingsActivity?.let { settingsActivity ->
+                val appBarLayout = settingsActivity.appBarLayout
+
+                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).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(R.xml.root_preferences)
+}
diff --git a/app/src/main/java/org/lineageos/twelve/ext/AppBarLayout.kt b/app/src/main/java/org/lineageos/twelve/ext/AppBarLayout.kt
new file mode 100644
index 0000000..ad12102
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/ext/AppBarLayout.kt
@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.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/color/settingslib_switch_thumb_color.xml b/app/src/main/res/color/settingslib_switch_thumb_color.xml
new file mode 100644
index 0000000..ce1668d
--- /dev/null
+++ b/app/src/main/res/color/settingslib_switch_thumb_color.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     SPDX-FileCopyrightText: 2021 The Android Open Source Project
+     SPDX-License-Identifier: Apache-2.0
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Disabled status of thumb -->
+    <item android:color="?attr/colorOutline" android:state_enabled="false" />
+    <!-- Toggle off status of thumb -->
+    <item android:color="?attr/colorOutline" android:state_checked="false" />
+    <!-- Enabled or toggle on status of thumb -->
+    <item android:color="?attr/colorOnPrimary" />
+</selector>
diff --git a/app/src/main/res/color/settingslib_switch_track_color.xml b/app/src/main/res/color/settingslib_switch_track_color.xml
new file mode 100644
index 0000000..ba62e66
--- /dev/null
+++ b/app/src/main/res/color/settingslib_switch_track_color.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     SPDX-FileCopyrightText: 2021 The Android Open Source Project
+     SPDX-License-Identifier: Apache-2.0
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Disabled status of thumb -->
+    <item android:alpha="?android:attr/disabledAlpha" android:color="?attr/colorSurfaceVariant" android:state_enabled="false" />
+    <!-- Toggle off status of thumb -->
+    <item android:color="?attr/colorSurfaceVariant" android:state_checked="false" />
+    <!-- Enabled or toggle on status of thumb -->
+    <item android:color="?attr/colorPrimary" />
+</selector>
diff --git a/app/src/main/res/drawable/settingslib_switch_thumb.xml b/app/src/main/res/drawable/settingslib_switch_thumb.xml
new file mode 100644
index 0000000..35ca08d
--- /dev/null
+++ b/app/src/main/res/drawable/settingslib_switch_thumb.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     SPDX-FileCopyrightText: 2021 The Android Open Source Project
+     SPDX-License-Identifier: Apache-2.0
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:bottom="4dp"
+        android:left="4dp"
+        android:right="4dp"
+        android:top="4dp">
+        <shape android:shape="oval">
+            <size
+                android:width="20dp"
+                android:height="20dp" />
+            <solid android:color="@color/settingslib_switch_thumb_color" />
+        </shape>
+    </item>
+</layer-list>
diff --git a/app/src/main/res/drawable/settingslib_switch_track.xml b/app/src/main/res/drawable/settingslib_switch_track.xml
new file mode 100644
index 0000000..52675f5
--- /dev/null
+++ b/app/src/main/res/drawable/settingslib_switch_track.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     SPDX-FileCopyrightText: 2021 The Android Open Source Project
+     SPDX-License-Identifier: Apache-2.0
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="52dp"
+    android:height="28dp"
+    android:shape="rectangle">
+
+    <solid android:color="@color/settingslib_switch_track_color" />
+    <corners android:radius="35dp" />
+</shape>
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 0000000..0765e3c
--- /dev/null
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     SPDX-FileCopyrightText: 2022-2023 The LineageOS Project
+     SPDX-License-Identifier: Apache-2.0
+-->
+<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"
+        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.SettingsLib.ToolbarCollapsedTextAppearance"
+            app:expandedTitleMarginBottom="31dp"
+            app:expandedTitleMarginStart="24dp"
+            app:expandedTitleTextAppearance="@style/Theme.SettingsLib.ToolbarExpandedTextAppearance"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
+            app:titleCollapseMode="fade"
+            app:toolbarId="@id/toolbar">
+
+            <com.google.android.material.appbar.MaterialToolbar
+                android:id="@+id/toolbar"
+                android:layout_width="match_parent"
+                android:layout_height="?actionBarSize"
+                android:elevation="0dp"
+                app:layout_collapseMode="pin" />
+
+        </com.google.android.material.appbar.CollapsingToolbarLayout>
+
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <FrameLayout
+        android:id="@+id/settings"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4271651..10b38aa 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -139,6 +139,9 @@
     <string name="argument_validation_error_message">Error while parsing the following arguments:\n%1$s</string>
     <string name="argument_validation_error_item">%1$s: %2$s</string>
 
+    <!-- Settings title -->
+    <string name="title_activity_settings">Settings</string>
+
     <!-- Now playing widget -->
     <string name="now_playing_widget_description">Now playing</string>
     <string name="now_playing_widget_dummy_title">Title</string>
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..740da76
--- /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:colorBackground">?attr/colorSurface</item>
+        <item name="dialogCornerRadius">16dp</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/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
new file mode 100644
index 0000000..d6118c0
--- /dev/null
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     SPDX-FileCopyrightText: 2024 The LineageOS Project
+     SPDX-License-Identifier: Apache-2.0
+-->
+<PreferenceScreen />
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 1411095..e169874 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -20,6 +20,7 @@
 navigation = "2.8.0"
 nier-visualizer = "v0.1.3"
 okhttp = "4.12.0"
+preference = "1.2.1"
 recyclerview = "1.3.2"
 room = "2.6.1"
 viewpager2 = "1.1.0"
@@ -41,6 +42,7 @@
 androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3" }
 androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation" }
 androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation" }
+androidx-preference = { group = "androidx.preference", name = "preference", version.ref = "preference" }
 androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" }
 androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
 androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }