Import some SystemSettingColorPickerPreference
This reverts commit f547af0ea1.
Signed-off-by: Hưng Phan <phandinhhungvp2001@gmail.com>
This commit is contained in:
5
res/drawable/color_picker_preview_background.xml
Normal file
5
res/drawable/color_picker_preview_background.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="6dp" />
|
||||||
|
</shape>
|
||||||
5
res/drawable/color_picker_seekbar_background.xml
Normal file
5
res/drawable/color_picker_seekbar_background.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<corners android:radius="5dp" />
|
||||||
|
<gradient android:type="linear" />
|
||||||
|
</shape>
|
||||||
11
res/drawable/color_picker_seekbar_thumb.xml
Normal file
11
res/drawable/color_picker_seekbar_thumb.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="#FFFFFF">
|
||||||
|
<item>
|
||||||
|
<shape android:shape="oval">
|
||||||
|
<solid android:color="?android:attr/colorPrimaryDark" />
|
||||||
|
<size android:width="20dp"
|
||||||
|
android:height="20dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
135
res/layout/color_picker_layout.xml
Normal file
135
res/layout/color_picker_layout.xml
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2022 FlamingoOS 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.
|
||||||
|
-->
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="@dimen/default_padding"
|
||||||
|
android:paddingBottom="@dimen/default_padding">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/color_preview"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="@dimen/color_picker_preview_height"
|
||||||
|
android:layout_marginStart="@dimen/default_margin"
|
||||||
|
android:layout_marginEnd="@dimen/default_margin"
|
||||||
|
android:background="@drawable/color_picker_preview_background"
|
||||||
|
settings:layout_constraintEnd_toEndOf="parent"
|
||||||
|
settings:layout_constraintStart_toStartOf="parent"
|
||||||
|
settings:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/color_input"
|
||||||
|
style="@style/ColorPickerEditTextStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
settings:layout_constraintStart_toStartOf="@id/color_preview"
|
||||||
|
settings:layout_constraintTop_toTopOf="@id/color_preview"
|
||||||
|
settings:layout_constraintEnd_toEndOf="@id/color_preview"
|
||||||
|
settings:layout_constraintBottom_toBottomOf="@id/color_preview" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/seekBar1"
|
||||||
|
style="@style/ColorPickerSeekBarStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
settings:layout_constraintBottom_toTopOf="@id/seekBar2"
|
||||||
|
settings:layout_constraintEnd_toEndOf="parent"
|
||||||
|
settings:layout_constraintStart_toStartOf="parent"
|
||||||
|
settings:layout_constraintTop_toBottomOf="@id/color_preview" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/seekBar2"
|
||||||
|
style="@style/ColorPickerSeekBarStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
settings:layout_constraintBottom_toTopOf="@id/seekBar3"
|
||||||
|
settings:layout_constraintEnd_toEndOf="parent"
|
||||||
|
settings:layout_constraintStart_toStartOf="parent"
|
||||||
|
settings:layout_constraintTop_toBottomOf="@id/seekBar1" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/seekBar3"
|
||||||
|
style="@style/ColorPickerSeekBarStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/default_margin"
|
||||||
|
settings:layout_constraintBottom_toTopOf="@id/barrier"
|
||||||
|
settings:layout_constraintEnd_toEndOf="parent"
|
||||||
|
settings:layout_constraintStart_toStartOf="parent"
|
||||||
|
settings:layout_constraintTop_toBottomOf="@id/seekBar2" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/color_model_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/default_margin"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
settings:layout_constraintBottom_toBottomOf="@id/confirm_button"
|
||||||
|
settings:layout_constraintStart_toStartOf="parent"
|
||||||
|
settings:layout_constraintTop_toTopOf="@id/confirm_button">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rgb_button"
|
||||||
|
style="@style/ColorPickerRadioButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="true"
|
||||||
|
android:text="@string/rgb" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/hsl_button"
|
||||||
|
style="@style/ColorPickerRadioButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/hsl" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/hsv_button"
|
||||||
|
style="@style/ColorPickerRadioButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/hsv" />
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/cancel_button"
|
||||||
|
style="@style/ColorPickerButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:text="@string/cancel"
|
||||||
|
settings:layout_constraintBottom_toBottomOf="@id/confirm_button"
|
||||||
|
settings:layout_constraintEnd_toStartOf="@id/confirm_button"
|
||||||
|
settings:layout_constraintTop_toTopOf="@id/confirm_button" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/confirm_button"
|
||||||
|
style="@style/ColorPickerButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/default_margin"
|
||||||
|
android:text="@string/confirm"
|
||||||
|
settings:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
settings:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/barrier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
settings:barrierDirection="top"
|
||||||
|
settings:constraint_referenced_ids="color_model_group, confirm_button" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
22
res/values-night/styles.xml
Normal file
22
res/values-night/styles.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2010 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<style name="ColorPickerStyle" parent="Theme.MaterialComponents.BottomSheetDialog">
|
||||||
|
<item name="colorPrimary">@*android:color/primary_device_default_settings</item>
|
||||||
|
<item name="colorPrimaryDark">@*android:color/primary_device_default_settings_light</item>
|
||||||
|
<item name="colorAccent">@*android:color/accent_device_default_dark</item>
|
||||||
|
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
|
||||||
|
<item name="android:textColorPrimary">#FFFFFF</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -22,6 +22,8 @@
|
|||||||
<dimen name="alert_dialog_padding_material">20dp</dimen>
|
<dimen name="alert_dialog_padding_material">20dp</dimen>
|
||||||
<dimen name="color_preference_width">18dip</dimen>
|
<dimen name="color_preference_width">18dip</dimen>
|
||||||
<dimen name="color_preference_height">18dip</dimen>
|
<dimen name="color_preference_height">18dip</dimen>
|
||||||
|
|
||||||
|
<dimen name="color_picker_preview_height">80dp</dimen>
|
||||||
|
|
||||||
<!-- Shortcut picker -->
|
<!-- Shortcut picker -->
|
||||||
<dimen name="shortcut_picker_left_padding">70dip</dimen>
|
<dimen name="shortcut_picker_left_padding">70dip</dimen>
|
||||||
|
|||||||
@@ -96,6 +96,13 @@
|
|||||||
<!-- Power menu Animations -->
|
<!-- Power menu Animations -->
|
||||||
<string name="power_menu_animation">Power menu</string>
|
<string name="power_menu_animation">Power menu</string>
|
||||||
|
|
||||||
|
<!-- Color picker -->
|
||||||
|
<string name="rgb">RGB</string>
|
||||||
|
<string name="hsl">HSL</string>
|
||||||
|
<string name="hsv">HSV</string>
|
||||||
|
<string name="confirm">Confirm</string>
|
||||||
|
<string name="invalid_color">Invalid color!</string>
|
||||||
|
|
||||||
<!-- QS header image -->
|
<!-- QS header image -->
|
||||||
|
|
||||||
<!-- Lockscreen -->
|
<!-- Lockscreen -->
|
||||||
|
|||||||
@@ -23,4 +23,46 @@
|
|||||||
<dimen name="fod_picker_item_padding">20dp</dimen>
|
<dimen name="fod_picker_item_padding">20dp</dimen>
|
||||||
|
|
||||||
<color name="option_border_default">#5f6368</color>
|
<color name="option_border_default">#5f6368</color>
|
||||||
|
|
||||||
|
<style name="ColorPickerStyle" parent="Theme.MaterialComponents.BottomSheetDialog">
|
||||||
|
<item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
|
||||||
|
<item name="colorPrimaryDark">@*android:color/primary_device_default_settings</item>
|
||||||
|
<item name="colorAccent">@*android:color/accent_device_default_light</item>
|
||||||
|
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
|
||||||
|
<item name="android:textColorPrimary">#000000</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ColorPickerSeekBarStyle" parent="Widget.AppCompat.SeekBar">
|
||||||
|
<item name="android:layout_marginTop">30dp</item>
|
||||||
|
<item name="android:paddingStart">16dp</item>
|
||||||
|
<item name="android:paddingEnd">18dp</item>
|
||||||
|
<item name="android:maxHeight">8dp</item>
|
||||||
|
<item name="android:progressDrawable">@drawable/color_picker_seekbar_background</item>
|
||||||
|
<item name="android:thumb">@drawable/color_picker_seekbar_thumb</item>
|
||||||
|
<item name="android:splitTrack">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ColorPickerRadioButtonStyle">
|
||||||
|
<item name="android:paddingTop">12dp</item>
|
||||||
|
<item name="android:paddingBottom">12dp</item>
|
||||||
|
<item name="android:buttonTint">?android:attr/colorAccent</item>
|
||||||
|
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ColorPickerButtonStyle">
|
||||||
|
<item name="android:background">@android:color/transparent</item>
|
||||||
|
<item name="android:minWidth">0dp</item>
|
||||||
|
<item name="android:minHeight">0dp</item>
|
||||||
|
<item name="android:padding">8dp</item>
|
||||||
|
<item name="android:textColor">?android:attr/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ColorPickerEditTextStyle">
|
||||||
|
<item name="android:gravity">center</item>
|
||||||
|
<item name="android:inputType">textCapCharacters|textNoSuggestions</item>
|
||||||
|
<item name="android:importantForAutofill">no</item>
|
||||||
|
<item name="android:textCursorDrawable">@null</item>
|
||||||
|
<item name="android:imeOptions">actionDone</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -20,15 +20,26 @@ import androidx.preference.Preference
|
|||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto
|
import com.android.internal.logging.nano.MetricsProto
|
||||||
import com.android.settings.dashboard.DashboardFragment
|
import com.android.settings.dashboard.DashboardFragment
|
||||||
|
import com.cherish.settings.fragments.ColorPickerFragment
|
||||||
|
import com.cherish.settings.preferences.ColorPickerPreference
|
||||||
|
|
||||||
abstract class CherishDashboardFragment: DashboardFragment() {
|
abstract class CherishDashboardFragment: DashboardFragment() {
|
||||||
override fun getMetricsCategory(): Int = MetricsProto.MetricsEvent.CHERISH_SETTINGS
|
override fun getMetricsCategory(): Int = MetricsProto.MetricsEvent.CHERISH_SETTINGS
|
||||||
|
|
||||||
override fun onDisplayPreferenceDialog(preference: Preference) {
|
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||||
super.onDisplayPreferenceDialog(preference)
|
if (preference is ColorPickerPreference) {
|
||||||
|
ColorPickerFragment(preference.color).apply {
|
||||||
|
setOnConfirmListener {
|
||||||
|
preference.setColor(it)
|
||||||
|
}
|
||||||
|
}.show(childFragmentManager, COLOR_PICKER_DIALOG_KEY)
|
||||||
|
} else {
|
||||||
|
super.onDisplayPreferenceDialog(preference)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val REQUEST_KEY = "CherishDashboardFragment#RequestKey"
|
const val REQUEST_KEY = "CherishDashboardFragment#RequestKey"
|
||||||
|
const val COLOR_PICKER_DIALOG_KEY = "color_picker_dialog"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
418
src/com/cherish/settings/fragments/ColorPickerFragment.kt
Normal file
418
src/com/cherish/settings/fragments/ColorPickerFragment.kt
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 FlamingoOS 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.cherish.settings.fragments
|
||||||
|
|
||||||
|
import android.annotation.ColorInt
|
||||||
|
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.Spanned
|
||||||
|
import android.text.InputFilter
|
||||||
|
import android.view.HapticFeedbackConstants.KEYBOARD_PRESS
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.RadioGroup
|
||||||
|
import android.widget.SeekBar
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import androidx.core.widget.doAfterTextChanged
|
||||||
|
import androidx.fragment.app.DialogFragment.STYLE_NORMAL
|
||||||
|
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import com.android.settings.R
|
||||||
|
|
||||||
|
class ColorPickerFragment(
|
||||||
|
defaultColor: String? = "#FFFFFF",
|
||||||
|
) : BottomSheetDialogFragment(),
|
||||||
|
RadioGroup.OnCheckedChangeListener,
|
||||||
|
SeekBar.OnSeekBarChangeListener {
|
||||||
|
|
||||||
|
private lateinit var colorPreview: View
|
||||||
|
private lateinit var colorInput: EditText
|
||||||
|
private lateinit var seekBarOne: SeekBar
|
||||||
|
private lateinit var seekBarTwo: SeekBar
|
||||||
|
private lateinit var seekBarThree: SeekBar
|
||||||
|
|
||||||
|
private var colorModel = ColorModel.RGB
|
||||||
|
private var textInputChangedInternal = false // Internal variable to prevent loops with TextWatcher
|
||||||
|
private var confirmListener: (String) -> Unit = {}
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
private var color: Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
color = if (defaultColor == null || defaultColor.isEmpty()) {
|
||||||
|
Color.WHITE
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Color.parseColor(defaultColor)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
Color.WHITE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
activity?.requestedOrientation = SCREEN_ORIENTATION_PORTRAIT
|
||||||
|
setStyle(STYLE_NORMAL, R.style.ColorPickerStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View = inflater.inflate(R.layout.color_picker_layout, container, false)
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
colorPreview = view.findViewById(R.id.color_preview)
|
||||||
|
colorInput = view.findViewById(R.id.color_input)
|
||||||
|
|
||||||
|
colorInput.doAfterTextChanged {
|
||||||
|
if (textInputChangedInternal) {
|
||||||
|
// Reset it here
|
||||||
|
textInputChangedInternal = false
|
||||||
|
return@doAfterTextChanged
|
||||||
|
}
|
||||||
|
if (it?.length != 7) return@doAfterTextChanged
|
||||||
|
color = try {
|
||||||
|
Color.parseColor(it.toString())
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
R.string.invalid_color,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
Color.WHITE
|
||||||
|
}
|
||||||
|
updateSliders()
|
||||||
|
updateSliderGradients(false)
|
||||||
|
previewColor(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
colorInput.filters = arrayOf(
|
||||||
|
InputFilter.LengthFilter(7),
|
||||||
|
InputFilter filter@ { source, start, end, _, dstart, dend ->
|
||||||
|
// Deletion
|
||||||
|
if (start == 0 && end == 0) {
|
||||||
|
return@filter null
|
||||||
|
}
|
||||||
|
if (dstart == 0) {
|
||||||
|
// First character has to be # and rest of them
|
||||||
|
// (if present) be a valid hex char
|
||||||
|
if (!source.startsWith("#")) {
|
||||||
|
return@filter ""
|
||||||
|
}
|
||||||
|
// Just a single char
|
||||||
|
if (dstart == dend) return@filter null
|
||||||
|
if (!HEX_PATTERN.matches(source.subSequence(1, end))) {
|
||||||
|
return@filter ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Does not start from 0, so every char has to be valid hex
|
||||||
|
if (!HEX_PATTERN.matches(source)) {
|
||||||
|
return@filter ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((end - start) == 7) { // Full hex input
|
||||||
|
if (!COLOR_HEX_PATTERN.matches(source)) {
|
||||||
|
return@filter ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
view.findViewById<Button>(R.id.cancel_button).setOnClickListener {
|
||||||
|
it.performHapticFeedback(KEYBOARD_PRESS)
|
||||||
|
dialog?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
view.findViewById<Button>(R.id.confirm_button).setOnClickListener {
|
||||||
|
it.performHapticFeedback(KEYBOARD_PRESS)
|
||||||
|
dialog?.dismiss()
|
||||||
|
val colorHex = colorInput.text.toString()
|
||||||
|
if (colorHex.isEmpty() || colorHex.length == 7) {
|
||||||
|
confirmListener(colorHex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the drawables as mutable so that they
|
||||||
|
* do not share a constant state or else all
|
||||||
|
* three slider gradients will look alike
|
||||||
|
*/
|
||||||
|
seekBarOne = view.findViewById<SeekBar>(R.id.seekBar1).also {
|
||||||
|
it.progressDrawable.mutate()
|
||||||
|
it.setOnSeekBarChangeListener(this)
|
||||||
|
}
|
||||||
|
seekBarTwo = view.findViewById<SeekBar>(R.id.seekBar2).also {
|
||||||
|
it.progressDrawable.mutate()
|
||||||
|
it.setOnSeekBarChangeListener(this)
|
||||||
|
}
|
||||||
|
seekBarThree = view.findViewById<SeekBar>(R.id.seekBar3).also {
|
||||||
|
it.progressDrawable.mutate()
|
||||||
|
it.setOnSeekBarChangeListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register listener for color model change
|
||||||
|
view.findViewById<RadioGroup>(R.id.color_model_group).also {
|
||||||
|
it.setOnCheckedChangeListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update sliders and preview
|
||||||
|
updateSliderMax()
|
||||||
|
updateSliders()
|
||||||
|
updateSliderGradients(true)
|
||||||
|
previewColor(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||||
|
if (!fromUser) return
|
||||||
|
color = when (colorModel) {
|
||||||
|
ColorModel.RGB -> {
|
||||||
|
Color.rgb(
|
||||||
|
seekBarOne.progress,
|
||||||
|
seekBarTwo.progress,
|
||||||
|
seekBarThree.progress
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ColorModel.HSV -> {
|
||||||
|
HSVToColor(
|
||||||
|
seekBarOne.progress.toFloat(),
|
||||||
|
seekBarTwo.progress / 100f,
|
||||||
|
seekBarThree.progress / 100f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ColorModel.HSL -> {
|
||||||
|
HSLToColor(
|
||||||
|
seekBarOne.progress.toFloat(),
|
||||||
|
seekBarTwo.progress / 100f,
|
||||||
|
seekBarThree.progress / 100f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (colorModel != ColorModel.RGB) {
|
||||||
|
updateSliderGradients(false)
|
||||||
|
}
|
||||||
|
previewColor(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCheckedChanged(group: RadioGroup, checkedId: Int) {
|
||||||
|
colorModel = when (checkedId) {
|
||||||
|
R.id.rgb_button -> ColorModel.RGB
|
||||||
|
R.id.hsv_button -> ColorModel.HSV
|
||||||
|
R.id.hsl_button -> ColorModel.HSL
|
||||||
|
else -> ColorModel.RGB
|
||||||
|
}
|
||||||
|
updateSliderMax()
|
||||||
|
updateSliders()
|
||||||
|
updateSliderGradients(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set a confirmation listener that will be invoked when confirm
|
||||||
|
* button of the dialog is pressed.
|
||||||
|
*
|
||||||
|
* @param listener the listener to be invoked. Hex value of the
|
||||||
|
* color (including # prefix and RGB) will be the type parameter
|
||||||
|
* of the listener. Do note that the parameter can also be empty.
|
||||||
|
*/
|
||||||
|
fun setOnConfirmListener(listener: (String) -> Unit) {
|
||||||
|
confirmListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to update sliders if color model changes or
|
||||||
|
* user inputs a color hex. For the latter it must be called
|
||||||
|
* only after the accent colors are updated.
|
||||||
|
*/
|
||||||
|
private fun updateSliders() {
|
||||||
|
when (colorModel) {
|
||||||
|
ColorModel.RGB -> updateSliderProgressFromColor()
|
||||||
|
ColorModel.HSV -> {
|
||||||
|
val array = FloatArray(3)
|
||||||
|
Color.colorToHSV(color, array)
|
||||||
|
updateSliderProgressFromHSVorHSL(array)
|
||||||
|
}
|
||||||
|
ColorModel.HSL -> {
|
||||||
|
val array = FloatArray(3)
|
||||||
|
ColorUtils.colorToHSL(color, array)
|
||||||
|
updateSliderProgressFromHSVorHSL(array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For updating RGB slider progress
|
||||||
|
private fun updateSliderProgressFromColor() {
|
||||||
|
seekBarOne.progress = Color.red(color)
|
||||||
|
seekBarTwo.progress = Color.green(color)
|
||||||
|
seekBarThree.progress = Color.blue(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For updating HSV / HSL slider progress
|
||||||
|
private fun updateSliderProgressFromHSVorHSL(hsvOrHSL: FloatArray) {
|
||||||
|
seekBarOne.progress = hsvOrHSL[0].toInt()
|
||||||
|
seekBarTwo.progress = (hsvOrHSL[1] * 100).toInt()
|
||||||
|
seekBarThree.progress = (hsvOrHSL[2] * 100).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// For updating the slider GradientDrawable's based on ColorModel
|
||||||
|
private fun updateSliderGradients(colorModelChanged: Boolean) {
|
||||||
|
if (colorModel == ColorModel.RGB) {
|
||||||
|
if (colorModelChanged) {
|
||||||
|
updateRGBGradient(seekBarOne.progressDrawable, Color.RED)
|
||||||
|
updateRGBGradient(seekBarTwo.progressDrawable, Color.GREEN)
|
||||||
|
updateRGBGradient(seekBarThree.progressDrawable, Color.BLUE)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (colorModelChanged) {
|
||||||
|
updateHueGradient()
|
||||||
|
}
|
||||||
|
updateSaturationGradient()
|
||||||
|
if (colorModel == ColorModel.HSV) {
|
||||||
|
updateValueGradient()
|
||||||
|
} else {
|
||||||
|
updateLuminanceGradient()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateLuminanceGradient() {
|
||||||
|
val drawable = seekBarThree.progressDrawable as GradientDrawable
|
||||||
|
drawable.colors = intArrayOf(
|
||||||
|
Color.BLACK,
|
||||||
|
HSLToColor(
|
||||||
|
seekBarOne.progress.toFloat(),
|
||||||
|
seekBarTwo.progress / 100f,
|
||||||
|
0.5f
|
||||||
|
),
|
||||||
|
Color.WHITE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateValueGradient() {
|
||||||
|
val drawable = seekBarThree.progressDrawable as GradientDrawable
|
||||||
|
drawable.colors = intArrayOf(
|
||||||
|
Color.BLACK,
|
||||||
|
HSVToColor(
|
||||||
|
seekBarOne.progress.toFloat(),
|
||||||
|
seekBarTwo.progress / 100f,
|
||||||
|
1f
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSaturationGradient() {
|
||||||
|
val drawable = seekBarTwo.progressDrawable as GradientDrawable
|
||||||
|
drawable.colors = intArrayOf(
|
||||||
|
Color.WHITE,
|
||||||
|
if (colorModel == ColorModel.HSV) {
|
||||||
|
HSVToColor(
|
||||||
|
seekBarOne.progress.toFloat(),
|
||||||
|
1f,
|
||||||
|
seekBarThree.progress / 100f
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
HSLToColor(
|
||||||
|
seekBarOne.progress.toFloat(),
|
||||||
|
1f,
|
||||||
|
seekBarThree.progress / 100f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateHueGradient() {
|
||||||
|
val drawable = seekBarOne.progressDrawable as GradientDrawable
|
||||||
|
drawable.colors = hueGradientColors
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateRGBGradient(progressDrawable: Drawable, color: Int) {
|
||||||
|
val drawable = progressDrawable as GradientDrawable
|
||||||
|
drawable.colors = intArrayOf(Color.BLACK, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// inputFromUser should be set to true when user has entered a hex color
|
||||||
|
private fun previewColor(inputFromUser: Boolean) {
|
||||||
|
colorPreview.backgroundTintList = ColorStateList.valueOf(color)
|
||||||
|
colorInput.setTextColor(
|
||||||
|
if (ColorUtils.calculateLuminance(color) > 0.5) {
|
||||||
|
Color.BLACK
|
||||||
|
} else {
|
||||||
|
Color.WHITE
|
||||||
|
}
|
||||||
|
)
|
||||||
|
textInputChangedInternal = true
|
||||||
|
if (!inputFromUser) {
|
||||||
|
colorInput.setText(colorToHex(color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSliderMax() {
|
||||||
|
val isRGB = colorModel == ColorModel.RGB
|
||||||
|
seekBarOne.max = if (isRGB) 255 else 360
|
||||||
|
seekBarTwo.max = if (isRGB) 255 else 100
|
||||||
|
seekBarThree.max = if (isRGB) 255 else 100
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class ColorModel {
|
||||||
|
RGB,
|
||||||
|
HSL,
|
||||||
|
HSV
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val HEX_PATTERN = Regex("[0-9a-fA-F]+")
|
||||||
|
private val COLOR_HEX_PATTERN = Regex("^[#][0-9a-fA-F]{6}")
|
||||||
|
|
||||||
|
private val hueGradientColors = IntArray(7) {
|
||||||
|
HSVToColor(it * 60f, 1f, 1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun HSVToColor(
|
||||||
|
hue: Float,
|
||||||
|
sat: Float,
|
||||||
|
value: Float,
|
||||||
|
): Int = Color.HSVToColor(floatArrayOf(hue, sat, value))
|
||||||
|
|
||||||
|
private fun HSLToColor(
|
||||||
|
hue: Float,
|
||||||
|
sat: Float,
|
||||||
|
lum: Float,
|
||||||
|
): Int = ColorUtils.HSLToColor(floatArrayOf(hue, sat, lum))
|
||||||
|
|
||||||
|
private fun colorToHex(color: Int) = String.format("#%06X", (0xFFFFFF and color))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 FlamingoOS 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.cherish.settings.preferences
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.TypedArray
|
||||||
|
import android.util.AttributeSet
|
||||||
|
|
||||||
|
import androidx.preference.DialogPreference
|
||||||
|
|
||||||
|
open class ColorPickerPreference @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
) : DialogPreference(context, attrs) {
|
||||||
|
|
||||||
|
var color: String? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
override protected fun onGetDefaultValue(a: TypedArray, index: Int): Any? {
|
||||||
|
return a.getString(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun onSetInitialValue(restoreValue: Boolean, defaultValue: Any?) {
|
||||||
|
val def = defaultValue as? String
|
||||||
|
color = if (restoreValue) getPersistedString(def) else def
|
||||||
|
setSummary(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setColor(color: String) {
|
||||||
|
if (!callChangeListener(color)) return
|
||||||
|
this.color = color
|
||||||
|
setSummary(color)
|
||||||
|
persistString(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 FlamingoOS 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.cherish.settings.preferences
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
|
||||||
|
public class SecureSettingColorPickerPreference @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
): ColorPickerPreference(context, attrs) {
|
||||||
|
init {
|
||||||
|
setPreferenceDataStore(SecureSettingsStore(context.contentResolver))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 FlamingoOS 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.cherish.settings.preferences
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
|
||||||
|
public class SystemSettingColorPickerPreference @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
): ColorPickerPreference(context, attrs) {
|
||||||
|
init {
|
||||||
|
setPreferenceDataStore(SystemSettingsStore(context.contentResolver))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user