add a separate fragment for setting user-defined colors

issues:
when using day/night mode, active keyboard is shown in preview, not the acually modified varian
reloading / updating the preview is either slow or will not show the keyboard again
This commit is contained in:
Helium314 2023-09-12 14:21:40 +02:00
parent d33650586b
commit b18b7dc820
20 changed files with 383 additions and 206 deletions

View file

@ -27,6 +27,9 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', 'src/main/proguard.flags' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', 'src/main/proguard.flags'
applicationIdSuffix ".debug" applicationIdSuffix ".debug"
} }
buildFeatures {
viewBinding true
}
archivesBaseName = "openboard_" + defaultConfig.versionName archivesBaseName = "openboard_" + defaultConfig.versionName
} }
@ -60,5 +63,6 @@ dependencies {
implementation 'androidx.preference:preference:1.2.1' // includes appcompat implementation 'androidx.preference:preference:1.2.1' // includes appcompat
implementation 'androidx.recyclerview:recyclerview:1.3.1' implementation 'androidx.recyclerview:recyclerview:1.3.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.github.skydoves:colorpickerview:2.2.4'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
} }

View file

@ -141,23 +141,22 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
return KEYBOARD_THEMES[DEFAULT_THEME_ID]; return KEYBOARD_THEMES[DEFAULT_THEME_ID];
} }
// todo (later): material you, system accent, ...
public static Colors getThemeColors(final String themeColors, final String themeStyle, final Context context, final SharedPreferences prefs) { public static Colors getThemeColors(final String themeColors, final String themeStyle, final Context context, final SharedPreferences prefs) {
final boolean hasBorders = prefs.getBoolean(Settings.PREF_THEME_KEY_BORDERS, false); final boolean hasBorders = prefs.getBoolean(Settings.PREF_THEME_KEY_BORDERS, false);
switch (themeColors) { switch (themeColors) {
case THEME_USER: case THEME_USER:
final int accent = prefs.getInt(Settings.PREF_THEME_USER_COLOR_ACCENT, Color.BLUE); final int accent = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_ACCENT_SUFFIX, false);
final int keyBgColor = prefs.getInt(Settings.PREF_THEME_USER_COLOR_KEYS, Color.LTGRAY); final int keyBgColor = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_KEYS_SUFFIX, false);
final int keyTextColor = prefs.getInt(Settings.PREF_THEME_USER_COLOR_TEXT, Color.WHITE); final int keyTextColor = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_TEXT_SUFFIX, false);
final int hintTextColor = prefs.getInt(Settings.PREF_THEME_USER_COLOR_HINT_TEXT, Color.WHITE); final int hintTextColor = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_HINT_TEXT_SUFFIX, false);
final int background = prefs.getInt(Settings.PREF_THEME_USER_COLOR_BACKGROUND, Color.DKGRAY); final int background = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_BACKGROUND_SUFFIX, false);
return new Colors(themeStyle, hasBorders, accent, background, keyBgColor, ColorUtilKt.brightenOrDarken(keyBgColor, true), keyBgColor, keyTextColor, hintTextColor); return new Colors(themeStyle, hasBorders, accent, background, keyBgColor, ColorUtilKt.brightenOrDarken(keyBgColor, true), keyBgColor, keyTextColor, hintTextColor);
case THEME_USER_NIGHT: case THEME_USER_NIGHT:
final int accent2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_ACCENT, Color.BLUE); final int accent2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_ACCENT_SUFFIX, true);
final int keyBgColor2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_KEYS, Color.LTGRAY); final int keyBgColor2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_KEYS_SUFFIX, true);
final int keyTextColor2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_TEXT, Color.WHITE); final int keyTextColor2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_TEXT_SUFFIX, true);
final int hintTextColor2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_HINT_TEXT, Color.WHITE); final int hintTextColor2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_HINT_TEXT_SUFFIX, true);
final int background2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_BACKGROUND, Color.DKGRAY); final int background2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_BACKGROUND_SUFFIX, true);
return new Colors(themeStyle, hasBorders, accent2, background2, keyBgColor2, ColorUtilKt.brightenOrDarken(keyBgColor2, true), keyBgColor2, keyTextColor2, hintTextColor2); return new Colors(themeStyle, hasBorders, accent2, background2, keyBgColor2, ColorUtilKt.brightenOrDarken(keyBgColor2, true), keyBgColor2, keyTextColor2, hintTextColor2);
case THEME_DARK: case THEME_DARK:
return new Colors( return new Colors(

View file

@ -2,10 +2,8 @@ package org.dslul.openboard.inputmethod.latin.settings
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.TwoStatePreference import androidx.preference.TwoStatePreference
@ -137,65 +135,7 @@ class AppearanceSettingsFragment : SubScreenFragment() {
} }
themeVariantNightPref?.isVisible = dayNightPref?.isChecked == true themeVariantNightPref?.isVisible = dayNightPref?.isChecked == true
userColorsPref.isVisible = themeVariantPref.value == KeyboardTheme.THEME_USER userColorsPref.isVisible = themeVariantPref.value == KeyboardTheme.THEME_USER
userColorsPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
adjustColors(false)
true
}
userColorsPrefNight?.isVisible = dayNightPref?.isChecked == true && themeVariantNightPref?.value == KeyboardTheme.THEME_USER_NIGHT userColorsPrefNight?.isVisible = dayNightPref?.isChecked == true && themeVariantNightPref?.value == KeyboardTheme.THEME_USER_NIGHT
userColorsPrefNight?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
adjustColors(true)
true
}
}
// todo: improve color selection, should at very least show a preview of the color
// but maybe a separate fragment would be better
// idea:
// left: which color (background, key, text,...)
// right: color preview (always the correct one, even if determined automatically)
// maybe copy parts from simple keyboard, see e.g. screenshot 4 in https://github.com/SimpleMobileTools/Simple-Keyboard/tree/main/fastlane/metadata/android/en-US/images/phoneScreenshots
// below (for some colors, with indent):
// enable user-defining (most colors, but definitely not background)
// use system accent (for accent and text colors)
// on click: color selector
// maybe copy parts from simple keyboard, see e.g. screenshot 4 in https://github.com/SimpleMobileTools/Simple-Keyboard/tree/main/fastlane/metadata/android/en-US/images/phoneScreenshots
// but full range would be preferable
// use some color picker library? would likely allow nicer tuning
private fun adjustColors(dark: Boolean) {
val items = listOf(
R.string.select_color_background,
R.string.select_color_key_background,
R.string.select_color_key,
R.string.select_color_key_hint,
R.string.select_color_accent,
).map { requireContext().getString(it) }
AlertDialog.Builder(requireContext())
.setPositiveButton(android.R.string.ok, null)
.setTitle(R.string.select_color_to_adjust)
.setItems(items.toTypedArray()) { _, i ->
val (pref, default) =
if (dark)
when (i) {
0 -> Settings.PREF_THEME_USER_DARK_COLOR_BACKGROUND to Color.DKGRAY
1 -> Settings.PREF_THEME_USER_DARK_COLOR_KEYS to Color.LTGRAY
2 -> Settings.PREF_THEME_USER_DARK_COLOR_TEXT to Color.WHITE
3 -> Settings.PREF_THEME_USER_DARK_COLOR_HINT_TEXT to Color.WHITE
4 -> Settings.PREF_THEME_USER_DARK_COLOR_ACCENT to Color.BLUE
else -> return@setItems
}
else
when (i) {
0 -> Settings.PREF_THEME_USER_COLOR_BACKGROUND to Color.DKGRAY
1 -> Settings.PREF_THEME_USER_COLOR_KEYS to Color.LTGRAY
2 -> Settings.PREF_THEME_USER_COLOR_TEXT to Color.WHITE
3 -> Settings.PREF_THEME_USER_COLOR_HINT_TEXT to Color.WHITE
4 -> Settings.PREF_THEME_USER_COLOR_ACCENT to Color.BLUE
else -> return@setItems
}
val d = ColorPickerDialog(requireContext(), items[i], sharedPreferences, pref, default)
d.show()
}
.show()
} }
private fun setupKeyboardHeight(prefKey: String, defaultValue: Float) { private fun setupKeyboardHeight(prefKey: String, defaultValue: Float) {

View file

@ -1,113 +0,0 @@
/*
* Copyright (C) 2013 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.
*/
// adapted from https://github.com/rkkr/simple-keyboard/blob/master/app/src/main/java/rkr/simplekeyboard/inputmethod/latin/settings/ColorDialogPreference.java
package org.dslul.openboard.inputmethod.latin.settings;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import org.dslul.openboard.inputmethod.latin.R;
public class ColorPickerDialog extends AlertDialog implements SeekBar.OnSeekBarChangeListener {
protected ColorPickerDialog(final Context context, final String title, final SharedPreferences prefs,
final String colorPref, final int defaultColor) {
super(context);
setTitle(title);
View view = getLayoutInflater().inflate(R.layout.color_dialog, null);
mSeekBarRed = (SeekBar)view.findViewById(R.id.seek_bar_dialog_bar_red);
mSeekBarRed.setMax(255);
mSeekBarRed.setOnSeekBarChangeListener(this);
mSeekBarRed.getProgressDrawable().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
mSeekBarRed.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
mSeekBarGreen = (SeekBar)view.findViewById(R.id.seek_bar_dialog_bar_green);
mSeekBarGreen.setMax(255);
mSeekBarGreen.setOnSeekBarChangeListener(this);
mSeekBarGreen.getThumb().setColorFilter(Color.GREEN, PorterDuff.Mode.SRC_IN);
mSeekBarGreen.getProgressDrawable().setColorFilter(Color.GREEN, PorterDuff.Mode.SRC_IN);
mSeekBarBlue = (SeekBar)view.findViewById(R.id.seek_bar_dialog_bar_blue);
mSeekBarBlue.setMax(255);
mSeekBarBlue.setOnSeekBarChangeListener(this);
mSeekBarBlue.getThumb().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
mSeekBarBlue.getProgressDrawable().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
mValueView = (TextView)view.findViewById(R.id.seek_bar_dialog_value);
setView(view);
// init with correct values
// using onShowListener?
setOnShowListener(dialogInterface -> {
int color = prefs.getInt(colorPref, defaultColor);
mSeekBarRed.setProgress(Color.red(color));
mSeekBarGreen.setProgress(Color.green(color));
mSeekBarBlue.setProgress(Color.blue(color));
setHeaderText(color);
});
// set on ok and on cancel listeners
setButton(BUTTON_NEGATIVE, context.getText(android.R.string.cancel), (dialogInterface, i) -> dismiss());
setButton(BUTTON_POSITIVE, context.getText(android.R.string.ok), (dialogInterface, i) -> {
final int value = Color.rgb(
mSeekBarRed.getProgress(),
mSeekBarGreen.getProgress(),
mSeekBarBlue.getProgress());
prefs.edit().putInt(colorPref, value).apply();
dismiss();
});
}
private final TextView mValueView;
private final SeekBar mSeekBarRed;
private final SeekBar mSeekBarGreen;
private final SeekBar mSeekBarBlue;
@Override
public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) {
int color = Color.rgb(
mSeekBarRed.getProgress(),
mSeekBarGreen.getProgress(),
mSeekBarBlue.getProgress());
setHeaderText(color);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
private void setHeaderText(int color) {
mValueView.setText(getValueText(color));
boolean bright = Color.red(color) + Color.green(color) + Color.blue(color) > 128 * 3;
mValueView.setTextColor(bright ? Color.BLACK : Color.WHITE);
mValueView.setBackgroundColor(color);
}
private String getValueText(final int value) {
String temp = Integer.toHexString(value);
for (; temp.length() < 8; temp = "0" + temp);
return temp.substring(2).toUpperCase();
}
}

View file

@ -0,0 +1,169 @@
package org.dslul.openboard.inputmethod.latin.settings
import android.app.Activity
import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.widget.CompoundButton
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.view.forEachIndexed
import androidx.core.view.setPadding
import androidx.fragment.app.Fragment
import com.skydoves.colorpickerview.ColorPickerDialog
import com.skydoves.colorpickerview.flag.BubbleFlag
import com.skydoves.colorpickerview.flag.FlagMode
import com.skydoves.colorpickerview.listeners.ColorEnvelopeListener
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher
import org.dslul.openboard.inputmethod.latin.R
import org.dslul.openboard.inputmethod.latin.RichInputMethodManager
import org.dslul.openboard.inputmethod.latin.databinding.ColorSettingBinding
import org.dslul.openboard.inputmethod.latin.databinding.ColorSettingsBinding
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils
import org.dslul.openboard.inputmethod.latin.utils.ExecutorUtils
import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils
open class ColorsSettingsFragment : Fragment(R.layout.color_settings) {
private val binding by viewBinding(ColorSettingsBinding::bind)
open val isNight = false
open val titleResId = R.string.select_user_colors
private val prefs by lazy { DeviceProtectedUtils.getSharedPreferences(requireContext()) }
private val colorPrefs = listOf(
Settings.PREF_COLOR_BACKGROUND_SUFFIX,
Settings.PREF_COLOR_KEYS_SUFFIX,
Settings.PREF_COLOR_TEXT_SUFFIX,
Settings.PREF_COLOR_HINT_TEXT_SUFFIX,
Settings.PREF_COLOR_ACCENT_SUFFIX,
)
override fun onResume() {
super.onResume()
val activity: Activity? = activity
if (activity is AppCompatActivity) {
val actionBar = activity.supportActionBar ?: return
actionBar.setTitle(titleResId)
}
if (isNight != ResourceUtils.isNight(requireContext().resources))
// reload to get the right configuration
// todo: this does not work, keyboard also reloading with some other context
reloadKeyboard(false)
}
override fun onPause() {
super.onPause()
if (isNight != ResourceUtils.isNight(requireContext().resources))
// reload again so the correct configuration is applied
// todo: this does not work, keyboard also reloading with some other context
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val colorPrefNames = listOf(
R.string.select_color_background,
R.string.select_color_key_background,
R.string.select_color_key,
R.string.select_color_key_hint,
R.string.select_color_accent,
).map { requireContext().getString(it) }
val prefPrefix = if (isNight) Settings.PREF_THEME_USER_COLOR_NIGHT_PREFIX else Settings.PREF_THEME_USER_COLOR_PREFIX
colorPrefs.forEachIndexed { index, colorPref ->
val csb = ColorSettingBinding.inflate(layoutInflater, binding.colorSettingsContainer, true)
csb.colorSwitch.isChecked = !prefs.getBoolean(prefPrefix + colorPref + Settings.PREF_AUTO_USER_COLOR_SUFFIX, true)
csb.colorPreview.setColorFilter(Settings.readUserColor(prefs, requireContext(), colorPrefs[index], isNight))
csb.colorText.text = colorPrefNames[index]
if (!csb.colorSwitch.isChecked) {
csb.colorSummary.setText(R.string.auto_user_color)
}
val switchListener = CompoundButton.OnCheckedChangeListener { _, b ->
val hidden = RichInputMethodManager.getInstance().inputMethodManager.hideSoftInputFromWindow(binding.dummyText.windowToken, 0)
prefs.edit { putBoolean(prefPrefix + colorPref + Settings.PREF_AUTO_USER_COLOR_SUFFIX, !b) }
if (b) csb.colorSummary.text = ""
else csb.colorSummary.setText(R.string.auto_user_color)
reloadKeyboard(hidden)
updateColorPreviews()
}
csb.colorSwitch.setOnCheckedChangeListener(switchListener)
val clickListener = View.OnClickListener {
val hidden = RichInputMethodManager.getInstance().inputMethodManager.hideSoftInputFromWindow(binding.dummyText.windowToken, 0)
val b = ColorPickerDialog.Builder(requireContext())
.setTitle(colorPrefNames[index])
// todo: later it should be activated, but currently setting alpha leads to glitches,
// e.g. when setting alpha on key text it's not applied for key icons, but for emojis
.attachAlphaSlideBar(false)
.setPositiveButton(android.R.string.ok, ColorEnvelopeListener { envelope, _ ->
prefs.edit { putInt(prefPrefix + colorPrefs[index], envelope.color) }
if (!csb.colorSwitch.isChecked) {
prefs.edit { putBoolean(prefPrefix + colorPref + Settings.PREF_AUTO_USER_COLOR_SUFFIX, false) }
csb.colorSwitch.setOnCheckedChangeListener(null)
csb.colorSwitch.isChecked = true
csb.colorSummary.text = ""
csb.colorSwitch.setOnCheckedChangeListener(switchListener)
reloadKeyboard(hidden)
updateColorPreviews()
return@ColorEnvelopeListener
}
reloadKeyboard(hidden)
updateColorPreviews()
})
.setNegativeButton(android.R.string.cancel) { _, _ ->
if (hidden)
RichInputMethodManager.getInstance().inputMethodManager.showSoftInput(binding.dummyText, 0)
}
val initialColor = if (prefs.contains(prefPrefix + colorPref))
prefs.getInt(prefPrefix + colorPref, Color.GRAY)
else
Settings.readUserColor(prefs, requireContext(), colorPrefs[index], isNight)
b.colorPickerView.setInitialColor(initialColor)
b.colorPickerView.setPadding(15)
// set better color drawable? neither the white circle nor the plus is nice
b.colorPickerView.setSelectorDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_plus))
b.colorPickerView.flagView = BubbleFlag(requireContext()).apply { flagMode = FlagMode.ALWAYS }
b.show()
}
csb.colorTextContainer.setOnClickListener(clickListener)
csb.colorPreview.setOnClickListener(clickListener)
}
}
private fun updateColorPreviews() {
binding.colorSettingsContainer.forEachIndexed { index, view ->
val color = Settings.readUserColor(prefs, requireContext(), colorPrefs[index], isNight)
view.findViewById<ImageView>(R.id.color_preview)?.setColorFilter(color)
}
}
private fun reloadKeyboard(show: Boolean) {
// todo: any way to make some kind of "light update" to keyboard?
// only reloading main keyboard view is necessary...
// or get an actual (live) preview instead of the full keyboard?
// or accelerate keyboard inflate, a big here issue is emojiCategory creating many keyboards
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext())
// todo: this does not work, keyboard also reloading with some other context
// KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(Settings.getDayNightContext(requireContext(), isNight))
if (!show) return
Thread.sleep(100) // some pause is necessary to avoid visual glitches
RichInputMethodManager.getInstance().inputMethodManager.showSoftInput(binding.dummyText, 0)
return
// for some reason showing again does not work when running with executor
// but when running without it's noticeably slow, and sometimes produces glitches
// todo: decider whether to just hide, or have some slowdown and show again
ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute {
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext())
if (!show) return@execute
Thread.sleep(100)
RichInputMethodManager.getInstance().inputMethodManager.showSoftInput(binding.dummyText, 0)
}
}
}
class ColorsNightSettingsFragment : ColorsSettingsFragment() {
override val isNight = true
override val titleResId = R.string.select_user_colors_night
}

