diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java index 3bf890c10..bf60b8d10 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java @@ -226,6 +226,11 @@ private void setKeyboard(final KeyboardElement keyboardElement, @NonNull final K && (currentSettingsValues.mInlineEmojiSearch || currentSettingsValues.mSuggestEmojis)) { EmojiParserKt.loadEmojiDefaultVersionsAndPopupSpecs(mThemeContext); } + + // the mode changed (e.g. numpad/dpad), so refresh the toolbar toggle highlights + if (mSuggestionStripView != null) { + mSuggestionStripView.updateToolbarButtonStates(); + } } @Nullable public Keyboard getKeyboard() { diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt index eddc578c8..0ca961d70 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt @@ -96,7 +96,6 @@ class KeyboardIconsSet private constructor() { "start_onehanded_mode_key" to ToolbarKey.ONE_HANDED.name.lowercase(Locale.US), ) - // todo: incognito and force incognito should not be the same? or not the same as toolbar key? private val keyboardIconsHolo by lazy { hashMapOf( NAME_SHIFT_KEY to R.drawable.sym_keyboard_shift_holo, NAME_SHIFT_KEY_SHIFTED to R.drawable.sym_keyboard_shifted_holo, @@ -138,7 +137,7 @@ class KeyboardIconsSet private constructor() { ToolbarKey.PASTE -> R.drawable.sym_keyboard_paste ToolbarKey.ONE_HANDED -> R.drawable.sym_keyboard_start_onehanded_holo ToolbarKey.FLOATING -> R.drawable.ic_drag_indicator - ToolbarKey.INCOGNITO -> R.drawable.sym_keyboard_incognito_holo + ToolbarKey.INCOGNITO -> R.drawable.ic_incognito ToolbarKey.AUTOCORRECT -> R.drawable.ic_autocorrect ToolbarKey.CLEAR_CLIPBOARD -> R.drawable.sym_keyboard_clear_clipboard_holo ToolbarKey.CLOSE_HISTORY -> R.drawable.ic_close @@ -202,7 +201,7 @@ class KeyboardIconsSet private constructor() { ToolbarKey.PASTE -> R.drawable.sym_keyboard_paste ToolbarKey.ONE_HANDED -> R.drawable.sym_keyboard_start_onehanded_lxx ToolbarKey.FLOATING -> R.drawable.ic_drag_indicator - ToolbarKey.INCOGNITO -> R.drawable.sym_keyboard_incognito_lxx + ToolbarKey.INCOGNITO -> R.drawable.ic_incognito ToolbarKey.AUTOCORRECT -> R.drawable.ic_autocorrect ToolbarKey.CLEAR_CLIPBOARD -> R.drawable.sym_keyboard_clear_clipboard_lxx ToolbarKey.CLOSE_HISTORY -> R.drawable.ic_close @@ -266,7 +265,7 @@ class KeyboardIconsSet private constructor() { ToolbarKey.PASTE -> R.drawable.sym_keyboard_paste_rounded ToolbarKey.ONE_HANDED -> R.drawable.sym_keyboard_start_onehanded_rounded ToolbarKey.FLOATING -> R.drawable.ic_drag_indicator - ToolbarKey.INCOGNITO -> R.drawable.sym_keyboard_incognito_lxx + ToolbarKey.INCOGNITO -> R.drawable.ic_incognito ToolbarKey.AUTOCORRECT -> R.drawable.ic_autocorrect_rounded ToolbarKey.CLEAR_CLIPBOARD -> R.drawable.sym_keyboard_clear_clipboard_rounded ToolbarKey.CLOSE_HISTORY -> R.drawable.ic_close_rounded diff --git a/app/src/main/java/helium314/keyboard/latin/common/Colors.kt b/app/src/main/java/helium314/keyboard/latin/common/Colors.kt index e14e10b8f..9d0489fcd 100644 --- a/app/src/main/java/helium314/keyboard/latin/common/Colors.kt +++ b/app/src/main/java/helium314/keyboard/latin/common/Colors.kt @@ -147,11 +147,9 @@ class DynamicColors(context: Context, override val themeStyle: String, override private val spaceBarStateList: ColorStateList private val adjustedBackgroundStateList: ColorStateList private val stripBackgroundList: ColorStateList - private val toolbarKeyStateList = activatedStateList( - keyText, - if (isBrightColor(keyText)) darken(darken(keyText)) - else brighten(brighten(keyText)) - ) + // enabled toolbar toggles use the accent (same as the selected emoji category, EMOJI_CATEGORY_SELECTED + // -> doubleAdjustedAccent), everything else the plain key color + private val toolbarKeyStateList = activatedStateList(darken(darken(accent)), keyText) /** darkened variant of [accent] because the accent color is always light for dynamic colors */ private val adjustedAccent: Int = darken(accent) @@ -400,11 +398,9 @@ class DefaultColors ( private val spaceBarStateList: ColorStateList private val adjustedBackgroundStateList: ColorStateList private val stripBackgroundList: ColorStateList - private val toolbarKeyStateList = activatedStateList( - suggestionText, - if (isBrightColor(suggestionText)) darken(darken(suggestionText)) - else brighten(brighten(suggestionText)) - ) + // enabled toolbar toggles use the accent (same as the selected emoji category, + // EMOJI_CATEGORY_SELECTED -> accent), everything else the plain suggestion color + private val toolbarKeyStateList = activatedStateList(accent, suggestionText) private var backgroundSetupDone = false init { diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt index e662b8df0..d403350a6 100644 --- a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt +++ b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt @@ -12,6 +12,7 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.graphics.Color import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable +import android.os.Build import android.text.TextUtils import android.util.AttributeSet import android.util.TypedValue @@ -55,6 +56,7 @@ import helium314.keyboard.latin.utils.getPinnedToolbarKeys import helium314.keyboard.latin.utils.prefs import helium314.keyboard.latin.utils.removeFirst import helium314.keyboard.latin.utils.removePinnedKey +import helium314.keyboard.latin.utils.refreshToolbarButtonActivatedStates import helium314.keyboard.latin.utils.setToolbarButtonsActivatedStateOnPrefChange import java.util.concurrent.atomic.AtomicBoolean import kotlin.math.abs @@ -62,11 +64,6 @@ import kotlin.math.min import androidx.core.view.isGone import helium314.keyboard.latin.utils.onClickToolbarKey import helium314.keyboard.latin.utils.onLongClickToolbarKey -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext @SuppressLint("InflateParams") class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int) : @@ -119,7 +116,6 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int) private val pinnedKeys: ViewGroup = findViewById(R.id.pinned_keys) private val suggestionsStrip: ViewGroup = findViewById(R.id.suggestions_strip) private val toolbarExpandKey = findViewById(R.id.suggestions_strip_toolbar_key) - private val incognitoIcon = KeyboardIconsSet.instance.getNewDrawable(ToolbarKey.INCOGNITO.name, context) private val toolbarArrowIcon = KeyboardIconsSet.instance.getNewDrawable(KeyboardIconsSet.NAME_TOOLBAR_KEY, context) private val defaultToolbarBackground: Drawable = toolbarExpandKey.background private val enabledToolKeyBackground = GradientDrawable() @@ -291,8 +287,12 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int) override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String?) { setToolbarButtonsActivatedStateOnPrefChange(pinnedKeys, key) setToolbarButtonsActivatedStateOnPrefChange(toolbar, key) - if (key == Settings.PREF_ALWAYS_INCOGNITO_MODE) - GlobalScope.launch { delay(10); withContext(Dispatchers.Main) { updateKeys() } } + } + + /** refresh the toolbar toggle highlights, e.g. after the keyboard mode (numpad/dpad) changed */ + fun updateToolbarButtonStates() { + refreshToolbarButtonActivatedStates(toolbar) + refreshToolbarButtonActivatedStates(pinnedKeys) } override fun onVisibilityChanged(view: View, visibility: Int) { @@ -352,7 +352,10 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int) override fun onLongClick(view: View): Boolean { if (view.tag is ToolbarKey) { onLongClickToolbarKey(view) - return true + // when long-press isn't used for pinning keys, don't consume it so the system tooltip + // (set via tooltipText, API 26+) can explain what the key does; on older APIs there is no + // tooltip to consume the press, so keep consuming it to avoid a stray click on release + return Build.VERSION.SDK_INT < Build.VERSION_CODES.O || Settings.getValues().mQuickPinToolbarKeys } AudioAndHapticFeedbackManager.getInstance().performHapticFeedback(this, HapticEvent.KEY_LONG_PRESS) return if (view is TextView && wordViews.contains(view)) { @@ -511,19 +514,20 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int) val settingsValues = Settings.getValues() val toolbarIsExpandable = settingsValues.mToolbarMode == ToolbarMode.EXPANDABLE - if (settingsValues.mIncognitoModeEnabled) { - toolbarExpandKey.setImageDrawable(incognitoIcon) - toolbarExpandKey.isVisible = true - } else { - toolbarExpandKey.setImageDrawable(toolbarArrowIcon) - toolbarExpandKey.isVisible = toolbarIsExpandable - } + updateExpandKeyImage() + // the expand key is only needed when it can toggle the toolbar + toolbarExpandKey.isVisible = toolbarIsExpandable toolbarExpandKey.setOnClickListener(if (!toolbarIsExpandable) null else this) pinnedKeys.visibility = suggestionsStrip.visibility isExternalSuggestionVisible = false } + private fun updateExpandKeyImage() { + toolbarExpandKey.setImageDrawable(toolbarArrowIcon) + Settings.getValues().mColors.setColor(toolbarExpandKey, ColorType.TOOL_BAR_EXPAND_KEY) + } + private fun addKeyToPinnedKeys(pinnedKey: ToolbarKey) { val original = toolbar.findViewWithTag(pinnedKey) ?: return // copy the original key to a new ImageButton @@ -533,6 +537,8 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int) copy.scaleX = original.scaleX copy.scaleY = original.scaleY copy.contentDescription = original.contentDescription + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + copy.tooltipText = original.tooltipText // keep the long-press tooltip; also lets it consume the press copy.setImageDrawable(original.drawable) copy.layoutParams = original.layoutParams copy.isActivated = original.isActivated diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt index 837d233fa..e9d06fb98 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt @@ -3,6 +3,8 @@ package helium314.keyboard.latin.utils import android.content.Context import android.content.SharedPreferences +import android.content.res.Configuration +import android.os.Build import android.view.View import android.view.ViewConfiguration import android.view.ViewGroup @@ -11,6 +13,8 @@ import android.widget.ImageView import androidx.core.content.edit import androidx.core.view.forEach import helium314.keyboard.event.HapticEvent +import helium314.keyboard.keyboard.KeyboardElement +import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.keyboard.internal.KeyboardIconsSet import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.latin.AudioAndHapticFeedbackManager @@ -19,9 +23,6 @@ import helium314.keyboard.latin.common.Constants.Separators import helium314.keyboard.latin.settings.Defaults import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.ToolbarKey.* -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import java.util.EnumMap import java.util.Locale @@ -30,8 +31,11 @@ fun createToolbarKey(context: Context, key: ToolbarKey): ImageButton { button.scaleType = ImageView.ScaleType.CENTER button.tag = key button.contentDescription = key.name.lowercase().getStringResourceOrName("", context) - setToolbarButtonActivatedState(button) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + button.tooltipText = button.contentDescription // long-press shows what the key does + button.setImageDrawable(KeyboardIconsSet.instance.getNewDrawable(key.name, context)) + setToolbarButtonActivatedState(button) return button } @@ -41,23 +45,46 @@ fun setToolbarButtonsActivatedStateOnPrefChange(buttonsGroup: ViewGroup, key: St && key != Settings.PREF_ALWAYS_INCOGNITO_MODE && key != GestureDataGatheringSettings.PREF_BACKGROUND_GATHERING_ENABLED && key != GestureDataGatheringSettings.PREF_BACKGROUND_DISABLED_BEFORE_TIME_MILLIS - && key?.startsWith(Settings.PREF_ONE_HANDED_MODE_PREFIX) == false) + && key?.startsWith(Settings.PREF_ONE_HANDED_MODE_PREFIX) == false + && key?.startsWith(Settings.PREF_ENABLE_SPLIT_KEYBOARD) == false) return - GlobalScope.launch { - delay(10) // need to wait until SettingsValues are reloaded - buttonsGroup.forEach { if (it is ImageButton) setToolbarButtonActivatedState(it) } - } + // states are read straight from prefs (which are already written at this point), so no wait for + // a SettingsValues reload is needed; post only to make sure we touch the views on the UI thread + buttonsGroup.post { buttonsGroup.forEach { if (it is ImageButton) setToolbarButtonActivatedState(it) } } +} + +/** refresh the activated state of all toolbar buttons, e.g. after the keyboard mode changed */ +fun refreshToolbarButtonActivatedStates(buttonsGroup: ViewGroup) { + buttonsGroup.forEach { if (it is ImageButton) setToolbarButtonActivatedState(it) } } private fun setToolbarButtonActivatedState(button: ImageButton) { + // read the current values straight from prefs instead of the cached SettingsValues: that cache is + // only reloaded asynchronously after a pref change, which previously made the toggle color lag or + // show the previous state (and split was never refreshed at all) + val prefs = button.context.prefs() + val landscape = button.context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + val folded = FoldableUtils.isFolded + val split = Settings.readSplitKeyboardEnabled(prefs, landscape, folded) button.isActivated = when (button.tag) { - INCOGNITO -> button.context.prefs().getBoolean(Settings.PREF_ALWAYS_INCOGNITO_MODE, Defaults.PREF_ALWAYS_INCOGNITO_MODE) - ONE_HANDED -> Settings.getValues().mOneHandedModeEnabled - SPLIT -> Settings.getValues().mIsSplitKeyboardEnabled - AUTOCORRECT -> Settings.getValues().mAutoCorrectionEnabledPerUserSettings + // incognito is highlighted like any other toggle (accent when force-incognito is on); it keeps a + // single plain glasses icon and is only re-tinted, not swapped/crossed out, to match the toolbar + INCOGNITO -> prefs.getBoolean(Settings.PREF_ALWAYS_INCOGNITO_MODE, Defaults.PREF_ALWAYS_INCOGNITO_MODE) + // floating keyboard force-disables one-handed mode (see SettingsValues.mOneHandedModeEnabled), + // so mirror that guard here, otherwise the toggle would light up while floating + ONE_HANDED -> !Settings.getValues().mIsFloatingKeyboard && Settings.readOneHandedModeEnabled(prefs, landscape, split, folded) + SPLIT -> split + AUTOCORRECT -> prefs.getBoolean(Settings.PREF_AUTO_CORRECTION, Defaults.PREF_AUTO_CORRECTION) + // mode keys: highlighted while their panel/mode is the one currently active (not a fleeting action) + NUMPAD -> KeyboardSwitcher.getInstance().isShowingKeyboardId(KeyboardElement.NUMPAD) + DPAD -> KeyboardSwitcher.getInstance().isShowingKeyboardId(KeyboardElement.DPAD) + FLOATING -> Settings.getValues().mIsFloatingKeyboard BACKGROUND_GATHERING -> useBackgroundGathering - else -> true + // non-toggle keys have no on/off state; keep them neutral so only enabled toggles get + // the activated highlight (previously they were all "activated", which highlighted the + // idle toggles instead of the active ones) + else -> false } } diff --git a/app/src/main/res/drawable/ic_incognito.xml b/app/src/main/res/drawable/ic_incognito.xml new file mode 100644 index 000000000..0f62aa404 --- /dev/null +++ b/app/src/main/res/drawable/ic_incognito.xml @@ -0,0 +1,13 @@ + + + +