Allow resizing emoji and clipboard views in one-handed mode (#337)

This commit is contained in:
Helium314 2023-12-20 19:46:18 +01:00 committed by GitHub
parent 9a62d85dbf
commit 9f21f540af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 195 additions and 146 deletions

View file

@ -7,9 +7,13 @@
package org.dslul.openboard.inputmethod.compat;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.widget.TabHost;
import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils;
/*
* Custom version of {@link TabHost} that triggers its {@link TabHost.OnTabChangeListener} when
* a tab is reselected. It is hacky but it avoids importing material widgets lib.
@ -55,4 +59,13 @@ public class TabHostCompat extends TabHost implements TabHost.OnTabChangeListene
public void setFireOnTabChangeListenerOnReselection(boolean whether) {
mFireOnTabChangeListenerOnReselection = whether;
}
@Override public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final Resources res = getContext().getResources();
// fill full width, otherwise the layout is messed up
final int width = ResourceUtils.getDefaultKeyboardWidth(res);
final int height = res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height);
setMeasuredDimension(width, height);
}
}

View file

@ -49,12 +49,12 @@ public class Keyboard {
/** Total height of the keyboard, including the padding and keys */
public final int mOccupiedHeight;
/** Total width of the keyboard, including the padding and keys */
public final int mOccupiedWidth;
public int mOccupiedWidth;
/** Base height of the keyboard, used to calculate rows' height */
public final int mBaseHeight;
/** Base width of the keyboard, used to calculate keys' width */
public final int mBaseWidth;
public int mBaseWidth;
/** The padding above the keyboard */
public final int mTopPadding;

View file

@ -46,6 +46,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private View mMainKeyboardFrame;
private MainKeyboardView mKeyboardView;
private EmojiPalettesView mEmojiPalettesView;
private View mEmojiTabStripView;
private View mSuggestionStripView;
private ClipboardHistoryView mClipboardHistoryView;
private LatinIME mLatinIME;
private RichInputMethodManager mRichImm;
@ -272,8 +274,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private void setMainKeyboardFrame(
@NonNull final SettingsValues settingsValues,
@NonNull final KeyboardSwitchState toggleState) {
final int visibility = isImeSuppressedByHardwareKeyboard(settingsValues, toggleState)
? View.GONE : View.VISIBLE;
final int visibility = isImeSuppressedByHardwareKeyboard(settingsValues, toggleState) ? View.GONE : View.VISIBLE;
mKeyboardView.setVisibility(visibility);
// The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
// @see #getVisibleKeyboardView() and
@ -281,6 +282,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mMainKeyboardFrame.setVisibility(visibility);
mEmojiPalettesView.setVisibility(View.GONE);
mEmojiPalettesView.stopEmojiPalettes();
mEmojiTabStripView.setVisibility(View.GONE);
mSuggestionStripView.setVisibility(View.VISIBLE);
mClipboardHistoryView.setVisibility(View.GONE);
mClipboardHistoryView.stopClipboardHistory();
}
@ -292,11 +295,14 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
Log.d(TAG, "setEmojiKeyboard");
}
final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
mMainKeyboardFrame.setVisibility(View.GONE);
mMainKeyboardFrame.setVisibility(View.VISIBLE);
// The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
// @see #getVisibleKeyboardView() and
// @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets)
mKeyboardView.setVisibility(View.GONE);
mSuggestionStripView.setVisibility(View.GONE);
mEmojiTabStripView.setVisibility(View.VISIBLE);
mClipboardHistoryView.setVisibility(View.GONE);
mEmojiPalettesView.startEmojiPalettes(
mKeyboardLayoutSet.mLocaleKeyTexts.getLabelAlphabet(),
mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet);
@ -310,11 +316,14 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
Log.d(TAG, "setClipboardKeyboard");
}
final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
mMainKeyboardFrame.setVisibility(View.GONE);
mMainKeyboardFrame.setVisibility(View.VISIBLE);
// The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
// @see #getVisibleKeyboardView() and
// @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets)
mKeyboardView.setVisibility(View.GONE);
mEmojiTabStripView.setVisibility(View.GONE);
mSuggestionStripView.setVisibility(View.VISIBLE);
mEmojiPalettesView.setVisibility(View.GONE);
mClipboardHistoryView.startClipboardHistory(
mLatinIME.getClipboardHistoryManager(),
mKeyboardLayoutSet.mLocaleKeyTexts.getLabelAlphabet(),
@ -429,15 +438,23 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
if (mKeyboardViewWrapper.getOneHandedModeEnabled() == enabled) {
return;
}
mEmojiPalettesView.clearKeyboardCache();
final Settings settings = Settings.getInstance();
mKeyboardViewWrapper.setOneHandedModeEnabled(enabled);
mKeyboardViewWrapper.setOneHandedGravity(settings.getCurrent().mOneHandedModeGravity);
settings.writeOneHandedModeEnabled(enabled);
// Reload the entire keyboard set with the same parameters
// Reload the entire keyboard set with the same parameters, and switch to the previous layout
boolean wasEmoji = isShowingEmojiPalettes();
boolean wasClipboard = isShowingClipboardHistory();
loadKeyboard(mLatinIME.getCurrentInputEditorInfo(), settings.getCurrent(),
mLatinIME.getCurrentAutoCapsState(), mLatinIME.getCurrentRecapitalizeState());
if (wasEmoji)
setEmojiKeyboard();
else if (wasClipboard) {
setClipboardKeyboard();
}
}
// Implements {@link KeyboardState.SwitchActions}.
@ -499,9 +516,17 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
} else if (isShowingClipboardHistory()) {
return mClipboardHistoryView;
}
return mKeyboardView;
}
public View getWrapperView() {
return mKeyboardViewWrapper;
}
public View getEmojiTabStrip() {
return mEmojiTabStripView;
}
public MainKeyboardView getMainKeyboardView() {
return mKeyboardView;
}
@ -540,6 +565,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
mClipboardHistoryView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
mClipboardHistoryView.setKeyboardActionListener(mLatinIME);
mEmojiTabStripView = mCurrentInputView.findViewById(R.id.emoji_tab_strip);
mSuggestionStripView = mCurrentInputView.findViewById(R.id.suggestion_strip_view);
mEmojiPalettesView.initialStart();
return mCurrentInputView;
}