View file

@ -0,0 +1,46 @@
package org.dslul.openboard.inputmethod.latin.settings
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
// taken from StreetComplete, ViewBinder.kt
inline fun <reified T : ViewBinding> Fragment.viewBinding(
noinline viewBinder: (View) -> T,
rootViewId: Int? = null
) = FragmentViewBindingPropertyDelegate(this, viewBinder, rootViewId)
class FragmentViewBindingPropertyDelegate<T : ViewBinding>(
private val fragment: Fragment,
private val viewBinder: (View) -> T,
private val rootViewId: Int? = null
) : ReadOnlyProperty<Fragment, T>, LifecycleEventObserver {
private var binding: T? = null
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
binding = null
source.lifecycle.removeObserver(this)
}
}
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
if (binding == null) {
val rootView = if (rootViewId != null) {
thisRef.requireView().findViewById<ViewGroup>(rootViewId)!!.getChildAt(0)
} else {
thisRef.requireView()
}
binding = viewBinder(rootView)
fragment.viewLifecycleOwner.lifecycle.addObserver(this)
}
return binding!!
}
}

View file

@ -22,12 +22,16 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Color;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.Gravity; import android.view.Gravity;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import org.dslul.openboard.inputmethod.keyboard.KeyboardTheme; import org.dslul.openboard.inputmethod.keyboard.KeyboardTheme;
import org.dslul.openboard.inputmethod.latin.AudioAndHapticFeedbackManager; import org.dslul.openboard.inputmethod.latin.AudioAndHapticFeedbackManager;
@ -37,6 +41,7 @@ import org.dslul.openboard.inputmethod.latin.common.Colors;
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils; import org.dslul.openboard.inputmethod.latin.common.LocaleUtils;
import org.dslul.openboard.inputmethod.latin.common.StringUtils; import org.dslul.openboard.inputmethod.latin.common.StringUtils;
import org.dslul.openboard.inputmethod.latin.utils.AdditionalSubtypeUtils; import org.dslul.openboard.inputmethod.latin.utils.AdditionalSubtypeUtils;
import org.dslul.openboard.inputmethod.latin.utils.ColorUtilKt;
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils; import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils;
import org.dslul.openboard.inputmethod.latin.utils.JniUtils; import org.dslul.openboard.inputmethod.latin.utils.JniUtils;
import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils; import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils;
@ -66,16 +71,14 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_THEME_VARIANT_NIGHT = "theme_variant_night"; public static final String PREF_THEME_VARIANT_NIGHT = "theme_variant_night";
public static final String PREF_THEME_KEY_BORDERS = "theme_key_borders"; public static final String PREF_THEME_KEY_BORDERS = "theme_key_borders";
public static final String PREF_THEME_DAY_NIGHT = "theme_auto_day_night"; public static final String PREF_THEME_DAY_NIGHT = "theme_auto_day_night";
public static final String PREF_THEME_USER_COLOR_TEXT = "theme_color_text"; public static final String PREF_THEME_USER_COLOR_PREFIX = "theme_color_";
public static final String PREF_THEME_USER_COLOR_HINT_TEXT = "theme_color_hint_text"; public static final String PREF_THEME_USER_COLOR_NIGHT_PREFIX = "theme_dark_color_";
public static final String PREF_THEME_USER_COLOR_BACKGROUND = "theme_color_background"; public static final String PREF_COLOR_KEYS_SUFFIX = "keys";
public static final String PREF_THEME_USER_COLOR_KEYS = "theme_color_keys"; public static final String PREF_COLOR_ACCENT_SUFFIX = "accent";
public static final String PREF_THEME_USER_COLOR_ACCENT = "theme_color_accent"; public static final String PREF_COLOR_TEXT_SUFFIX = "text";
public static final String PREF_THEME_USER_DARK_COLOR_TEXT = "theme_dark_color_text"; public static final String PREF_COLOR_HINT_TEXT_SUFFIX = "hint_text";
public static final String PREF_THEME_USER_DARK_COLOR_HINT_TEXT = "theme_dark_color_hint_text"; public static final String PREF_COLOR_BACKGROUND_SUFFIX = "background";
public static final String PREF_THEME_USER_DARK_COLOR_BACKGROUND = "theme_dark_color_background"; public static final String PREF_AUTO_USER_COLOR_SUFFIX = "_auto";
public static final String PREF_THEME_USER_DARK_COLOR_KEYS = "theme_dark_color_keys";
public static final String PREF_THEME_USER_DARK_COLOR_ACCENT = "theme_dark_color_accent";
public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key"; public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key";
public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary"; public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary";
public static final String PREF_AUTO_CORRECTION = "pref_key_auto_correction"; public static final String PREF_AUTO_CORRECTION = "pref_key_auto_correction";
@ -532,9 +535,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static Colors getColorsForCurrentTheme(final Context context, final SharedPreferences prefs) { public static Colors getColorsForCurrentTheme(final Context context, final SharedPreferences prefs) {
// todo: night mode can be unspecified -> maybe need to adjust for correct behavior on some devices? final boolean isNight = ResourceUtils.isNight(context.getResources());
final boolean isNight = (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; final String themeColors = (isNight && prefs.getBoolean(PREF_THEME_DAY_NIGHT, context.getResources().getBoolean(R.bool.day_night_default)))
final String themeColors = (isNight && prefs.getBoolean(Settings.PREF_THEME_DAY_NIGHT, context.getResources().getBoolean(R.bool.day_night_default)))
? prefs.getString(Settings.PREF_THEME_VARIANT_NIGHT, KeyboardTheme.THEME_DARKER) ? prefs.getString(Settings.PREF_THEME_VARIANT_NIGHT, KeyboardTheme.THEME_DARKER)
: prefs.getString(Settings.PREF_THEME_VARIANT, KeyboardTheme.THEME_LIGHT); : prefs.getString(Settings.PREF_THEME_VARIANT, KeyboardTheme.THEME_LIGHT);
final String themeStyle = prefs.getString(Settings.PREF_THEME_STYLE, KeyboardTheme.THEME_STYLE_MATERIAL); final String themeStyle = prefs.getString(Settings.PREF_THEME_STYLE, KeyboardTheme.THEME_STYLE_MATERIAL);
@ -542,4 +544,58 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return KeyboardTheme.getThemeColors(themeColors, themeStyle, context, prefs); return KeyboardTheme.getThemeColors(themeColors, themeStyle, context, prefs);
} }
public static int readUserColor(final SharedPreferences prefs, final Context context, final String colorName, final boolean isNight) {
final String pref = getColorPref(colorName, isNight);
if (prefs.getBoolean(pref + PREF_AUTO_USER_COLOR_SUFFIX, true)) {
return determineAutoColor(prefs, context, colorName, isNight);
}
if (prefs.contains(pref))
return prefs.getInt(pref, Color.GRAY);
else return determineAutoColor(prefs, context, colorName, isNight);
}
public static String getColorPref(final String color, final boolean isNight) {
return (isNight ? PREF_THEME_USER_COLOR_NIGHT_PREFIX : PREF_THEME_USER_COLOR_PREFIX) + color;
}
private static int determineAutoColor(final SharedPreferences prefs, final Context context, final String color, final boolean isNight) {
switch (color) {
case PREF_COLOR_ACCENT_SUFFIX:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
// try determining accent color on Android 10 & 11, accent is not available in resources
// todo: test whether this actually works
final Context wrapper = new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault);
final TypedValue value = new TypedValue();
if (wrapper.getTheme().resolveAttribute(android.R.attr.colorAccent, value, true))
return value.data;
}
return ContextCompat.getColor(getDayNightContext(context, isNight), R.color.accent);
case PREF_COLOR_TEXT_SUFFIX:
// base it on background color, and not key, because it's also used for suggestions
if (ColorUtilKt.isBrightColor(readUserColor(prefs, context, PREF_COLOR_BACKGROUND_SUFFIX, isNight))) return Color.BLACK;
else return Color.WHITE;
case PREF_COLOR_HINT_TEXT_SUFFIX:
if (ColorUtilKt.isBrightColor(readUserColor(prefs, context, PREF_COLOR_KEYS_SUFFIX, isNight))) return Color.DKGRAY;
else return Color.LTGRAY;
case PREF_COLOR_KEYS_SUFFIX:
return ColorUtilKt.brightenOrDarken(readUserColor(prefs, context, PREF_COLOR_BACKGROUND_SUFFIX, isNight), isNight);
case PREF_COLOR_BACKGROUND_SUFFIX:
default:
return ContextCompat.getColor(getDayNightContext(context, isNight), R.color.keyboard_background);
}
}
public static Context getDayNightContext(final Context context, final boolean wantNight) {
final boolean isNight = (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
if (isNight == wantNight)
return context;
final Configuration config = new Configuration(context.getResources().getConfiguration());
final int night = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
final int uiModeWithNightBitsZero = config.uiMode - night;
config.uiMode = uiModeWithNightBitsZero + (wantNight ? Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO);
final ContextThemeWrapper wrapper = new ContextThemeWrapper(context, R.style.platformActivityTheme);
wrapper.applyOverrideConfiguration(config);
return wrapper;
}
} }

