SETTINGS - test SegmentedButton

This commit is contained in:
Alex Sim committed 2026-02-24 20:46:28 +01:00
1 parent e5fab3d3b4
commit 50f3e2c05e
12 files changed
+497 -2

No files matched your search

@@ -0,0 +1,31 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
android_library {
name: "SettingsLibSegmentedButtonPreference",
use_resource_processor: true,
defaults: [
"SettingsLibApexDefaults",
"SettingsLintDefaults",
],
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
resource_dirs: ["res"],
static_libs: [
"SettingsLibSettingsTheme",
"androidx.annotation_annotation",
"androidx.preference_preference",
],
sdk_version: "system_current",
min_sdk_version: "23",
}
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.widget.preference.segmentedbutton">
<uses-sdk android:minSdkVersion="21" />
</manifest>
@@ -0,0 +1,35 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
namespace = "com.android.settingslib.widget.preference.segmentedbutton"
compileSdk = 36
defaultConfig {
minSdk = 26
}
sourceSets {
getByName("main") {
java.srcDirs("src")
res.srcDirs("res")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
kotlinOptions {
jvmTarget = "21"
}
}
dependencies {
api project(':SettingsLib-SettingsTheme')
implementation(libs.annotation)
implementation(libs.androidx.preference)
}
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/settingslib_expressive_space_extrasmall4"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:importantForAccessibility="no">
<com.google.android.material.button.MaterialButtonToggleGroup
android:theme="@style/Theme.Material3.DynamicColors.DayNight"
android:id="@+id/button_group"
style="@style/Widget.Material3Expressive.MaterialButtonGroup.Connected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:singleSelection="true"
app:selectionRequired="true"
app:checkedButton="@id/button_1">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_1"
android:layout_weight="1"
android:visibility="gone"
app:iconPadding="0dp"
app:iconGravity="textStart"
style="@style/SettingsLibButtonStyle.Expressive.Tonal.Large"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_2"
android:layout_weight="1"
android:visibility="gone"
app:iconPadding="0dp"
app:iconGravity="textStart"
style="@style/SettingsLibButtonStyle.Expressive.Tonal.Large"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_3"
android:layout_weight="1"
android:visibility="gone"
app:iconPadding="0dp"
app:iconGravity="textStart"
style="@style/SettingsLibButtonStyle.Expressive.Tonal.Large"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_4"
android:layout_weight="1"
android:visibility="gone"
app:iconPadding="0dp"
app:iconGravity="textStart"
style="@style/SettingsLibButtonStyle.Expressive.Tonal.Large"/>
</com.google.android.material.button.MaterialButtonToggleGroup>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button_group_text"
android:orientation="horizontal"
android:gravity="center"
android:paddingTop="@dimen/settingslib_expressive_space_extrasmall4">
<TextView
android:id="@+id/button_1_text"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextAppearance.SettingsLib.TitleSmall.Emphasized"
android:textColor="@color/settingslib_materialColorOnSurface"
android:visibility="gone" />
<TextView
android:id="@+id/button_2_text"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextAppearance.SettingsLib.TitleSmall.Emphasized"
android:textColor="@color/settingslib_materialColorOnSurface"
android:visibility="gone" />
<TextView
android:id="@+id/button_3_text"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextAppearance.SettingsLib.TitleSmall.Emphasized"
android:textColor="@color/settingslib_materialColorOnSurface"
android:visibility="gone" />
<TextView
android:id="@+id/button_4_text"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextAppearance.SettingsLib.TitleSmall.Emphasized"
android:textColor="@color/settingslib_materialColorOnSurface"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,180 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.widget
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.View
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.core.view.isGone
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.android.settingslib.widget.preference.segmentedbutton.R
import com.google.android.material.button.MaterialButton
import com.google.android.material.button.MaterialButtonToggleGroup
class SegmentedButtonPreference @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
private var buttonGroup: MaterialButtonToggleGroup? = null
private var buttonLabels: MutableList<TextView> = mutableListOf()
private var buttonCheckedListener: MaterialButtonToggleGroup.OnButtonCheckedListener? = null
// Data to be applied during onBindViewHolder
private val buttonSetupData =
mutableMapOf<Int, Pair<String, SegmentedButtonIcon>>() // (index, text, icon)
private val buttonVisibilityData = mutableMapOf<Int, Boolean>() // (index, visibility)
private val buttonEnableData = mutableMapOf<Int, Boolean>() // (index, enable)
// Default checked button
private var checkedIndex: Int = -1
init {
layoutResource = R.layout.settingslib_expressive_preference_segmentedbutton
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
holder.isDividerAllowedBelow = false
holder.isDividerAllowedAbove = false
buttonGroup = holder.findViewById(R.id.button_group) as MaterialButtonToggleGroup?
buttonLabels.add(0, holder.findViewById(R.id.button_1_text) as TextView)
buttonLabels.add(1, holder.findViewById(R.id.button_2_text) as TextView)
buttonLabels.add(2, holder.findViewById(R.id.button_3_text) as TextView)
buttonLabels.add(3, holder.findViewById(R.id.button_4_text) as TextView)
// Apply stored data
applyButtonSetupData()
applyButtonVisibilityData()
applyButtonEnableData()
buttonGroup?.apply {
clearOnButtonCheckedListeners()
applyCheckIndex(checkedIndex)
buttonCheckedListener?.let { listener ->
addOnButtonCheckedListener(listener)
}
}
}
fun setUpButton(index: Int, text: String, @DrawableRes icon: Int) {
buttonSetupData.put(index, Pair(text, SegmentedButtonIcon.ResourceIcon(icon)))
notifyChanged()
}
fun setUpButton(index: Int, text: String, icon: Drawable) {
buttonSetupData.put(index, Pair(text, SegmentedButtonIcon.DrawableIcon(icon)))
notifyChanged()
}
fun setButtonVisibility(index: Int, visible: Boolean) {
buttonVisibilityData.put(index, visible)
notifyChanged()
}
fun setButtonEnabled(index: Int, enabled: Boolean) {
buttonEnableData.put(index, enabled)
notifyChanged()
}
fun setCheckedIndex(index: Int) {
checkedIndex = index
notifyChanged()
}
fun getCheckedIndex(): Int {
val checkedButtonId = buttonGroup?.checkedButtonId ?: return -1
return buttonGroup?.indexOfChild(buttonGroup?.findViewById(checkedButtonId)) ?: -1
}
fun setOnButtonClickListener(listener: MaterialButtonToggleGroup.OnButtonCheckedListener) {
buttonCheckedListener = listener
notifyChanged()
}
fun removeOnButtonClickListener() {
buttonCheckedListener = null
notifyChanged()
}
private fun applyButtonSetupData() {
// The button group is default gone to avoid NullPointerException
// if all children's visibility are GONE.
if(buttonSetupData.isNotEmpty()) {
buttonGroup?.isGone = false
}
for ((index, config) in buttonSetupData) {
applyButtonSetupData(index, config.first, config.second)
}
}
private fun applyButtonVisibilityData() {
for ((index, visible) in buttonVisibilityData) {
applyButtonVisibilityData(index, visible)
}
}
private fun applyButtonEnableData() {
for ((index, enable) in buttonEnableData) {
applyButtonEnableData(index, enable)
}
}
private fun applyButtonSetupData(index: Int, text: String, icon: SegmentedButtonIcon) {
when (icon) {
is SegmentedButtonIcon.ResourceIcon ->
(buttonGroup?.getChildAt(index) as? MaterialButton)?.setIconResource(icon.resId)
is SegmentedButtonIcon.DrawableIcon ->
(buttonGroup?.getChildAt(index) as? MaterialButton)?.icon = icon.drawable
}
buttonLabels[index].text = text
}
private fun applyButtonVisibilityData(index: Int, visible: Boolean) {
buttonGroup?.getChildAt(index)?.isGone = !visible
buttonLabels[index].isGone = !visible
}
private fun applyButtonEnableData(index: Int, enabled: Boolean) {
buttonGroup?.getChildAt(index)?.isEnabled = enabled
}
private fun applyCheckIndex(index: Int) {
val button = buttonGroup?.getChildAt(index) ?: run {
buttonGroup?.clearChecked()
return
}
if (button.id == View.NO_ID || button.isGone) {
buttonGroup?.clearChecked()
return
}
buttonGroup?.check(button.id)
}
private sealed interface SegmentedButtonIcon {
data class DrawableIcon(val drawable: Drawable) : SegmentedButtonIcon
data class ResourceIcon(@DrawableRes val resId: Int) : SegmentedButtonIcon
}
}
+1 -1
View File
@@ -30,6 +30,6 @@ android {
dependencies {
api(project(":flags"))
api(libs.material)
implementation(libs.androidx.preference)
implementation(libs.material)
}
@@ -32,6 +32,5 @@ dependencies {
api project(':SettingsLib-Preference')
api project(':SettingsLib-SettingsTheme')
implementation(libs.annotation)
implementation(libs.material)
implementation(libs.androidx.preference)
}
+1
View File
@@ -290,6 +290,7 @@ dependencies {
implementation project(':SettingsLib-SliderPreference')
implementation project(':SettingsLib-SelectorWithWidgetPreference')
implementation project(':SettingsLib-ExpandablePreference')
implementation project(':SettingsLib-SegmentedButtonPreference')
withQuickstepCompileOnly project(':HiddenApi')
// Include all compat libs
+28
View File
@@ -0,0 +1,28 @@
import os
path = r"C:\Users\Alex\StudioProjects\Murine-launcher\src\app\murinelauncher\settings\SettingsHomeFragment.kt"
with open(path, "r", encoding="utf-8") as f:
content = f.read()
old = " // Explicitly hide the remaining unused slots in the layout"
new = """ // Make configured buttons visible (all start as gone in layout)
setButtonVisibility(0, true)
setButtonVisibility(1, true)
// Explicitly hide the remaining unused slots in the layout"""
if old in content:
content = content.replace(old, new)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
print("SUCCESS: File updated.")
else:
print("ERROR: Target string not found in file.")
# Debug: print surrounding context
idx = content.find("Explicitly")
if idx >= 0:
print(f"Found 'Explicitly' at index {idx}")
print(repr(content[idx-50:idx+80]))
else:
print("'Explicitly' not found at all")
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<androidx.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.android.settingslib.widget.SegmentedButtonPreference
android:key="pref_drawer_type"
android:title="Screen Mode" />
</androidx.preference.PreferenceScreen>
+2
View File
@@ -74,6 +74,8 @@ include ':SettingsLib-SelectorWithWidgetPreference'
project(':SettingsLib-SelectorWithWidgetPreference').projectDir = new File(rootDir, 'SettingsLib/SelectorWithWidgetPreference')
include ':SettingsLib-ExpandablePreference'
project(':SettingsLib-ExpandablePreference').projectDir = new File(rootDir, 'SettingsLib/ExpandablePreference')
include ':SettingsLib-SegmentedButtonPreference'
project(':SettingsLib-SegmentedButtonPreference').projectDir = new File(rootDir, 'SettingsLib/SegmentedButtonPreference')
include ':compatLib'
include ':compatLib:compatLibVQ'
@@ -0,0 +1,55 @@
package app.murinelauncher.settings
import android.content.pm.ActivityInfo
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.preference.Preference
import com.android.launcher3.BuildConfig
import com.android.launcher3.Flags
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.R
import com.android.launcher3.states.RotationHelper
import com.android.launcher3.util.DisplayController
import com.android.settingslib.widget.SegmentedButtonPreference
public final class SettingsDrawerFragment: AbstractSettingsFragment() {
companion object {
const val DRAWER_TYPE: String = "pref_drawer_type"
}
override fun getPreferenceScreenResId() = R.xml.murine_prefs_drawer
override fun getPreferenceTitle(): Int? = R.string.pref_category_drawer_title
override fun initPreference(preference: Preference, info: DisplayController.Info): Boolean {
when (preference.key) {
DRAWER_TYPE -> {
preference as SegmentedButtonPreference
preference.apply {
// Configure the visible buttons (0-indexed)
setUpButton(0, "Option 1", R.drawable.ic_setting)
setUpButton(1, "Option 2", R.drawable.ic_setting)
// Explicitly hide the remaining unused slots in the layout
setButtonVisibility(0, true)
setButtonVisibility(1, true)
setButtonVisibility(2, false)
setButtonVisibility(3, false)
// Set initial state (e.g., from saved settings)
setCheckedIndex(0)
// Use the custom listener provided by the class
setOnButtonClickListener { _, _, _ ->
val selectedIndex = getCheckedIndex()
// Handle your logic based on the 0, 1 index
Log.d("Settings", "Selected index: $selectedIndex")
}
}
return true
}
else -> return true
}
}
}