View file

@ -213,12 +213,7 @@ public class KeyboardView extends View {
}
mKeyboard = keyboard;
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);
mKeyScaleForText = (float) Math.sqrt(1 / Settings.getInstance().getCurrent().mKeyboardHeightScale);
final int scaledKeyHeight = (int) ((keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap) * mKeyScaleForText);
mKeyDrawParams.updateParams(scaledKeyHeight, mKeyVisualAttributes);
mKeyDrawParams.updateParams(scaledKeyHeight, keyboard.mKeyVisualAttributes);
@ -362,6 +357,7 @@ public class KeyboardView extends View {
canvas.translate(keyDrawX, keyDrawY);
final KeyVisualAttributes attr = key.getVisualAttributes();
// don't use the raw key height, linear font scaling with height is too extreme
final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams((int) (key.getHeight() * mKeyScaleForText), attr);
params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;

View file

@ -56,14 +56,16 @@ class ClipboardHistoryView @JvmOverloads constructor(
keyboardViewAttr.recycle()
}
// todo: add another strip to clipboard, with select all, arrow keys, select, copy, clear buttons
// at the bottom, remove the clear button and add the keys like in the emoji view (abc, space, delete)
// also allow swipe to remove a word from clipboard history (except current clip and pinned clips)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val res = context.resources
// The main keyboard expands to the entire this {@link KeyboardView}.
val width = (ResourceUtils.getDefaultKeyboardWidth(res) + paddingLeft + paddingRight)
val height = (ResourceUtils.getKeyboardHeight(res, Settings.getInstance().current)
+ res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height)
+ paddingTop + paddingBottom)
val width = ResourceUtils.getKeyboardWidth(res, Settings.getInstance().current) + paddingLeft + paddingRight
val height = ResourceUtils.getKeyboardHeight(res, Settings.getInstance().current) + paddingTop + paddingBottom
findViewById<FrameLayout>(R.id.clipboard_action_bar)?.layoutParams?.width = width
setMeasuredDimension(width, height)
}
@ -154,6 +156,7 @@ class ClipboardHistoryView @JvmOverloads constructor(
}
clipboardRecyclerView.apply {
adapter = clipboardAdapter
layoutParams.width = ResourceUtils.getKeyboardWidth(context.resources, Settings.getInstance().current)
}
Settings.getInstance().current.mColors.setBackground(this, ColorType.CLIPBOARD_BACKGROUND)
}