View file

@ -16,6 +16,7 @@
package org.dslul.openboard.inputmethod.latin.utils; package org.dslul.openboard.inputmethod.latin.utils;
import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.os.Build; import android.os.Build;
@ -298,4 +299,9 @@ public final class ResourceUtils {
public static boolean isStringValue(final TypedValue v) { public static boolean isStringValue(final TypedValue v) {
return v.type == TypedValue.TYPE_STRING; return v.type == TypedValue.TYPE_STRING;
} }
public static boolean isNight(final Resources res) {
// todo: night mode can be unspecified -> maybe need to adjust for correct behavior on some devices?
return (res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
}
} }

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_margin="10dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/color_preview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:layout_gravity="center_vertical"
android:src="@drawable/btn_keyboard_key_action_normal_lxx_base" />
<LinearLayout
android:id="@+id/color_text_container"
android:orientation="vertical"
android:layout_gravity="center_vertical"
android:layout_width="0dp"
android:layout_weight="0.8"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/color_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
style="@style/PreferenceTitleText" />
<TextView
android:id="@+id/color_summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
style="@style/PreferenceSubtitleText" />
</LinearLayout>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/color_switch"
android:layout_gravity="center_vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2" />
</LinearLayout>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:paddingTop="6dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/color_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<EditText
android:id="@+id/dummy_text"
android:hint="@string/hint_show_keyboard"
android:layout_marginHorizontal="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</ScrollView>

