diff --git a/app/build.gradle b/app/build.gradle index 99e02e763..3e07e121c 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', 'src/main/proguard.flags' applicationIdSuffix ".debug" } + buildFeatures { + viewBinding true + } archivesBaseName = "openboard_" + defaultConfig.versionName } @@ -60,5 +63,6 @@ dependencies { implementation 'androidx.preference:preference:1.2.1' // includes appcompat implementation 'androidx.recyclerview:recyclerview:1.3.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'com.github.skydoves:colorpickerview:2.2.4' testImplementation 'junit:junit:4.13.2' } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardTheme.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardTheme.java index 8cc0afbcc..484adb74d 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardTheme.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardTheme.java @@ -141,23 +141,22 @@ public final class KeyboardTheme implements Comparable { 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) { final boolean hasBorders = prefs.getBoolean(Settings.PREF_THEME_KEY_BORDERS, false); switch (themeColors) { case THEME_USER: - final int accent = prefs.getInt(Settings.PREF_THEME_USER_COLOR_ACCENT, Color.BLUE); - final int keyBgColor = prefs.getInt(Settings.PREF_THEME_USER_COLOR_KEYS, Color.LTGRAY); - final int keyTextColor = prefs.getInt(Settings.PREF_THEME_USER_COLOR_TEXT, Color.WHITE); - final int hintTextColor = prefs.getInt(Settings.PREF_THEME_USER_COLOR_HINT_TEXT, Color.WHITE); - final int background = prefs.getInt(Settings.PREF_THEME_USER_COLOR_BACKGROUND, Color.DKGRAY); + final int accent = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_ACCENT_SUFFIX, false); + final int keyBgColor = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_KEYS_SUFFIX, false); + final int keyTextColor = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_TEXT_SUFFIX, false); + final int hintTextColor = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_HINT_TEXT_SUFFIX, false); + 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); case THEME_USER_NIGHT: - final int accent2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_ACCENT, Color.BLUE); - final int keyBgColor2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_KEYS, Color.LTGRAY); - final int keyTextColor2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_TEXT, Color.WHITE); - final int hintTextColor2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_HINT_TEXT, Color.WHITE); - final int background2 = prefs.getInt(Settings.PREF_THEME_USER_DARK_COLOR_BACKGROUND, Color.DKGRAY); + final int accent2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_ACCENT_SUFFIX, true); + final int keyBgColor2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_KEYS_SUFFIX, true); + final int keyTextColor2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_TEXT_SUFFIX, true); + final int hintTextColor2 = Settings.readUserColor(prefs, context, Settings.PREF_COLOR_HINT_TEXT_SUFFIX, true); + 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); case THEME_DARK: return new Colors( diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AppearanceSettingsFragment.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AppearanceSettingsFragment.kt index 0e75e88af..98f70d0c1 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AppearanceSettingsFragment.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AppearanceSettingsFragment.kt @@ -2,10 +2,8 @@ package org.dslul.openboard.inputmethod.latin.settings import android.content.SharedPreferences import android.content.res.Configuration -import android.graphics.Color import android.os.Build import android.os.Bundle -import androidx.appcompat.app.AlertDialog import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.TwoStatePreference @@ -137,65 +135,7 @@ class AppearanceSettingsFragment : SubScreenFragment() { } themeVariantNightPref?.isVisible = dayNightPref?.isChecked == true 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?.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) { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/ColorPickerDialog.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/ColorPickerDialog.java deleted file mode 100644 index d1e6fa6ff..000000000 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/ColorPickerDialog.java +++ /dev/null @@ -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(); - } - -} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/ColorsSettingsFragment.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/ColorsSettingsFragment.kt new file mode 100644 index 000000000..8f4c549e6 --- /dev/null +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/ColorsSettingsFragment.kt @@ -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(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 +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/FragmentBindingUtils.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/FragmentBindingUtils.kt new file mode 100644 index 000000000..e8ca2066e --- /dev/null +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/FragmentBindingUtils.kt @@ -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 Fragment.viewBinding( + noinline viewBinder: (View) -> T, + rootViewId: Int? = null +) = FragmentViewBindingPropertyDelegate(this, viewBinder, rootViewId) + +class FragmentViewBindingPropertyDelegate( + private val fragment: Fragment, + private val viewBinder: (View) -> T, + private val rootViewId: Int? = null +) : ReadOnlyProperty, 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(rootViewId)!!.getChildAt(0) + } else { + thisRef.requireView() + } + binding = viewBinder(rootView) + fragment.viewLifecycleOwner.lifecycle.addObserver(this) + } + return binding!! + } +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java index beeb508db..274405187 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java @@ -22,12 +22,16 @@ import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Color; import android.os.Build; import android.util.Log; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; import android.view.Gravity; import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import org.dslul.openboard.inputmethod.keyboard.KeyboardTheme; 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.StringUtils; 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.JniUtils; 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_KEY_BORDERS = "theme_key_borders"; 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_HINT_TEXT = "theme_color_hint_text"; - public static final String PREF_THEME_USER_COLOR_BACKGROUND = "theme_color_background"; - public static final String PREF_THEME_USER_COLOR_KEYS = "theme_color_keys"; - public static final String PREF_THEME_USER_COLOR_ACCENT = "theme_color_accent"; - public static final String PREF_THEME_USER_DARK_COLOR_TEXT = "theme_dark_color_text"; - public static final String PREF_THEME_USER_DARK_COLOR_HINT_TEXT = "theme_dark_color_hint_text"; - public static final String PREF_THEME_USER_DARK_COLOR_BACKGROUND = "theme_dark_color_background"; - 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_THEME_USER_COLOR_PREFIX = "theme_color_"; + public static final String PREF_THEME_USER_COLOR_NIGHT_PREFIX = "theme_dark_color_"; + public static final String PREF_COLOR_KEYS_SUFFIX = "keys"; + public static final String PREF_COLOR_ACCENT_SUFFIX = "accent"; + public static final String PREF_COLOR_TEXT_SUFFIX = "text"; + public static final String PREF_COLOR_HINT_TEXT_SUFFIX = "hint_text"; + public static final String PREF_COLOR_BACKGROUND_SUFFIX = "background"; + public static final String PREF_AUTO_USER_COLOR_SUFFIX = "_auto"; 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_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) { - // todo: night mode can be unspecified -> maybe need to adjust for correct behavior on some devices? - final boolean isNight = (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; - final String themeColors = (isNight && prefs.getBoolean(Settings.PREF_THEME_DAY_NIGHT, context.getResources().getBoolean(R.bool.day_night_default))) + final boolean isNight = ResourceUtils.isNight(context.getResources()); + final String themeColors = (isNight && prefs.getBoolean(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, KeyboardTheme.THEME_LIGHT); 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); } + 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; + } + } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ResourceUtils.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ResourceUtils.java index c15c49fc7..780458693 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ResourceUtils.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ResourceUtils.java @@ -16,6 +16,7 @@ package org.dslul.openboard.inputmethod.latin.utils; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Build; @@ -298,4 +299,9 @@ public final class ResourceUtils { public static boolean isStringValue(final TypedValue v) { 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; + } } diff --git a/app/src/main/res/layout/color_setting.xml b/app/src/main/res/layout/color_setting.xml new file mode 100644 index 000000000..d56c5c0c9 --- /dev/null +++ b/app/src/main/res/layout/color_setting.xml @@ -0,0 +1,40 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/color_settings.xml b/app/src/main/res/layout/color_settings.xml new file mode 100644 index 000000000..445061837 --- /dev/null +++ b/app/src/main/res/layout/color_settings.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 919a4e204..6025e1a47 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -184,7 +184,6 @@ Карыстацкая Настройка колераў тэмы Абярыце колеру для тэксту і фону - Выберыце колеру для наладкі Фон клавіятуры Тэкст клавіш Тэкст спецзнакаў клавіш diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index b3ab91ff6..9647a1d17 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -230,7 +230,6 @@ ব্যবহারকারী নির্ধারিত থিমের রং সমন্বয় লেখা এবং পটভূমির জন্য রং নির্বাচন - সমন্বয়ের জন্য রং নির্বাচন কিবোর্ডের পটভূমি বোতামের লেখা বোতামের পরামর্শের লেখা diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index fffe22480..6d47edea8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -217,7 +217,6 @@ Personnaliser le thème Personnaliser le thème (mode nuit) Sélection des couleurs du texte et de l\'arrière-plan - Sélection des couleurs à modifier Arrière-plan du clavier Texte des touches Indice des touches diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 131defe4b..af40e94bf 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -7,4 +7,5 @@ #666 @color/highlight_color_lxx_dark + @color/keyboard_background_dark diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 05846a077..957f0faa6 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -208,7 +208,6 @@ Пользовательская Настройка цветов темы Выберите цвета для текста и фона - Выберите цвета для настройки Фон клавиатуры Текст клавиш Текст спецсимволов клавиш diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index d9be2be7a..6cb294160 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -185,7 +185,6 @@ Користувальницька Налаштування кольорів теми Виберіть кольори для тексту та фону - Виберіть кольори для налаштування Фон клавіатури Текст клавіш Текст спецсимволів клавіш diff --git a/app/src/main/res/values-v31/colors.xml b/app/src/main/res/values-v31/colors.xml index 78517e63e..0e4205e8a 100644 --- a/app/src/main/res/values-v31/colors.xml +++ b/app/src/main/res/values-v31/colors.xml @@ -34,4 +34,8 @@ @color/highlight_color_lxx_dark @android:color/system_accent1_200 @color/highlight_color_lxx_dark + + + @android:color/system_neutral1_100 + @android:color/system_neutral1_800 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 7a41ff673..95204fe04 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -111,5 +111,9 @@ #555 #AAA + @color/highlight_color_lxx_light + @color/keyboard_background_light + @color/keyboard_background_lxx_light_border + #263238 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2bfd8d5fc..bffe77408 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -584,10 +584,12 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM Adjust theme colors Adjust theme colors (night) + + Choose color automatically + + Click for preview Select colors for text and background - - Select colors to adjust Keyboard background diff --git a/app/src/main/res/xml/prefs_screen_appearance.xml b/app/src/main/res/xml/prefs_screen_appearance.xml index b0a270fbe..960423f31 100644 --- a/app/src/main/res/xml/prefs_screen_appearance.xml +++ b/app/src/main/res/xml/prefs_screen_appearance.xml @@ -36,7 +36,8 @@ android:key="theme_key_borders" android:title="@string/key_borders"/> - @@ -51,7 +52,8 @@ android:key="theme_variant_night" android:title="@string/theme_variant_night"/> -