View file

@ -26,8 +26,7 @@ class ClipboardLayoutParams(res: Resources) {
init {
val defaultKeyboardHeight = ResourceUtils.getKeyboardHeight(res, Settings.getInstance().current)
val suggestionStripHeight = res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height)
val defaultKeyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res)
val defaultKeyboardWidth = ResourceUtils.getKeyboardWidth(res, Settings.getInstance().current)
if (Settings.getInstance().current.mNarrowKeyGaps) {
keyVerticalGap = res.getFraction(R.fraction.config_key_vertical_gap_holo_narrow,
@ -45,8 +44,9 @@ class ClipboardLayoutParams(res: Resources) {
topPadding = res.getFraction(R.fraction.config_keyboard_top_padding_holo,
defaultKeyboardHeight, defaultKeyboardHeight).toInt()
actionBarHeight = (defaultKeyboardHeight - bottomPadding - topPadding) / DEFAULT_KEYBOARD_ROWS - keyVerticalGap / 2
listHeight = defaultKeyboardHeight + suggestionStripHeight - actionBarHeight - bottomPadding
val rowCount = DEFAULT_KEYBOARD_ROWS + if (Settings.getInstance().current.mShowsNumberRow) 1 else 0
actionBarHeight = (defaultKeyboardHeight - bottomPadding - topPadding) / rowCount - keyVerticalGap / 2
listHeight = defaultKeyboardHeight - actionBarHeight - bottomPadding
}
fun setListProperties(recycler: RecyclerView) {

View file

@ -49,19 +49,33 @@ final class DynamicGridKeyboard extends Keyboard {
private List<Key> mCachedGridKeys;
public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
final int maxKeyCount, final int categoryId) {
final int maxKeyCount, final int categoryId, final int width) {
super(templateKeyboard);
// todo: would be better to keep them final and not require width, but how to properly set width of the template keyboard?
// an alternative would be to always create the templateKeyboard with full width
final int paddingWidth = mOccupiedWidth - mBaseWidth;
mBaseWidth = width - paddingWidth;
mOccupiedWidth = width;
final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
mHorizontalGap = Math.abs(key1.getX() - key0.getX()) - key0.getWidth();
mHorizontalStep = key0.getWidth() + mHorizontalGap;
mVerticalStep = key0.getHeight() + mVerticalGap;
final int horizontalGap = Math.abs(key1.getX() - key0.getX()) - key0.getWidth();
final float widthScale = determineWidthScale(key0.getWidth() + horizontalGap);
mHorizontalGap = (int) (horizontalGap * widthScale);
mHorizontalStep = (int) ((key0.getWidth() + horizontalGap) * widthScale);
mVerticalStep = (int) ((key0.getHeight() + mVerticalGap) / Math.sqrt(Settings.getInstance().getCurrent().mKeyboardHeightScale));
mColumnsNum = mBaseWidth / mHorizontalStep;
mMaxKeyCount = maxKeyCount;
mIsRecents = categoryId == EmojiCategory.ID_RECENTS;
mPrefs = prefs;
}
// determine a width scale so emojis evenly fill the entire width
private float determineWidthScale(final float horizontalStep) {
final float columnsNumRaw = mBaseWidth / horizontalStep;
final float columnsNum = Math.round(columnsNumRaw);
return columnsNumRaw / columnsNum;
}
private Key getTemplateKey(final int code) {
for (final Key key : super.getSortedKeys()) {
if (key.getCode() == code) {

View file

@ -20,6 +20,7 @@ import org.dslul.openboard.inputmethod.keyboard.KeyboardId;
import org.dslul.openboard.inputmethod.keyboard.KeyboardLayoutSet;
import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.settings.Settings;
import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils;
import java.util.ArrayList;
import java.util.Collections;
@ -118,8 +119,7 @@ final class EmojiCategory {
private final HashMap<String, Integer> mCategoryNameToIdMap = new HashMap<>();
private final int[] mCategoryTabIconId = new int[sCategoryName.length];
private final ArrayList<CategoryProperties> mShownCategories = new ArrayList<>();
private final ConcurrentHashMap<Long, DynamicGridKeyboard> mCategoryKeyboardMap =
new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, DynamicGridKeyboard> mCategoryKeyboardMap = new ConcurrentHashMap<>();
private int mCurrentCategoryId = EmojiCategory.ID_UNSPECIFIED;
private int mCurrentCategoryPageId = 0;
@ -132,8 +132,7 @@ final class EmojiCategory {
mLayoutSet = layoutSet;
for (int i = 0; i < sCategoryName.length; ++i) {
mCategoryNameToIdMap.put(sCategoryName[i], i);
mCategoryTabIconId[i] = emojiPaletteViewAttr.getResourceId(
sCategoryTabIconAttr[i], 0);
mCategoryTabIconId[i] = emojiPaletteViewAttr.getResourceId(sCategoryTabIconAttr[i], 0);
}
int defaultCategoryId = EmojiCategory.ID_SMILEYS_EMOTION;
@ -168,6 +167,10 @@ final class EmojiCategory {
}
}
public void clearKeyboardCache() {
mCategoryKeyboardMap.clear();
}
private void addShownCategoryId(final int categoryId) {
// Load a keyboard of categoryId
final CategoryProperties properties = new CategoryProperties(categoryId);
@ -294,10 +297,11 @@ final class EmojiCategory {
return mCategoryKeyboardMap.get(categoryKeyboardMapKey);
}
final int currentWidth = ResourceUtils.getKeyboardWidth(mRes, Settings.getInstance().getCurrent());
if (categoryId == EmojiCategory.ID_RECENTS) {
final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
mMaxRecentsKeyCount, categoryId);
mMaxRecentsKeyCount, categoryId, currentWidth);
mCategoryKeyboardMap.put(categoryKeyboardMapKey, kbd);
return kbd;
}
@ -309,15 +313,14 @@ final class EmojiCategory {
for (int pageId = 0; pageId < sortedKeysPages.length; ++pageId) {
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
keyCountPerPage, categoryId);
keyCountPerPage, categoryId, currentWidth);
for (final Key emojiKey : sortedKeysPages[pageId]) {
if (emojiKey == null) {
break;
}
tempKeyboard.addKeyLast(emojiKey);
}
mCategoryKeyboardMap.put(
getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard);
mCategoryKeyboardMap.put(getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard);
}
return mCategoryKeyboardMap.get(categoryKeyboardMapKey);
}
@ -326,7 +329,7 @@ final class EmojiCategory {
private int computeMaxKeyCountPerPage() {
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
0, 0);
0, 0, ResourceUtils.getKeyboardWidth(mRes, Settings.getInstance().getCurrent()));
return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getColumnsCount();
}

View file

@ -32,7 +32,7 @@ final class EmojiLayoutParams {
public EmojiLayoutParams(final Resources res) {
final SettingsValues settingsValues = Settings.getInstance().getCurrent();
final int defaultKeyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues);
final int defaultKeyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
final int defaultKeyboardWidth = ResourceUtils.getKeyboardWidth(res, settingsValues);
if (settingsValues.mNarrowKeyGaps) {
mKeyVerticalGap = (int) res.getFraction(R.fraction.config_key_vertical_gap_holo_narrow,
defaultKeyboardHeight, defaultKeyboardHeight);
@ -77,6 +77,7 @@ final class EmojiLayoutParams {
public void setActionBarProperties(final LinearLayout ll) {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
lp.height = getActionBarHeight();
lp.width = ResourceUtils.getKeyboardWidth(ll.getResources(), Settings.getInstance().getCurrent());
ll.setLayoutParams(lp);
}

View file

@ -110,8 +110,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
final Keyboard keyboard = getKeyboard();
if (keyboard instanceof DynamicGridKeyboard) {
final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
final int occupiedHeight =
((DynamicGridKeyboard) keyboard).getDynamicOccupiedHeight();
final int occupiedHeight = ((DynamicGridKeyboard) keyboard).getDynamicOccupiedHeight();
final int height = occupiedHeight + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(width, height);
return;
@ -185,8 +184,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
}
final View container = mMoreKeysKeyboardContainer;
final MoreKeysKeyboardView moreKeysKeyboardView =
container.findViewById(R.id.more_keys_keyboard_view);
final MoreKeysKeyboardView moreKeysKeyboardView = container.findViewById(R.id.more_keys_keyboard_view);
moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@ -198,8 +196,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
? CoordinateUtils.x(lastCoords)
: key.getX() + key.getWidth() / 2;
final int pointY = key.getY();
moreKeysKeyboardView.showMoreKeysPanel(this, this,
pointX, pointY, mListener);
moreKeysKeyboardView.showMoreKeysPanel(this, this, pointX, pointY, mListener);
return moreKeysKeyboardView;
}
@ -253,10 +250,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
*/
@Override
public boolean onHoverEvent(final MotionEvent event) {
final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> accessibilityDelegate =
mAccessibilityDelegate;
if (accessibilityDelegate != null
&& AccessibilityUtils.Companion.getInstance().isTouchExplorationEnabled()) {
final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> accessibilityDelegate = mAccessibilityDelegate;
if (accessibilityDelegate != null && AccessibilityUtils.Companion.getInstance().isTouchExplorationEnabled()) {
return accessibilityDelegate.onHoverEvent(event);
}
return super.onHoverEvent(event);
@ -387,12 +382,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
} else if (key == currentKey && pendingKeyDown != null) {
pendingKeyDown.run();
// Trigger key-release event a little later so that a user can see visual feedback.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
callListenerOnReleaseKey(key, true /* withRegistering */);
}
}, KEY_RELEASE_DELAY_TIME);
mHandler.postDelayed(() -> callListenerOnReleaseKey(key, true), KEY_RELEASE_DELAY_TIME);
} else if (key != null) {
callListenerOnReleaseKey(key, true /* withRegistering */);
}
@ -402,7 +392,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
}
public boolean onCancel(final MotionEvent e) {
releaseCurrentKey(false /* withKeyRegistering */);
releaseCurrentKey(false);
dismissMoreKeysPanel();
cancelLongPress();
return true;
@ -417,7 +407,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
// Touched key has changed, release previous key's callbacks and
// re-register them for the new key.
if (key != mCurrentKey && !isShowingMoreKeysPanel) {
releaseCurrentKey(false /* withKeyRegistering */);
releaseCurrentKey(false);
mCurrentKey = key;
if (key == null) {
return false;

View file

@ -30,6 +30,7 @@ import org.dslul.openboard.inputmethod.compat.TabHostCompat;
import org.dslul.openboard.inputmethod.keyboard.Key;
import org.dslul.openboard.inputmethod.keyboard.KeyboardActionListener;
import org.dslul.openboard.inputmethod.keyboard.KeyboardLayoutSet;
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher;
import org.dslul.openboard.inputmethod.keyboard.KeyboardView;
import org.dslul.openboard.inputmethod.keyboard.internal.KeyDrawParams;
import org.dslul.openboard.inputmethod.keyboard.internal.KeyVisualAttributes;
@ -108,7 +109,7 @@ public final class EmojiPalettesView extends LinearLayout
final Resources res = context.getResources();
mEmojiLayoutParams = new EmojiLayoutParams(res);
builder.setSubtype(RichInputMethodSubtype.getEmojiSubtype());
builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res),
builder.setKeyboardGeometry(ResourceUtils.getKeyboardWidth(res, Settings.getInstance().getCurrent()),
mEmojiLayoutParams.mEmojiKeyboardHeight);
final KeyboardLayoutSet layoutSet = builder.build();
final TypedArray emojiPalettesViewAttr = context.obtainStyledAttributes(attrs,
@ -133,10 +134,9 @@ public final class EmojiPalettesView extends LinearLayout
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final Resources res = getContext().getResources();
// The main keyboard expands to the entire this {@link KeyboardView}.
final int width = ResourceUtils.getDefaultKeyboardWidth(res)
final int width = ResourceUtils.getKeyboardWidth(res, Settings.getInstance().getCurrent())
+ getPaddingLeft() + getPaddingRight();
final int height = ResourceUtils.getKeyboardHeight(res, Settings.getInstance().getCurrent())
+ res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height)
+ getPaddingTop() + getPaddingBottom();
setMeasuredDimension(width, height);
}
@ -155,14 +155,10 @@ public final class EmojiPalettesView extends LinearLayout
host.addTab(tspec);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTabHost = findViewById(R.id.emoji_category_tabhost);
public void initialStart() { // needs to be delayed for access to EmojiTabStrip, which is not a child of this view
mTabHost = KeyboardSwitcher.getInstance().getEmojiTabStrip().findViewById(R.id.emoji_category_tabhost);
mTabHost.setup();
for (final EmojiCategory.CategoryProperties properties
: mEmojiCategory.getShownCategories()) {
for (final EmojiCategory.CategoryProperties properties : mEmojiCategory.getShownCategories()) {
addTab(mTabHost, properties.mCategoryId);
}
mTabHost.setOnTabChangedListener(this);
@ -226,9 +222,6 @@ public final class EmojiPalettesView extends LinearLayout
// Enable reselection after the first setCurrentCategoryAndPageId() init call
mTabHost.setFireOnTabChangeListenerOnReselection(true);
final LinearLayout actionBar = findViewById(R.id.emoji_action_bar);
mEmojiLayoutParams.setActionBarProperties(actionBar);
// deleteKey depends only on OnTouchListener.
mDeleteKey = findViewById(R.id.emoji_keyboard_delete);
mDeleteKey.setBackgroundResource(mFunctionalKeyBackgroundId);
@ -378,6 +371,7 @@ public final class EmojiPalettesView extends LinearLayout
if (deleteIconResId != 0) {
mDeleteKey.setImageResource(deleteIconResId);
}
mEmojiLayoutParams.setActionBarProperties(findViewById(R.id.emoji_action_bar));
final KeyDrawParams params = new KeyDrawParams();
params.updateParams(mEmojiLayoutParams.getActionBarHeight(), keyVisualAttr);
setupAlphabetKey(mAlphabetKeyLeft, switchToAlphaLabel, params);
@ -480,4 +474,8 @@ public final class EmojiPalettesView extends LinearLayout
v.setPressed(false);
}
}
public void clearKeyboardCache() {
mEmojiCategory.clearKeyboardCache();
}
}

View file

@ -11,7 +11,7 @@ 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 org.dslul.openboard.inputmethod.latin.utils.ResourceUtils
import kotlin.math.sqrt
class EmojiParser(private val params: KeyboardParams, private val context: Context) {
@ -62,18 +62,23 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
*/
val row = ArrayList<KeyParams>(emojiArray.size)
var currentX = params.mLeftPadding.toFloat()
val currentY = params.mTopPadding.toFloat()
val currentY = params.mTopPadding.toFloat() // no need to ever change, assignment to rows into rows is done in DynamicGridKeyboard
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
// determine key width for default settings (no number row, no one-handed mode, 100% height and bottom padding scale)
// this is a bit long, but ensures that emoji size stays the same, independent of these settings
val defaultKeyWidth = (ResourceUtils.getDefaultKeyboardWidth(context.resources) - params.mLeftPadding - params.mRightPadding) * params.mDefaultRelativeKeyWidth
val keyWidth = defaultKeyWidth * sqrt(Settings.getInstance().current.mKeyboardHeightScale)
val defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(context.resources, false)
val defaultBottomPadding = context.resources.getFraction(R.fraction.config_keyboard_bottom_padding_holo, defaultKeyboardHeight, defaultKeyboardHeight);
val emojiKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(context.resources, false) * 0.75f + params.mVerticalGap - defaultBottomPadding - context.resources.getDimensionPixelSize(R.dimen.config_emoji_category_page_id_height)
val keyHeight = emojiKeyboardHeight * params.mDefaultRelativeRowHeight * Settings.getInstance().current.mKeyboardHeightScale // still apply height scale to key
emojiArray.forEachIndexed { i, codeArraySpec ->
val keyParams = parseEmojiKey(codeArraySpec, moreEmojisArray?.get(i)?.takeIf { it.isNotEmpty() }) ?: return@forEachIndexed
keyParams.setDimensionsFromRelativeSize(currentX, currentY)
keyParams.mFullHeight /= numScale
keyParams.mFullWidth *= widthScale
keyParams.xPos = currentX
keyParams.yPos = currentY
keyParams.mFullWidth = keyWidth
keyParams.mFullHeight = keyHeight
currentX += keyParams.mFullWidth
row.add(keyParams)
// if (row.size % numColumns == spacerIndex) { // also removed for now (would be missing setting the size and updating x
@ -83,18 +88,6 @@ 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<String, Int>? {
val specAndSdk = spec.split("||")
if (specAndSdk.getOrNull(1)?.toIntOrNull()?.let { it > Build.VERSION.SDK_INT } == true) return null

View file

@ -5,7 +5,6 @@ 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
@ -13,6 +12,7 @@ import android.view.View
import android.widget.FrameLayout
import android.widget.ImageButton
import org.dslul.openboard.inputmethod.keyboard.KeyboardActionListener
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher
import org.dslul.openboard.inputmethod.latin.common.ColorType
import org.dslul.openboard.inputmethod.latin.common.Constants
import org.dslul.openboard.inputmethod.latin.settings.Settings
@ -29,7 +29,6 @@ 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
@ -62,7 +61,6 @@ class KeyboardWrapperView @JvmOverloads constructor(
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)
@ -141,6 +139,7 @@ class KeyboardWrapperView @JvmOverloads constructor(
val isLeftGravity = oneHandedGravity == Gravity.LEFT
val width = right - left
val keyboardView = KeyboardSwitcher.getInstance().visibleKeyboardView
val spareWidth = width - keyboardView.measuredWidth
val keyboardLeft = if (isLeftGravity) 0 else spareWidth

View file

@ -1248,7 +1248,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mInputView == null) {
return;
}
final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
final View visibleKeyboardView = mKeyboardSwitcher.getWrapperView();
if (visibleKeyboardView == null || !hasSuggestionStripView()) {
return;
}
@ -1261,11 +1261,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInsetsUpdater.setInsets(outInsets);
return;
}
final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes()
&& !mKeyboardSwitcher.isShowingClipboardHistory()
&& mSuggestionStripView.getVisibility() == View.VISIBLE)
? mSuggestionStripView.getHeight() : 0;
final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight;
final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - mSuggestionStripView.getHeight();
mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY);
// Need to set expanded touchable region only if a keyboard view is being shown.
if (visibleKeyboardView.isShown()) {

View file

@ -192,7 +192,7 @@ public final class ResourceUtils {
return (int)(defaultKeyboardHeight * settingsValues.mKeyboardHeightScale);
}
private static int getDefaultKeyboardHeight(final Resources res, final boolean showsNumberRow) {
public 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) * (showsNumberRow ? 1.25f : 1f);
final float maxKeyboardHeight = res.getFraction(

View file

@ -14,40 +14,6 @@
android:visibility="gone"
style="?attr/emojiPalettesViewStyle"
>
<LinearLayout
android:id="@+id/emoji_tab_strip"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/config_suggestions_strip_height"
style="?attr/suggestionStripViewStyle"
>
<org.dslul.openboard.inputmethod.compat.TabHostCompat
android:id="@+id/emoji_category_tabhost"
android:layout_width="0dip"
android:layout_weight="87.5"
android:layout_height="match_parent"
>
<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="0dip"
android:layout_height="0dip"
>
<!-- Empty placeholder that TabHost requires. But we don't use it to actually
display anything. We monitor the tab changes and change the ViewPager.
Similarly the ViewPager swipes are intercepted and passed to the TabHost. -->
<View
android:id="@+id/emoji_keyboard_dummy"
android:layout_width="0dip"
android:layout_height="0dip"
android:visibility="gone" />
</FrameLayout>
</org.dslul.openboard.inputmethod.compat.TabHostCompat>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/emoji_keyboard_list"
android:layout_width="match_parent"

View file

@ -13,10 +13,4 @@
<include
android:id="@+id/main_keyboard_frame"
layout="@layout/main_keyboard_frame" />
<include
android:id="@+id/emoji_palettes_view"
layout="@layout/emoji_palettes_view" />
<include
android:id="@+id/clipboard_history_view"
layout="@layout/clipboard_history_view" />
</org.dslul.openboard.inputmethod.latin.InputView>

View file

@ -12,15 +12,9 @@
android:layout_gravity="bottom"
android:orientation="vertical" >
<!-- To ensure that key preview popup is correctly placed when the current system locale is
one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
<org.dslul.openboard.inputmethod.latin.suggestions.SuggestionStripView
android:id="@+id/suggestion_strip_view"
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="@dimen/config_suggestions_strip_height"
android:gravity="center_vertical"
style="?attr/suggestionStripViewStyle" />
<include
android:id="@+id/strip_container"
layout="@layout/strip_container" />
<!-- To ensure that key preview popup is correctly placed when the current system locale is
one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
@ -35,6 +29,12 @@
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include
android:id="@+id/emoji_palettes_view"
layout="@layout/emoji_palettes_view" />
<include
android:id="@+id/clipboard_history_view"
layout="@layout/clipboard_history_view" />
<ImageButton
android:id="@+id/btn_stop_one_handed_mode"

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 The Android Open Source Project
modified
SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/config_suggestions_strip_height">
<!-- To ensure that key preview popup is correctly placed when the current system locale is
one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
<org.dslul.openboard.inputmethod.latin.suggestions.SuggestionStripView
android:id="@+id/suggestion_strip_view"
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
style="?attr/suggestionStripViewStyle" />
<LinearLayout
android:id="@+id/emoji_tab_strip"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="?attr/suggestionStripViewStyle"
>
<org.dslul.openboard.inputmethod.compat.TabHostCompat
android:id="@+id/emoji_category_tabhost"
android:layout_width="0dip"
android:layout_weight="87.5"
android:layout_height="match_parent"
>
<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="0dip"
android:layout_height="0dip"
>
<!-- Empty placeholder that TabHost requires. But we don't use it to actually
display anything. We monitor the tab changes and change the ViewPager.
Similarly the ViewPager swipes are intercepted and passed to the TabHost. -->
<View
android:id="@+id/emoji_keyboard_dummy"
android:layout_width="0dip"
android:layout_height="0dip"
android:visibility="gone" />
</FrameLayout>
</org.dslul.openboard.inputmethod.compat.TabHostCompat>
</LinearLayout>
</FrameLayout>