View file

@ -184,7 +184,6 @@
<string name="theme_name_user">Карыстацкая</string> <string name="theme_name_user">Карыстацкая</string>
<string name="select_user_colors">Настройка колераў тэмы</string> <string name="select_user_colors">Настройка колераў тэмы</string>
<string name="select_user_colors_summary">Абярыце колеру для тэксту і фону</string> <string name="select_user_colors_summary">Абярыце колеру для тэксту і фону</string>
<string name="select_color_to_adjust">Выберыце колеру для наладкі</string>
<string name="select_color_background">Фон клавіятуры</string> <string name="select_color_background">Фон клавіятуры</string>
<string name="select_color_key">Тэкст клавіш</string> <string name="select_color_key">Тэкст клавіш</string>
<string name="select_color_key_hint">Тэкст спецзнакаў клавіш</string> <string name="select_color_key_hint">Тэкст спецзнакаў клавіш</string>

View file

@ -230,7 +230,6 @@
<string name="theme_name_user">ব্যবহারকারী নির্ধারিত</string> <string name="theme_name_user">ব্যবহারকারী নির্ধারিত</string>
<string name="select_user_colors">থিমের রং সমন্বয়</string> <string name="select_user_colors">থিমের রং সমন্বয়</string>
<string name="select_user_colors_summary">লেখা এবং পটভূমির জন্য রং নির্বাচন</string> <string name="select_user_colors_summary">লেখা এবং পটভূমির জন্য রং নির্বাচন</string>
<string name="select_color_to_adjust">সমন্বয়ের জন্য রং নির্বাচন</string>
<string name="select_color_background">কিবোর্ডের পটভূমি</string> <string name="select_color_background">কিবোর্ডের পটভূমি</string>
<string name="select_color_key">বোতামের লেখা</string> <string name="select_color_key">বোতামের লেখা</string>
<string name="select_color_key_hint">বোতামের পরামর্শের লেখা</string> <string name="select_color_key_hint">বোতামের পরামর্শের লেখা</string>

