From 0b55a92e02cb56d68d0c8d6569da42e4a10c2eb8 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Mon, 18 Dec 2023 17:30:53 +0100 Subject: [PATCH] Improve main views (#321) * views now have the same height * add one-handed mode width setting * key text and emojis scale more adequately with keyboard height and one-handed mode width * slightly adjust landscape config to match better with portrait --- .../openboard/inputmethod/keyboard/Key.java | 6 +- .../inputmethod/keyboard/KeyboardId.java | 4 ++ .../inputmethod/keyboard/KeyboardView.java | 24 ++++++-- .../clipboard/ClipboardHistoryView.kt | 2 +- .../clipboard/ClipboardLayoutParams.kt | 2 +- .../keyboard/emoji/EmojiLayoutParams.java | 17 +++--- .../keyboard/emoji/EmojiPalettesView.java | 2 +- .../keyboard/internal/KeyboardBuilder.kt | 14 +++-- .../internal/keyboard_parser/EmojiParser.kt | 23 ++++++- .../keyboard_parser/KeyboardParser.kt | 12 ++-- .../inputmethod/latin/KeyboardWrapperView.kt | 61 +++++++++++++++---- .../settings/PreferencesSettingsFragment.java | 2 +- .../inputmethod/latin/settings/Settings.java | 25 +++++--- .../latin/settings/SettingsValues.java | 12 +++- .../latin/utils/ResourceUtils.java | 9 ++- .../main/res/drawable/ic_arrow_horizontal.xml | 15 +++++ .../main/res/layout/main_keyboard_frame.xml | 8 +++ app/src/main/res/values-land/config.xml | 10 +-- app/src/main/res/values/attrs.xml | 1 + .../main/res/values/keyboard-icons-holo.xml | 1 + .../res/values/keyboard-icons-lxx-light.xml | 1 + .../res/values/keyboard-icons-rounded.xml | 1 + 22 files changed, 184 insertions(+), 68 deletions(-) create mode 100644 app/src/main/res/drawable/ic_arrow_horizontal.xml diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java index 78127d08..8f1adbb4 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java @@ -316,9 +316,7 @@ public class Key implements Comparable { mHorizontalGap = Math.round(horizontalGapFloat); mVerticalGap = Math.round(keyParams.mKeyboardParams.mVerticalGap); mWidth = Math.round(keyParams.mFullWidth - horizontalGapFloat); - // todo (later): height better should be rounded, but this may end up shifting all keys up by one pixel, - // increasing the keyboard height by one pixel, but not for emoji keyboard -> the 1 pixel shift feels very wrong - // how to do it properly? check again when keyboard height is same for all views! + // height is always rounded down, because rounding up may make the keyboard too high to fit, leading to issues mHeight = (int) (keyParams.mFullHeight - keyParams.mKeyboardParams.mVerticalGap); if (!isSpacer() && (mWidth == 0 || mHeight == 0)) { throw new IllegalStateException("key needs positive width and height"); @@ -326,7 +324,7 @@ public class Key implements Comparable { // Horizontal gap is divided equally to both sides of the key. mX = Math.round(keyParams.xPos + horizontalGapFloat / 2); mY = Math.round(keyParams.yPos); - mHitBox.set(Math.round(keyParams.xPos), (int) keyParams.yPos, Math.round(keyParams.xPos + keyParams.mFullWidth) + 1, + mHitBox.set(Math.round(keyParams.xPos), Math.round(keyParams.yPos), Math.round(keyParams.xPos + keyParams.mFullWidth) + 1, Math.round(keyParams.yPos + keyParams.mFullHeight)); mHashCode = computeHashCode(this); } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardId.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardId.java index 2f2ef5aa..0a5fdc20 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardId.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardId.java @@ -183,6 +183,10 @@ public final class KeyboardId { || mElementId == ELEMENT_PHONE || mElementId == ELEMENT_PHONE_SYMBOLS; } + public boolean isEmojiKeyboard() { + return mElementId >= ELEMENT_EMOJI_RECENTS && mElementId <= ELEMENT_EMOJI_CATEGORY16; + } + public int imeAction() { return InputTypeUtils.getImeOptionsActionIdFromEditorInfo(mEditorInfo); } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardView.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardView.java index f91c1df7..21e41ecd 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardView.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardView.java @@ -36,6 +36,7 @@ import org.dslul.openboard.inputmethod.latin.common.Colors; import org.dslul.openboard.inputmethod.latin.common.Constants; import org.dslul.openboard.inputmethod.latin.common.StringUtils; import org.dslul.openboard.inputmethod.latin.settings.Settings; +import org.dslul.openboard.inputmethod.latin.settings.SettingsValues; import org.dslul.openboard.inputmethod.latin.suggestions.MoreSuggestionsView; import org.dslul.openboard.inputmethod.latin.utils.TypefaceUtils; @@ -95,6 +96,7 @@ public class KeyboardView extends View { private final Rect mKeyBackgroundPadding = new Rect(); private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; private final Colors mColors; + private float mKeyScaleForText; // The maximum key label width in the proportion to the key width. private static final float MAX_LABEL_RATIO = 0.90f; @@ -203,9 +205,15 @@ public class KeyboardView extends View { */ public void setKeyboard(@NonNull final Keyboard keyboard) { mKeyboard = keyboard; - final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; - mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes); - mKeyDrawParams.updateParams(keyHeight, keyboard.mKeyVisualAttributes); + final SettingsValues sv = Settings.getInstance().getCurrent(); + // scale should not depend on mOneHandedModeScale for emoji and clipboard, because those views are not affected by one-handed mode (yet) + if (keyboard.mId.isEmojiKeyboard() || keyboard.mId.mElementId == KeyboardId.ELEMENT_CLIPBOARD) + mKeyScaleForText = (float) Math.sqrt(1 / sv.mKeyboardHeightScale); + else + mKeyScaleForText = (float) Math.sqrt(sv.mOneHandedModeScale / sv.mKeyboardHeightScale); + final int scaledKeyHeight = (int) ((keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap) * mKeyScaleForText); + mKeyDrawParams.updateParams(scaledKeyHeight, mKeyVisualAttributes); + mKeyDrawParams.updateParams(scaledKeyHeight, keyboard.mKeyVisualAttributes); invalidateAllKeys(); requestLayout(); } @@ -346,7 +354,7 @@ public class KeyboardView extends View { canvas.translate(keyDrawX, keyDrawY); final KeyVisualAttributes attr = key.getVisualAttributes(); - final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams(key.getHeight(), attr); + final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams((int) (key.getHeight() * mKeyScaleForText), attr); params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE; if (!key.isSpacer()) { @@ -413,7 +421,8 @@ public class KeyboardView extends View { if (key.isAlignLabelOffCenter() && mShowsHints) { // The label is placed off center of the key. Currently used only on "phone number" layout // to have letter hints shown nicely. We don't want to align it off center if hints are off. - labelX = centerX + params.mLabelOffCenterRatio * labelCharWidth; + // use a non-negative number to avoid label starting left of the letter for high keyboard scale on holo phone layout + labelX = Math.max(0f, centerX + params.mLabelOffCenterRatio * labelCharWidth); paint.setTextAlign(Align.LEFT); } else { labelX = centerX; @@ -474,6 +483,11 @@ public class KeyboardView extends View { hintBaseline = centerY + labelCharHeight / 2.0f; } paint.setTextAlign(Align.LEFT); + // shrink hint label before it's off the key + // looks bad, but still better than the alternative + final float ratio = Math.min(1.0f, (keyWidth - hintX) * 0.95f / TypefaceUtils.getStringWidth(hintLabel, paint)); + final float autoSize = paint.getTextSize() * ratio; + paint.setTextSize(autoSize); } else if (key.hasShiftedLetterHint()) { // The hint label is placed at top-right corner of the key. Used mainly on tablet. hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f; diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardHistoryView.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardHistoryView.kt index 9d27c1b0..7e4e4e62 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardHistoryView.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardHistoryView.kt @@ -61,7 +61,7 @@ class ClipboardHistoryView @JvmOverloads constructor( val res = context.resources // The main keyboard expands to the entire this {@link KeyboardView}. val width = (ResourceUtils.getDefaultKeyboardWidth(res) + paddingLeft + paddingRight) - val height = (ResourceUtils.getDefaultKeyboardHeight(res) + val height = (ResourceUtils.getKeyboardHeight(res, Settings.getInstance().current) + res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height) + paddingTop + paddingBottom) setMeasuredDimension(width, height) diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardLayoutParams.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardLayoutParams.kt index cf4e9fd6..c87ba48b 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardLayoutParams.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/clipboard/ClipboardLayoutParams.kt @@ -25,7 +25,7 @@ class ClipboardLayoutParams(res: Resources) { } init { - val defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res) + val defaultKeyboardHeight = ResourceUtils.getKeyboardHeight(res, Settings.getInstance().current) val suggestionStripHeight = res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height) val defaultKeyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res) diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/emoji/EmojiLayoutParams.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/emoji/EmojiLayoutParams.java index d7b9d6e7..632ce6a8 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/emoji/EmojiLayoutParams.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/emoji/EmojiLayoutParams.java @@ -13,6 +13,7 @@ import android.widget.LinearLayout; import androidx.recyclerview.widget.RecyclerView; import org.dslul.openboard.inputmethod.latin.R; import org.dslul.openboard.inputmethod.latin.settings.Settings; +import org.dslul.openboard.inputmethod.latin.settings.SettingsValues; import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils; final class EmojiLayoutParams { @@ -29,9 +30,10 @@ final class EmojiLayoutParams { private final int mTopPadding; public EmojiLayoutParams(final Resources res) { - final int defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); + final SettingsValues settingsValues = Settings.getInstance().getCurrent(); + final int defaultKeyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues); final int defaultKeyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); - if (Settings.getInstance().getCurrent().mNarrowKeyGaps) { + if (settingsValues.mNarrowKeyGaps) { mKeyVerticalGap = (int) res.getFraction(R.fraction.config_key_vertical_gap_holo_narrow, defaultKeyboardHeight, defaultKeyboardHeight); mKeyHorizontalGap = (int) (res.getFraction(R.fraction.config_key_horizontal_gap_holo_narrow, @@ -42,13 +44,14 @@ final class EmojiLayoutParams { mKeyHorizontalGap = (int) (res.getFraction(R.fraction.config_key_horizontal_gap_holo, defaultKeyboardWidth, defaultKeyboardWidth)); } - mBottomPadding = (int) (res.getFraction(R.fraction.config_keyboard_bottom_padding_holo, - defaultKeyboardHeight, defaultKeyboardHeight) * Settings.getInstance().getCurrent().mBottomPaddingScale); - mTopPadding = (int) res.getFraction(R.fraction.config_keyboard_top_padding_holo, - defaultKeyboardHeight, defaultKeyboardHeight); + final float defaultBottomPadding = res.getFraction(R.fraction.config_keyboard_bottom_padding_holo, defaultKeyboardHeight, defaultKeyboardHeight); + mBottomPadding = (int) (defaultBottomPadding * settingsValues.mBottomPaddingScale); + final int paddingScaleOffset = (int) (mBottomPadding - defaultBottomPadding); + mTopPadding = (int) res.getFraction(R.fraction.config_keyboard_top_padding_holo, defaultKeyboardHeight, defaultKeyboardHeight); mEmojiCategoryPageIdViewHeight = (int) (res.getDimension(R.dimen.config_emoji_category_page_id_height)); final int baseheight = defaultKeyboardHeight - mBottomPadding - mTopPadding + mKeyVerticalGap; - mEmojiActionBarHeight = baseheight / DEFAULT_KEYBOARD_ROWS - (mKeyVerticalGap - mBottomPadding) / 2; + final int rows = DEFAULT_KEYBOARD_ROWS + (settingsValues.mShowsNumberRow ? 1 : 0); // for proper size considering number row + mEmojiActionBarHeight = baseheight / rows - (mKeyVerticalGap - mBottomPadding) / 2 + paddingScaleOffset / 2; mEmojiListHeight = defaultKeyboardHeight - mEmojiActionBarHeight - mEmojiCategoryPageIdViewHeight; mEmojiListBottomMargin = 0; mEmojiKeyboardHeight = mEmojiListHeight - mEmojiListBottomMargin - 1; diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/emoji/EmojiPalettesView.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/emoji/EmojiPalettesView.java index 7efbe054..3b1bcf30 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/emoji/EmojiPalettesView.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/emoji/EmojiPalettesView.java @@ -135,7 +135,7 @@ public final class EmojiPalettesView extends LinearLayout // The main keyboard expands to the entire this {@link KeyboardView}. final int width = ResourceUtils.getDefaultKeyboardWidth(res) + getPaddingLeft() + getPaddingRight(); - final int height = ResourceUtils.getDefaultKeyboardHeight(res) + final int height = ResourceUtils.getKeyboardHeight(res, Settings.getInstance().getCurrent()) + res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height) + getPaddingTop() + getPaddingBottom(); setMeasuredDimension(width, height); diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt index ee9da2aa..39ece100 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt @@ -140,7 +140,7 @@ open class KeyboardBuilder(protected val mContext: Context, fun loadFromXml(xmlId: Int, id: KeyboardId): KeyboardBuilder { if (Settings.getInstance().current.mUseNewKeyboardParsing) { - if (id.mElementId >= KeyboardId.ELEMENT_EMOJI_RECENTS && id.mElementId <= KeyboardId.ELEMENT_EMOJI_CATEGORY16) { + if (id.isEmojiKeyboard) { mParams.mId = id readAttributes(R.xml.kbd_emoji_category1) // all the same anyway, gridRows are ignored keysInRows = EmojiParser(mParams, mContext).parse(Settings.getInstance().current.mIsSplitKeyboardEnabled) @@ -222,10 +222,7 @@ open class KeyboardBuilder(protected val mContext: Context, Log.d(TAG, "setting size and position for ${it.mLabel}, ${it.mCode}: x ${currentX.toInt()}, w ${it.mFullWidth.toInt()}") currentX += it.mFullWidth } - // need to truncate to int here, otherwise it may end up one pixel lower than original - // though actually not truncating would be more correct... but that's already an y / height issue somewhere in Key - // todo (later): round, and do the change together with the some thing in Key(KeyParams keyParams) - currentY += row.first().mFullHeight.toInt() + currentY += row.first().mFullHeight } } @@ -371,8 +368,13 @@ open class KeyboardBuilder(protected val mContext: Context, mParams.removeRedundantMoreKeys() // {@link #parseGridRows(XmlPullParser,boolean)} may populate keyboard rows higher than // previously expected. + // todo (low priority): mCurrentY may end up too high with the new parser and 4 row keyboards in landscape mode + // -> why is this happening? + // but anyway, since the height is resized correctly already, we don't need to adjust the + // occupied height, except for the scrollable emoji keyoards + if (!mParams.mId.isEmojiKeyboard) return val actualHeight = mCurrentY - mParams.mVerticalGap + mParams.mBottomPadding - mParams.mOccupiedHeight = Math.max(mParams.mOccupiedHeight, actualHeight) + mParams.mOccupiedHeight = mParams.mOccupiedHeight.coerceAtLeast(actualHeight) } private fun addKeysToParams() { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/EmojiParser.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/EmojiParser.kt index 6c80e7c4..adabc870 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/EmojiParser.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/EmojiParser.kt @@ -10,6 +10,9 @@ import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardParams import org.dslul.openboard.inputmethod.latin.R import org.dslul.openboard.inputmethod.latin.common.Constants import org.dslul.openboard.inputmethod.latin.common.StringUtils +import org.dslul.openboard.inputmethod.latin.settings.Settings +import kotlin.math.round +import kotlin.math.sqrt class EmojiParser(private val params: KeyboardParams, private val context: Context) { @@ -60,10 +63,18 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte val row = ArrayList(emojiArray.size) var currentX = params.mLeftPadding.toFloat() val currentY = params.mTopPadding.toFloat() + + val widthScale = getWidthScale() + // extra scale for height only, to undo the effect of number row increasing absolute key height + // todo: with this things look ok, but number row still slightly affects emoji size (which it should not) + val numScale = if (Settings.getInstance().current.mShowsNumberRow) 1.25f else 1f + emojiArray.forEachIndexed { i, codeArraySpec -> val keyParams = parseEmojiKey(codeArraySpec, moreEmojisArray?.get(i)?.takeIf { it.isNotEmpty() }) ?: return@forEachIndexed keyParams.setDimensionsFromRelativeSize(currentX, currentY) - currentX += keyParams.mFullWidth // exact value seems to be not really relevant, but keeping 0 doesn't work + keyParams.mFullHeight /= numScale + keyParams.mFullWidth *= widthScale + currentX += keyParams.mFullWidth row.add(keyParams) // if (row.size % numColumns == spacerIndex) { // also removed for now (would be missing setting the size and updating x // repeat(spacerNumKeys) { row.add(KeyParams.newSpacer(params, params.mDefaultRelativeKeyWidth)) } @@ -72,6 +83,16 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte return arrayListOf(row) } + private fun getWidthScale(): Float { + // height scale affects emoji size, but then emojis may be too wide or too narrow + // so we re-scale width too + // but not with exactly the same factor, adjust it a little so emojis fill the entire available width + // this looks much better than setting some offset in DynamicGridKeyboard (to center the rows) + val numColumnsNew = round(1f / (params.mDefaultRelativeKeyWidth * sqrt(Settings.getInstance().current.mKeyboardHeightScale))) + val numColumnsOld = round(1f / params.mDefaultRelativeKeyWidth) + return numColumnsOld / numColumnsNew - 0.0001f // small offset to have more emojis in a row in edge cases + } + // private fun Float.roundTo(even: Boolean) = if (toInt() % 2 == if (even) 0 else 1) toInt() else toInt() + 1 private fun getLabelAndCode(spec: String): Pair? { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/KeyboardParser.kt index 7fb93850..9e4618a4 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -70,20 +70,14 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } // rescale height if we have more than 4 rows (todo: there is some default row count in params that could be used) val heightRescale = if (keysInRows.size > 4) 4f / keysInRows.size else 1f - if (params.mId.mNumberRowEnabled && params.mId.mElementId <= KeyboardId.ELEMENT_SYMBOLS_SHIFTED) - keysInRows.add(0, getNumberRow()) // todo: maybe this should be in createAlphaSymbolRows, but first need to decide height stuff below if (heightRescale != 1f) { - // rescale all keys, so number row doesn't look weird (this is done like in current parsing) - // todo: in symbols view, number row is not rescaled - // so the symbols keyboard is higher than the normal one - // not a new issue, but should be solved in this migration - // how? possibly scale all keyboards to height of main alphabet? (consider suggestion strip) keysInRows.forEach { row -> row.forEach { it.mRelativeHeight *= heightRescale } } } return keysInRows } + // todo: get rid if the bottom-to-top thing, feels weird (then number row could also be added first) private fun createAlphaSymbolRows(baseKeys: MutableList>): ArrayList> { // number row related modifications of baseKeys if (!params.mId.mNumberRowEnabled && params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { @@ -97,7 +91,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co ) { // add number to the first 10 keys in first row // setting the correct moreKeys is handled in PopupSet - // not for korean/lao/thai layouts, todo: should be decided in the layout, not in the parser + // not for korean/lao/thai layouts, todo: should be decided in the layout / layoutInfos, not in the parser baseKeys.first().take(10).forEachIndexed { index, keyData -> keyData.popup.numberIndex = index } if (DebugFlags.DEBUG_ENABLED && baseKeys.first().size < 10) { val message = "first row only has ${baseKeys.first().size} keys: ${baseKeys.first().map { it.label }}" @@ -192,6 +186,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co functionalKeysRight.forEach { paramsRow.add(it) } keysInRows.add(0, paramsRow) // we're doing it backwards, so add on top } + if (params.mId.mNumberRowEnabled) + keysInRows.add(0, getNumberRow()) resizeLastNormalRowIfNecessaryForAlignment(keysInRows) return keysInRows } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/KeyboardWrapperView.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/KeyboardWrapperView.kt index 674875e6..1d44e5f4 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/KeyboardWrapperView.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/KeyboardWrapperView.kt @@ -4,9 +4,11 @@ package org.dslul.openboard.inputmethod.latin import android.annotation.SuppressLint import android.content.Context +import android.content.res.Configuration import android.graphics.Color import android.util.AttributeSet import android.view.Gravity +import android.view.MotionEvent import android.view.View import android.widget.FrameLayout import android.widget.ImageButton @@ -14,6 +16,8 @@ import org.dslul.openboard.inputmethod.keyboard.KeyboardActionListener import org.dslul.openboard.inputmethod.latin.common.BackgroundType import org.dslul.openboard.inputmethod.latin.common.Constants import org.dslul.openboard.inputmethod.latin.settings.Settings +import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils +import kotlin.math.abs class KeyboardWrapperView @JvmOverloads constructor( context: Context, @@ -26,8 +30,10 @@ class KeyboardWrapperView @JvmOverloads constructor( private lateinit var stopOneHandedModeBtn: ImageButton private lateinit var switchOneHandedModeBtn: ImageButton private lateinit var keyboardView: View + private lateinit var resizeOneHandedModeBtn: ImageButton private val iconStopOneHandedModeId: Int private val iconSwitchOneHandedModeId: Int + private val iconResizeOneHandedModeId: Int var oneHandedModeEnabled = false set(enabled) { @@ -44,6 +50,7 @@ class KeyboardWrapperView @JvmOverloads constructor( } + @SuppressLint("ClickableViewAccessibility") override fun onFinishInflate() { super.onFinishInflate() stopOneHandedModeBtn = findViewById(R.id.btn_stop_one_handed_mode) @@ -52,11 +59,41 @@ class KeyboardWrapperView @JvmOverloads constructor( switchOneHandedModeBtn = findViewById(R.id.btn_switch_one_handed_mode) switchOneHandedModeBtn.setImageResource(iconSwitchOneHandedModeId) switchOneHandedModeBtn.visibility = GONE + resizeOneHandedModeBtn = findViewById(R.id.btn_resize_one_handed_mode) + resizeOneHandedModeBtn.setImageResource(iconResizeOneHandedModeId) + resizeOneHandedModeBtn.visibility = GONE keyboardView = findViewById(R.id.keyboard_view) stopOneHandedModeBtn.setOnClickListener(this) switchOneHandedModeBtn.setOnClickListener(this) + var x = 0f + resizeOneHandedModeBtn.setOnTouchListener { _, motionEvent -> + when (motionEvent.action) { + MotionEvent.ACTION_DOWN -> x = motionEvent.rawX + MotionEvent.ACTION_MOVE -> { + // performance is not great because settings are reloaded and keyboard is redrawn + // on every move, but it's good enough + val sign = -switchOneHandedModeBtn.scaleX + // factor 2 to make it more sensitive (maybe could be tuned a little) + val changePercent = 2 * sign * (x - motionEvent.rawX) / context.resources.displayMetrics.density + if (abs(changePercent) < 1) return@setOnTouchListener true + x = motionEvent.rawX + val prefs = DeviceProtectedUtils.getSharedPreferences(context) + val oldScale = Settings.readOneHandedModeScale(prefs, Settings.getInstance().current.mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) + val newScale = (oldScale + changePercent / 100f).coerceAtMost(2.5f).coerceAtLeast(0.5f) + if (newScale == oldScale) return@setOnTouchListener true + Settings.getInstance().writeOneHandedModeScale(newScale) + prefs.edit().putFloat(Settings.PREF_ONE_HANDED_SCALE, newScale).apply() + oneHandedModeEnabled = false // intentionally putting wrong value, so KeyboardSwitcher.setOneHandedModeEnabled does actually reload + keyboardActionListener?.onCodeInput(Constants.CODE_START_ONE_HANDED_MODE, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false) + } + else -> x = 0f + } + true + } + val colors = Settings.getInstance().current.mColors stopOneHandedModeBtn.colorFilter = colors.keyTextFilter switchOneHandedModeBtn.colorFilter = colors.keyTextFilter @@ -74,6 +111,7 @@ class KeyboardWrapperView @JvmOverloads constructor( private fun updateViewsVisibility() { stopOneHandedModeBtn.visibility = if (oneHandedModeEnabled) VISIBLE else GONE switchOneHandedModeBtn.visibility = if (oneHandedModeEnabled) VISIBLE else GONE + resizeOneHandedModeBtn.visibility = if (oneHandedModeEnabled) VISIBLE else GONE } @SuppressLint("RtlHardcoded") @@ -114,21 +152,17 @@ class KeyboardWrapperView @JvmOverloads constructor( val scale = Settings.getInstance().current.mKeyboardHeightScale // scale one-handed mode button height if keyboard height scale is < 80% - // more relevant: also change the distance, so the buttons are actually visible val heightScale = if (scale < 0.8f) scale + 0.2f else 1f val buttonsLeft = if (isLeftGravity) keyboardView.measuredWidth else 0 - stopOneHandedModeBtn.layout( - buttonsLeft + (spareWidth - stopOneHandedModeBtn.measuredWidth) / 2, - (heightScale * stopOneHandedModeBtn.measuredHeight / 2).toInt(), - buttonsLeft + (spareWidth + stopOneHandedModeBtn.measuredWidth) / 2, - (heightScale * 3 * stopOneHandedModeBtn.measuredHeight / 2).toInt() - ) - switchOneHandedModeBtn.layout( - buttonsLeft + (spareWidth - switchOneHandedModeBtn.measuredWidth) / 2, - (heightScale * 2 * stopOneHandedModeBtn.measuredHeight).toInt(), - buttonsLeft + (spareWidth + switchOneHandedModeBtn.measuredWidth) / 2, - (heightScale * (2 * stopOneHandedModeBtn.measuredHeight + switchOneHandedModeBtn.measuredHeight)).toInt() - ) + val buttonXLeft = buttonsLeft + (spareWidth - stopOneHandedModeBtn.measuredWidth) / 2 + val buttonXRight = buttonsLeft + (spareWidth + stopOneHandedModeBtn.measuredWidth) / 2 + val buttonHeight = (heightScale * stopOneHandedModeBtn.measuredHeight).toInt() + fun View.setLayout(yPosition: Int) { + layout(buttonXLeft, yPosition - buttonHeight / 2, buttonXRight, yPosition + buttonHeight / 2) + } + stopOneHandedModeBtn.setLayout((keyboardView.measuredHeight * 0.2f).toInt()) + switchOneHandedModeBtn.setLayout((keyboardView.measuredHeight * 0.5f).toInt()) + resizeOneHandedModeBtn.setLayout((keyboardView.measuredHeight * 0.8f).toInt()) } init { @@ -136,6 +170,7 @@ class KeyboardWrapperView @JvmOverloads constructor( val keyboardAttr = context.obtainStyledAttributes(attrs, R.styleable.Keyboard, defStyle, R.style.Keyboard) iconStopOneHandedModeId = keyboardAttr.getResourceId(R.styleable.Keyboard_iconStopOneHandedMode, 0) iconSwitchOneHandedModeId = keyboardAttr.getResourceId(R.styleable.Keyboard_iconSwitchOneHandedMode, 0) + iconResizeOneHandedModeId = keyboardAttr.getResourceId(R.styleable.Keyboard_iconResizeOneHandedMode, 0) keyboardAttr.recycle() } } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/PreferencesSettingsFragment.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/PreferencesSettingsFragment.java index 5bd9d40f..7c94b974 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/PreferencesSettingsFragment.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/PreferencesSettingsFragment.java @@ -78,7 +78,7 @@ public final class PreferencesSettingsFragment extends SubScreenFragment { @Override public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { refreshEnablingsOfKeypressSoundAndVibrationAndHistRetentionSettings(); - if (Settings.PREF_SHOW_POPUP_HINTS.equals(key) || Settings.PREF_HINT_LABEL_FROM_FIRST_MORE_KEY.equals(key)) + if (Settings.PREF_SHOW_POPUP_HINTS.equals(key) || Settings.PREF_HINT_LABEL_FROM_FIRST_MORE_KEY.equals(key) || Settings.PREF_SHOW_NUMBER_ROW.equals(key)) mReloadKeyboard = true; if (key.equals(Settings.PREF_LOCALIZED_NUMBER_ROW)) KeyboardLayoutSet.onSystemLocaleChanged(); 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 6be5de9b..6f379fa0 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 @@ -105,8 +105,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon"; public static final String PREF_USE_NEW_KEYBOARD_PARSING = "pref_use_new_keyboard_parsing2"; // todo: remove later - public static final String PREF_ONE_HANDED_MODE = "pref_one_handed_mode_enabled"; - public static final String PREF_ONE_HANDED_GRAVITY = "pref_one_handed_mode_gravity"; + public static final String PREF_ONE_HANDED_MODE = "pref_one_handed_mode_enabled_p_"; + public static final String PREF_ONE_HANDED_GRAVITY = "pref_one_handed_mode_gravity_p_"; + public static final String PREF_ONE_HANDED_SCALE = "pref_one_handed_mode_scale_p_"; public static final String PREF_SHOW_NUMBER_ROW = "pref_show_number_row"; public static final String PREF_LOCALIZED_NUMBER_ROW = "pref_localized_number_row"; @@ -393,21 +394,29 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return prefs.getBoolean(PREF_SHOW_SETUP_WIZARD_ICON, false); } - public static boolean readOneHandedModeEnabled(final SharedPreferences prefs) { - return prefs.getBoolean(PREF_ONE_HANDED_MODE, false); + public static boolean readOneHandedModeEnabled(final SharedPreferences prefs, final boolean portrait) { + return prefs.getBoolean(PREF_ONE_HANDED_MODE + portrait, false); } public void writeOneHandedModeEnabled(final boolean enabled) { - mPrefs.edit().putBoolean(PREF_ONE_HANDED_MODE, enabled).apply(); + mPrefs.edit().putBoolean(PREF_ONE_HANDED_MODE + (getCurrent().mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT), enabled).apply(); + } + + public static float readOneHandedModeScale(final SharedPreferences prefs, final boolean portrait) { + return prefs.getFloat(PREF_ONE_HANDED_SCALE + portrait, 1f); + } + + public void writeOneHandedModeScale(final Float scale) { + mPrefs.edit().putFloat(PREF_ONE_HANDED_SCALE + (getCurrent().mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT), scale).apply(); } @SuppressLint("RtlHardcoded") - public static int readOneHandedModeGravity(final SharedPreferences prefs) { - return prefs.getInt(PREF_ONE_HANDED_GRAVITY, Gravity.LEFT); + public static int readOneHandedModeGravity(final SharedPreferences prefs, final boolean portrait) { + return prefs.getInt(PREF_ONE_HANDED_GRAVITY + portrait, Gravity.LEFT); } public void writeOneHandedModeGravity(final int gravity) { - mPrefs.edit().putInt(PREF_ONE_HANDED_GRAVITY, gravity).apply(); + mPrefs.edit().putInt(PREF_ONE_HANDED_GRAVITY + (getCurrent().mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT), gravity).apply(); } public static boolean readHasHardwareKeyboard(final Configuration conf) { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java index 9e5e8832..41c67ad7 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java @@ -81,6 +81,7 @@ public class SettingsValues { public final long mClipboardHistoryRetentionTime; public final boolean mOneHandedModeEnabled; public final int mOneHandedModeGravity; + public final float mOneHandedModeScale; public final boolean mNarrowKeyGaps; public final int mShowMoreKeys; public final List mSecondaryLocales; @@ -217,8 +218,15 @@ public class SettingsValues { mAutospaceAfterPunctuationEnabled = Settings.readAutospaceAfterPunctuationEnabled(prefs); mClipboardHistoryEnabled = Settings.readClipboardHistoryEnabled(prefs); mClipboardHistoryRetentionTime = Settings.readClipboardHistoryRetentionTime(prefs, res); - mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs); - mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs); + + mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs, mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT); + mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs, mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT); + if (mOneHandedModeEnabled) { + final float baseScale = res.getFraction(R.fraction.config_one_handed_mode_width, 1, 1); + final float extraScale = Settings.readOneHandedModeScale(prefs, mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT); + mOneHandedModeScale = 1 - (1 - baseScale) * extraScale; + } else + mOneHandedModeScale = 1f; final InputMethodSubtype selectedSubtype = SubtypeSettingsKt.getSelectedSubtype(prefs); mSecondaryLocales = Settings.getSecondaryLocales(prefs, selectedSubtype.getLocale()); mShowMoreKeys = selectedSubtype.isAsciiCapable() 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 9b087845..9eea038f 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 @@ -176,8 +176,7 @@ public final class ResourceUtils { public static int getKeyboardWidth(final Resources res, final SettingsValues settingsValues) { final int defaultKeyboardWidth = getDefaultKeyboardWidth(res); if (settingsValues.mOneHandedModeEnabled) { - return (int) res.getFraction(R.fraction.config_one_handed_mode_width, - defaultKeyboardWidth, defaultKeyboardWidth); + return (int) (settingsValues.mOneHandedModeScale * defaultKeyboardWidth); } return defaultKeyboardWidth; } @@ -188,14 +187,14 @@ public final class ResourceUtils { } public static int getKeyboardHeight(final Resources res, final SettingsValues settingsValues) { - final int defaultKeyboardHeight = getDefaultKeyboardHeight(res); + final int defaultKeyboardHeight = getDefaultKeyboardHeight(res, settingsValues.mShowsNumberRow); // mKeyboardHeightScale Ranges from [.5,1.5], from xml/prefs_screen_appearance.xml return (int)(defaultKeyboardHeight * settingsValues.mKeyboardHeightScale); } - public static int getDefaultKeyboardHeight(final Resources res) { + private static int getDefaultKeyboardHeight(final Resources res, final boolean showsNumberRow) { final DisplayMetrics dm = res.getDisplayMetrics(); - final float keyboardHeight = res.getDimension(R.dimen.config_default_keyboard_height); + final float keyboardHeight = res.getDimension(R.dimen.config_default_keyboard_height) * (showsNumberRow ? 1.25f : 1f); final float maxKeyboardHeight = res.getFraction( R.fraction.config_max_keyboard_height, dm.heightPixels, dm.heightPixels); float minKeyboardHeight = res.getFraction( diff --git a/app/src/main/res/drawable/ic_arrow_horizontal.xml b/app/src/main/res/drawable/ic_arrow_horizontal.xml new file mode 100644 index 00000000..bc4dd8d5 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_horizontal.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/layout/main_keyboard_frame.xml b/app/src/main/res/layout/main_keyboard_frame.xml index f096fb16..1ef41b47 100644 --- a/app/src/main/res/layout/main_keyboard_frame.xml +++ b/app/src/main/res/layout/main_keyboard_frame.xml @@ -52,6 +52,14 @@ android:scaleType="fitCenter" style="?attr/suggestionWordStyle" /> + + diff --git a/app/src/main/res/values-land/config.xml b/app/src/main/res/values-land/config.xml index 0dcee600..6366e2ca 100644 --- a/app/src/main/res/values-land/config.xml +++ b/app/src/main/res/values-land/config.xml @@ -24,8 +24,8 @@ 0.0%p 5.368%p 1.020%p - 5.368%p - 1.020%p + 4.85%p + 0.920%p -22.4dp 1.6dp @@ -67,9 +67,9 @@ 8.33%p - 40%p - 70%p - 70%p + 41%p + 78%p + 78%p 32 diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index a79817a4..9a2c2580 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -271,6 +271,7 @@ + diff --git a/app/src/main/res/values/keyboard-icons-holo.xml b/app/src/main/res/values/keyboard-icons-holo.xml index 2608ba51..264b6662 100644 --- a/app/src/main/res/values/keyboard-icons-holo.xml +++ b/app/src/main/res/values/keyboard-icons-holo.xml @@ -32,6 +32,7 @@ @drawable/sym_keyboard_start_onehanded_holo @drawable/sym_keyboard_stop_onehanded_holo @drawable/sym_keyboard_switch_onehanded_holo + @drawable/ic_arrow_horizontal @drawable/sym_keyboard_numpad_key_holo @drawable/ic_arrow_right @drawable/ic_select_all diff --git a/app/src/main/res/values/keyboard-icons-lxx-light.xml b/app/src/main/res/values/keyboard-icons-lxx-light.xml index e68fb718..e8b8fa6a 100644 --- a/app/src/main/res/values/keyboard-icons-lxx-light.xml +++ b/app/src/main/res/values/keyboard-icons-lxx-light.xml @@ -37,6 +37,7 @@ @drawable/sym_keyboard_start_onehanded_lxx @drawable/sym_keyboard_stop_onehanded_lxx @drawable/ic_arrow_left + @drawable/ic_arrow_horizontal @drawable/sym_keyboard_numpad_key_lxx @drawable/ic_arrow_right @drawable/ic_select_all diff --git a/app/src/main/res/values/keyboard-icons-rounded.xml b/app/src/main/res/values/keyboard-icons-rounded.xml index cb2437bb..b8705579 100644 --- a/app/src/main/res/values/keyboard-icons-rounded.xml +++ b/app/src/main/res/values/keyboard-icons-rounded.xml @@ -36,6 +36,7 @@ @drawable/sym_keyboard_start_onehanded_rounded @drawable/sym_keyboard_stop_onehanded_rounded @drawable/ic_arrow_left_rounded + @drawable/ic_arrow_horizontal @drawable/sym_keyboard_numpad_key_lxx @drawable/ic_arrow_right_rounded @drawable/ic_select_all_rounded