View file

@ -217,7 +217,6 @@
<string name="select_user_colors">Personnaliser le thème</string> <string name="select_user_colors">Personnaliser le thème</string>
<string name="select_user_colors_night">Personnaliser le thème (mode nuit)</string> <string name="select_user_colors_night">Personnaliser le thème (mode nuit)</string>
<string name="select_user_colors_summary">Sélection des couleurs du texte et de l\'arrière-plan</string> <string name="select_user_colors_summary">Sélection des couleurs du texte et de l\'arrière-plan</string>
<string name="select_color_to_adjust">Sélection des couleurs à modifier</string>
<string name="select_color_background">Arrière-plan du clavier</string> <string name="select_color_background">Arrière-plan du clavier</string>
<string name="select_color_key">Texte des touches</string> <string name="select_color_key">Texte des touches</string>
<string name="select_color_key_hint">Indice des touches</string> <string name="select_color_key_hint">Indice des touches</string>

View file

@ -7,4 +7,5 @@
<color name="almost_background">#666</color> <color name="almost_background">#666</color>
<color name="accent">@color/highlight_color_lxx_dark</color> <color name="accent">@color/highlight_color_lxx_dark</color>
<color name="keyboard_background">@color/keyboard_background_dark</color>
</resources> </resources>

View file

@ -208,7 +208,6 @@
<string name="theme_name_user">Пользовательская</string> <string name="theme_name_user">Пользовательская</string>
<string name="select_user_colors">Настройка цветов темы</string> <string name="select_user_colors">Настройка цветов темы</string>
<string name="select_user_colors_summary">Выберите цвета для текста и фона</string> <string name="select_user_colors_summary">Выберите цвета для текста и фона</string>
<string name="select_color_to_adjust">Выберите цвета для настройки</string>
<string name="select_color_background">Фон клавиатуры</string> <string name="select_color_background">Фон клавиатуры</string>
<string name="select_color_key">Текст клавиш</string> <string name="select_color_key">Текст клавиш</string>
<string name="select_color_key_hint">Текст спецсимволов клавиш</string> <string name="select_color_key_hint">Текст спецсимволов клавиш</string>

View file

@ -185,7 +185,6 @@
<string name="theme_name_user">Користувальницька</string> <string name="theme_name_user">Користувальницька</string>
<string name="select_user_colors">Налаштування кольорів теми</string> <string name="select_user_colors">Налаштування кольорів теми</string>
<string name="select_user_colors_summary">Виберіть кольори для тексту та фону</string> <string name="select_user_colors_summary">Виберіть кольори для тексту та фону</string>
<string name="select_color_to_adjust">Виберіть кольори для налаштування</string>
<string name="select_color_background">Фон клавіатури</string> <string name="select_color_background">Фон клавіатури</string>
<string name="select_color_key">Текст клавіш</string> <string name="select_color_key">Текст клавіш</string>
<string name="select_color_key_hint">Текст спецсимволів клавіш</string> <string name="select_color_key_hint">Текст спецсимволів клавіш</string>

View file

@ -34,4 +34,8 @@
<color name="action_key_background_lxx_dark">@color/highlight_color_lxx_dark</color> <color name="action_key_background_lxx_dark">@color/highlight_color_lxx_dark</color>
<color name="action_key_background_pressed_lxx_dark">@android:color/system_accent1_200</color> <color name="action_key_background_pressed_lxx_dark">@android:color/system_accent1_200</color>
<color name="icon_tint_system_accent_lxx_dark">@color/highlight_color_lxx_dark</color> <color name="icon_tint_system_accent_lxx_dark">@color/highlight_color_lxx_dark</color>
<!-- colors used when user sets keyboard background color to be determined automatically -->
<color name="keyboard_background_light">@android:color/system_neutral1_100</color>
<color name="keyboard_background_dark">@android:color/system_neutral1_800</color>
</resources> </resources>

View file

@ -111,5 +111,9 @@
<color name="foreground_weak">#555</color> <color name="foreground_weak">#555</color>
<color name="almost_background">#AAA</color> <color name="almost_background">#AAA</color>
<!-- accent and keyboard default background colors -->
<color name="accent">@color/highlight_color_lxx_light</color> <color name="accent">@color/highlight_color_lxx_light</color>
<color name="keyboard_background">@color/keyboard_background_light</color>
<color name="keyboard_background_light">@color/keyboard_background_lxx_light_border</color>
<color name="keyboard_background_dark">#263238</color>
</resources> </resources>

View file

@ -584,10 +584,12 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM
<string name="select_user_colors">Adjust theme colors</string> <string name="select_user_colors">Adjust theme colors</string>
<!-- Option for selecting custom theme colors for night mode only --> <!-- Option for selecting custom theme colors for night mode only -->
<string name="select_user_colors_night">Adjust theme colors (night)</string> <string name="select_user_colors_night">Adjust theme colors (night)</string>
<!-- Text when determining color automatically -->
<string name="auto_user_color">Choose color automatically</string>
<!-- Hint for text field just to show keyboard (in color settings) -->
<string name="hint_show_keyboard">Click for preview</string>
<!-- Description for selection of user-defined colors. --> <!-- Description for selection of user-defined colors. -->
<string name="select_user_colors_summary">Select colors for text and background</string> <string name="select_user_colors_summary">Select colors for text and background</string>
<!-- Dialog message when selecting color to adjust. -->
<string name="select_color_to_adjust">Select colors to adjust</string>
<!-- Selection: background color. --> <!-- Selection: background color. -->
<string name="select_color_background">Keyboard background</string> <string name="select_color_background">Keyboard background</string>
<!-- Selection: key text color. --> <!-- Selection: key text color. -->

View file

@ -36,7 +36,8 @@
android:key="theme_key_borders" android:key="theme_key_borders"
android:title="@string/key_borders"/> android:title="@string/key_borders"/>
<Preference <PreferenceScreen
android:fragment="org.dslul.openboard.inputmethod.latin.settings.ColorsSettingsFragment"
android:key="theme_select_colors" android:key="theme_select_colors"
android:title="@string/select_user_colors" android:title="@string/select_user_colors"
android:summary="@string/select_user_colors_summary"/> android:summary="@string/select_user_colors_summary"/>
@ -51,7 +52,8 @@
android:key="theme_variant_night" android:key="theme_variant_night"
android:title="@string/theme_variant_night"/> android:title="@string/theme_variant_night"/>
<Preference <PreferenceScreen
android:fragment="org.dslul.openboard.inputmethod.latin.settings.ColorsNightSettingsFragment"
android:key="theme_select_colors_night" android:key="theme_select_colors_night"
android:title="@string/select_user_colors_night" android:title="@string/select_user_colors_night"
android:summary="@string/select_user_colors_summary"/> android:summary="@string/select_user_colors_summary"/>