Implemented one-handed mode feature

This commit is contained in:
pdroidandroid@gmail.com 2022-02-20 16:14:12 +01:00
parent 56f363063d
commit 1359386d72
59 changed files with 577 additions and 333 deletions

View file

@ -32,6 +32,7 @@ import org.dslul.openboard.inputmethod.keyboard.emoji.EmojiPalettesView;
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardState; import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardState;
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardTextsSet; import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardTextsSet;
import org.dslul.openboard.inputmethod.latin.InputView; import org.dslul.openboard.inputmethod.latin.InputView;
import org.dslul.openboard.inputmethod.latin.KeyboardWrapperView;
import org.dslul.openboard.inputmethod.latin.LatinIME; import org.dslul.openboard.inputmethod.latin.LatinIME;
import org.dslul.openboard.inputmethod.latin.R; import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.RichInputMethodManager; import org.dslul.openboard.inputmethod.latin.RichInputMethodManager;
@ -51,6 +52,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private static final String TAG = KeyboardSwitcher.class.getSimpleName(); private static final String TAG = KeyboardSwitcher.class.getSimpleName();
private InputView mCurrentInputView; private InputView mCurrentInputView;
private KeyboardWrapperView mKeyboardViewWrapper;
private View mMainKeyboardFrame; private View mMainKeyboardFrame;
private MainKeyboardView mKeyboardView; private MainKeyboardView mKeyboardView;
private EmojiPalettesView mEmojiPalettesView; private EmojiPalettesView mEmojiPalettesView;
@ -117,7 +119,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
mThemeContext, editorInfo); mThemeContext, editorInfo);
final Resources res = mThemeContext.getResources(); final Resources res = mThemeContext.getResources();
final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); final int keyboardWidth = ResourceUtils.getKeyboardWidth(res, settingsValues);
final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues); final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues);
builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
builder.setSubtype(mRichImm.getCurrentSubtype()); builder.setSubtype(mRichImm.getCurrentSubtype());
@ -129,7 +131,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
&& settingsValues.mIsSplitKeyboardEnabled); && settingsValues.mIsSplitKeyboardEnabled);
mKeyboardLayoutSet = builder.build(); mKeyboardLayoutSet = builder.build();
try { try {
mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState); mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState,
settingsValues.mOneHandedModeEnabled);
mKeyboardTextsSet.setLocale(mRichImm.getCurrentSubtypeLocale(), mThemeContext); mKeyboardTextsSet.setLocale(mRichImm.getCurrentSubtypeLocale(), mThemeContext);
} catch (KeyboardLayoutSetException e) { } catch (KeyboardLayoutSetException e) {
Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
@ -424,6 +427,30 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
} }
} }
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setOneHandedModeEnabled(boolean enabled) {
if (mKeyboardViewWrapper.getOneHandedModeEnabled() == enabled) {
return;
}
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
loadKeyboard(mLatinIME.getCurrentInputEditorInfo(), settings.getCurrent(),
mLatinIME.getCurrentAutoCapsState(), mLatinIME.getCurrentRecapitalizeState());
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void switchOneHandedMode() {
mKeyboardViewWrapper.switchOneHandedModeSide();
Settings.getInstance().writeOneHandedModeGravity(mKeyboardViewWrapper.getOneHandedGravity());
}
// Implements {@link KeyboardState.SwitchActions}. // Implements {@link KeyboardState.SwitchActions}.
@Override @Override
public boolean isInDoubleTapShiftKeyTimeout() { public boolean isInDoubleTapShiftKeyTimeout() {
@ -476,7 +503,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
} else if (isShowingClipboardHistory()) { } else if (isShowingClipboardHistory()) {
return mClipboardHistoryView; return mClipboardHistoryView;
} }
return mKeyboardView; return mKeyboardViewWrapper;
} }
public MainKeyboardView getMainKeyboardView() { public MainKeyboardView getMainKeyboardView() {
@ -509,6 +536,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mEmojiPalettesView = mCurrentInputView.findViewById(R.id.emoji_palettes_view); mEmojiPalettesView = mCurrentInputView.findViewById(R.id.emoji_palettes_view);
mClipboardHistoryView = mCurrentInputView.findViewById(R.id.clipboard_history_view); mClipboardHistoryView = mCurrentInputView.findViewById(R.id.clipboard_history_view);
mKeyboardViewWrapper = mCurrentInputView.findViewById(R.id.keyboard_view_wrapper);
mKeyboardViewWrapper.setKeyboardActionListener(mLatinIME);
mKeyboardView = mCurrentInputView.findViewById(R.id.keyboard_view); mKeyboardView = mCurrentInputView.findViewById(R.id.keyboard_view);
mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled); mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
mKeyboardView.setKeyboardActionListener(mLatinIME); mKeyboardView.setKeyboardActionListener(mLatinIME);

View file

@ -54,7 +54,10 @@ public final class KeyboardCodesSet {
"key_alpha_from_emoji", "key_alpha_from_emoji",
"key_unspecified", "key_unspecified",
"key_clipboard", "key_clipboard",
"key_alpha_from_clipboard" "key_alpha_from_clipboard",
"key_start_onehanded",
"key_stop_onehanded",
"key_switch_onehanded"
}; };
private static final int[] DEFAULT = { private static final int[] DEFAULT = {
@ -76,7 +79,10 @@ public final class KeyboardCodesSet {
Constants.CODE_ALPHA_FROM_EMOJI, Constants.CODE_ALPHA_FROM_EMOJI,
Constants.CODE_UNSPECIFIED, Constants.CODE_UNSPECIFIED,
Constants.CODE_CLIPBOARD, Constants.CODE_CLIPBOARD,
Constants.CODE_ALPHA_FROM_CLIPBOARD Constants.CODE_ALPHA_FROM_CLIPBOARD,
Constants.CODE_START_ONE_HANDED_MODE,
Constants.CODE_STOP_ONE_HANDED_MODE,
Constants.CODE_SWITCH_ONE_HANDED_MODE
}; };
static { static {

View file

@ -62,6 +62,9 @@ public final class KeyboardIconsSet {
public static final String NAME_CLIPBOARD_ACTION_KEY = "clipboard_action_key"; public static final String NAME_CLIPBOARD_ACTION_KEY = "clipboard_action_key";
public static final String NAME_CLIPBOARD_NORMAL_KEY = "clipboard_normal_key"; public static final String NAME_CLIPBOARD_NORMAL_KEY = "clipboard_normal_key";
public static final String NAME_CLEAR_CLIPBOARD_KEY = "clear_clipboard_key"; public static final String NAME_CLEAR_CLIPBOARD_KEY = "clear_clipboard_key";
public static final String NAME_START_ONEHANDED_KEY = "start_onehanded_mode_key";
public static final String NAME_STOP_ONEHANDED_KEY = "stop_onehanded_mode_key";
public static final String NAME_SWITCH_ONEHANDED_KEY = "switch_onehanded_key";
private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray(); private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray();
@ -95,6 +98,9 @@ public final class KeyboardIconsSet {
NAME_CLIPBOARD_ACTION_KEY, R.styleable.Keyboard_iconClipboardActionKey, NAME_CLIPBOARD_ACTION_KEY, R.styleable.Keyboard_iconClipboardActionKey,
NAME_CLIPBOARD_NORMAL_KEY, R.styleable.Keyboard_iconClipboardNormalKey, NAME_CLIPBOARD_NORMAL_KEY, R.styleable.Keyboard_iconClipboardNormalKey,
NAME_CLEAR_CLIPBOARD_KEY, R.styleable.Keyboard_iconClearClipboardKey, NAME_CLEAR_CLIPBOARD_KEY, R.styleable.Keyboard_iconClearClipboardKey,
NAME_START_ONEHANDED_KEY, R.styleable.Keyboard_iconStartOneHandedMode,
NAME_STOP_ONEHANDED_KEY, R.styleable.Keyboard_iconStopOneHandedMode,
NAME_SWITCH_ONEHANDED_KEY, R.styleable.Keyboard_iconSwitchOneHandedMode,
}; };
private static int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2; private static int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2;

View file

@ -30,7 +30,7 @@ import org.dslul.openboard.inputmethod.latin.utils.RecapitalizeStatus;
* *
* This class contains all keyboard state transition logic. * This class contains all keyboard state transition logic.
* *
* The input events are {@link #onLoadKeyboard(int, int)}, {@link #onSaveKeyboardState()}, * The input events are {@link #onLoadKeyboard(int, int, boolean)}, {@link #onSaveKeyboardState()},
* {@link #onPressKey(int,boolean,int,int)}, {@link #onReleaseKey(int,boolean,int,int)}, * {@link #onPressKey(int,boolean,int,int)}, {@link #onReleaseKey(int,boolean,int,int)},
* {@link #onEvent(Event,int,int)}, {@link #onFinishSlidingInput(int,int)}, * {@link #onEvent(Event,int,int)}, {@link #onFinishSlidingInput(int,int)},
* {@link #onUpdateShiftState(int,int)}, {@link #onResetKeyboardStateToAlphabet(int,int)}. * {@link #onUpdateShiftState(int,int)}, {@link #onResetKeyboardStateToAlphabet(int,int)}.
@ -65,6 +65,9 @@ public final class KeyboardState {
void startDoubleTapShiftKeyTimer(); void startDoubleTapShiftKeyTimer();
boolean isInDoubleTapShiftKeyTimeout(); boolean isInDoubleTapShiftKeyTimeout();
void cancelDoubleTapShiftKeyTimer(); void cancelDoubleTapShiftKeyTimer();
void setOneHandedModeEnabled(boolean enabled);
void switchOneHandedMode();
} }
private final SwitchActions mSwitchActions; private final SwitchActions mSwitchActions;
@ -130,7 +133,8 @@ public final class KeyboardState {
mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
} }
public void onLoadKeyboard(final int autoCapsFlags, final int recapitalizeMode) { public void onLoadKeyboard(final int autoCapsFlags, final int recapitalizeMode,
final boolean onHandedModeEnabled) {
if (DEBUG_EVENT) { if (DEBUG_EVENT) {
Log.d(TAG, "onLoadKeyboard: " + stateToString(autoCapsFlags, recapitalizeMode)); Log.d(TAG, "onLoadKeyboard: " + stateToString(autoCapsFlags, recapitalizeMode));
} }
@ -147,6 +151,7 @@ public final class KeyboardState {
// Reset keyboard to alphabet mode. // Reset keyboard to alphabet mode.
setAlphabetKeyboard(autoCapsFlags, recapitalizeMode); setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
} }
mSwitchActions.setOneHandedModeEnabled(onHandedModeEnabled);
} }
// Constants for {@link SavedKeyboardState#mShiftMode} and {@link #setShifted(int)}. // Constants for {@link SavedKeyboardState#mShiftMode} and {@link #setShifted(int)}.
@ -368,6 +373,20 @@ public final class KeyboardState {
mSwitchActions.setClipboardKeyboard(); mSwitchActions.setClipboardKeyboard();
} }
private void setOneHandedModeEnabled(boolean enabled) {
if (DEBUG_INTERNAL_ACTION) {
Log.d(TAG, "setOneHandedModeEnabled");
}
mSwitchActions.setOneHandedModeEnabled(enabled);
}
private void switchOneHandedMode() {
if (DEBUG_INTERNAL_ACTION) {
Log.d(TAG, "switchOneHandedMode");
}
mSwitchActions.switchOneHandedMode();
}
public void onPressKey(final int code, final boolean isSinglePointer, final int autoCapsFlags, public void onPressKey(final int code, final boolean isSinglePointer, final int autoCapsFlags,
final int recapitalizeMode) { final int recapitalizeMode) {
if (DEBUG_EVENT) { if (DEBUG_EVENT) {
@ -697,6 +716,12 @@ public final class KeyboardState {
} }
} else if (code == Constants.CODE_ALPHA_FROM_CLIPBOARD) { } else if (code == Constants.CODE_ALPHA_FROM_CLIPBOARD) {
setAlphabetKeyboard(autoCapsFlags, recapitalizeMode); setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
} else if (code == Constants.CODE_START_ONE_HANDED_MODE) {
setOneHandedModeEnabled(true);
} else if (code == Constants.CODE_STOP_ONE_HANDED_MODE) {
setOneHandedModeEnabled(false);
} else if (code == Constants.CODE_SWITCH_ONE_HANDED_MODE) {
switchOneHandedMode();
} }
} }

View file

@ -0,0 +1,131 @@
package org.dslul.openboard.inputmethod.latin
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageButton
import org.dslul.openboard.inputmethod.keyboard.KeyboardActionListener
import org.dslul.openboard.inputmethod.latin.common.Constants
class KeyboardWrapperView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : FrameLayout(context, attrs, defStyle), View.OnClickListener {
var keyboardActionListener: KeyboardActionListener? = null
private lateinit var stopOneHandedModeBtn: ImageButton
private lateinit var switchOneHandedModeBtn: ImageButton
private lateinit var keyboardView: View
private val iconStopOneHandedModeId: Int
private val iconSwitchOneHandedModeId: Int
var oneHandedModeEnabled = false
set(enabled) {
field = enabled
updateViewsVisibility()
requestLayout()
}
var oneHandedGravity = Gravity.NO_GRAVITY
set(value) {
field = value
updateSwitchButtonSide()
requestLayout()
}
override fun onFinishInflate() {
super.onFinishInflate()
stopOneHandedModeBtn = findViewById(R.id.btn_stop_one_handed_mode)
stopOneHandedModeBtn.setImageResource(iconStopOneHandedModeId)
stopOneHandedModeBtn.visibility = GONE
switchOneHandedModeBtn = findViewById(R.id.btn_switch_one_handed_mode)
switchOneHandedModeBtn.setImageResource(iconSwitchOneHandedModeId)
switchOneHandedModeBtn.visibility = GONE
keyboardView = findViewById(R.id.keyboard_view)
stopOneHandedModeBtn.setOnClickListener(this)
switchOneHandedModeBtn.setOnClickListener(this)
}
@SuppressLint("RtlHardcoded")
fun switchOneHandedModeSide() {
oneHandedGravity = if (oneHandedGravity == Gravity.LEFT) Gravity.RIGHT else Gravity.LEFT
}
private fun updateViewsVisibility() {
stopOneHandedModeBtn.visibility = if (oneHandedModeEnabled) VISIBLE else GONE
switchOneHandedModeBtn.visibility = if (oneHandedModeEnabled) VISIBLE else GONE
}
private fun updateSwitchButtonSide() {
switchOneHandedModeBtn.scaleX = if (oneHandedGravity == Gravity.RIGHT) -1f else 1f
}
override fun onClick(view: View) {
if (view === stopOneHandedModeBtn) {
keyboardActionListener?.onCodeInput(Constants.CODE_STOP_ONE_HANDED_MODE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
false /* isKeyRepeat */)
} else if (view === switchOneHandedModeBtn) {
keyboardActionListener?.onCodeInput(Constants.CODE_SWITCH_ONE_HANDED_MODE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
false /* isKeyRepeat */)
}
}
@SuppressLint("RtlHardcoded")
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
if (!oneHandedModeEnabled) {
super.onLayout(changed, left, top, right, bottom)
return
}
val isLeftGravity = oneHandedGravity == Gravity.LEFT
val width = right - left
val spareWidth = width - keyboardView.measuredWidth
val keyboardLeft = if (isLeftGravity) 0 else spareWidth
keyboardView.layout(
keyboardLeft,
0,
keyboardLeft + keyboardView.measuredWidth,
keyboardView.measuredHeight
)
val buttonsLeft = if (isLeftGravity) keyboardView.measuredWidth else 0
stopOneHandedModeBtn.layout(
buttonsLeft + (spareWidth - stopOneHandedModeBtn.measuredWidth) / 2,
stopOneHandedModeBtn.measuredHeight / 2,
buttonsLeft + (spareWidth + stopOneHandedModeBtn.measuredWidth) / 2,
3 * stopOneHandedModeBtn.measuredHeight / 2
)
switchOneHandedModeBtn.layout(
buttonsLeft + (spareWidth - switchOneHandedModeBtn.measuredWidth) / 2,
2 * stopOneHandedModeBtn.measuredHeight,
buttonsLeft + (spareWidth + switchOneHandedModeBtn.measuredWidth) / 2,
2 * stopOneHandedModeBtn.measuredHeight + switchOneHandedModeBtn.measuredHeight
)
}
init {
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)
keyboardAttr.recycle()
val themeAttr = context.obtainStyledAttributes(attrs,
R.styleable.KeyboardTheme, defStyle, 0)
val keyboardViewStyleId = themeAttr.getResourceId(R.styleable.KeyboardTheme_mainKeyboardViewStyle, 0)
themeAttr.recycle()
val styleAttr = context.obtainStyledAttributes(keyboardViewStyleId, intArrayOf(android.R.attr.background))
setBackgroundResource(styleAttr.getResourceId(0, 0))
styleAttr.recycle()
}
}

View file

@ -1341,11 +1341,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
} }
int getCurrentAutoCapsState() { public int getCurrentAutoCapsState() {
return mInputLogic.getCurrentAutoCapsState(mSettings.getCurrent()); return mInputLogic.getCurrentAutoCapsState(mSettings.getCurrent());
} }
int getCurrentRecapitalizeState() { public int getCurrentRecapitalizeState() {
return mInputLogic.getCurrentRecapitalizeState(); return mInputLogic.getCurrentRecapitalizeState();
} }

View file

@ -247,8 +247,11 @@ public final class Constants {
public static final int CODE_SYMBOL_SHIFT = -14; public static final int CODE_SYMBOL_SHIFT = -14;
public static final int CODE_ALPHA_FROM_EMOJI = -15; public static final int CODE_ALPHA_FROM_EMOJI = -15;
public static final int CODE_ALPHA_FROM_CLIPBOARD = -16; public static final int CODE_ALPHA_FROM_CLIPBOARD = -16;
public static final int CODE_START_ONE_HANDED_MODE = -17;
public static final int CODE_STOP_ONE_HANDED_MODE = -18;
public static final int CODE_SWITCH_ONE_HANDED_MODE = -19;
// Code value representing the code is not specified. // Code value representing the code is not specified.
public static final int CODE_UNSPECIFIED = -17; public static final int CODE_UNSPECIFIED = -20;
public static boolean isLetterCode(final int code) { public static boolean isLetterCode(final int code) {
return code >= CODE_SPACE; return code >= CODE_SPACE;
@ -276,6 +279,9 @@ public final class Constants {
case CODE_TAB: return "tab"; case CODE_TAB: return "tab";
case CODE_ENTER: return "enter"; case CODE_ENTER: return "enter";
case CODE_SPACE: return "space"; case CODE_SPACE: return "space";
case CODE_START_ONE_HANDED_MODE: return "startOneHandedMode";
case CODE_STOP_ONE_HANDED_MODE: return "stopOneHandedMode";
case CODE_SWITCH_ONE_HANDED_MODE: return "switchOneHandedMode";
default: default:
if (code < CODE_SPACE) return String.format("\\u%02X", code); if (code < CODE_SPACE) return String.format("\\u%02X", code);
if (code < 0x100) return String.format("%c", code); if (code < 0x100) return String.format("%c", code);

View file

@ -717,6 +717,15 @@ public final class InputLogic {
// line, so that does affect the contents of the editor. // line, so that does affect the contents of the editor.
inputTransaction.setDidAffectContents(); inputTransaction.setDidAffectContents();
break; break;
case Constants.CODE_START_ONE_HANDED_MODE:
case Constants.CODE_STOP_ONE_HANDED_MODE:
// Note: One-handed mode activation is being
// handled in {@link KeyboardState#onEvent(Event,int)}.
break;
case Constants.CODE_SWITCH_ONE_HANDED_MODE:
// Note: Switching one-handed side is being
// handled in {@link KeyboardState#onEvent(Event,int)}.
break;
default: default:
throw new RuntimeException("Unknown key code : " + event.getMKeyCode()); throw new RuntimeException("Unknown key code : " + event.getMKeyCode());
} }

View file

@ -16,6 +16,7 @@
package org.dslul.openboard.inputmethod.latin.settings; package org.dslul.openboard.inputmethod.latin.settings;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
@ -24,6 +25,7 @@ import android.content.res.Resources;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import org.dslul.openboard.inputmethod.latin.AudioAndHapticFeedbackManager; import org.dslul.openboard.inputmethod.latin.AudioAndHapticFeedbackManager;
import org.dslul.openboard.inputmethod.latin.InputAttributes; import org.dslul.openboard.inputmethod.latin.InputAttributes;
import org.dslul.openboard.inputmethod.latin.R; import org.dslul.openboard.inputmethod.latin.R;
@ -107,6 +109,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
"pref_gesture_floating_preview_text"; "pref_gesture_floating_preview_text";
public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon"; public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon";
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_KEY_IS_INTERNAL = "pref_key_is_internal"; public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal";
public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging"; public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging";
@ -403,6 +408,23 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return prefs.getBoolean(PREF_SHOW_SETUP_WIZARD_ICON, false); 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 void writeOneHandedModeEnabled(final boolean enabled) {
mPrefs.edit().putBoolean(PREF_ONE_HANDED_MODE, enabled).apply();
}
@SuppressLint("RtlHardcoded")
public static int readOneHandedModeGravity(final SharedPreferences prefs) {
return prefs.getInt(PREF_ONE_HANDED_GRAVITY, Gravity.LEFT);
}
public void writeOneHandedModeGravity(final int gravity) {
mPrefs.edit().putInt(PREF_ONE_HANDED_GRAVITY, gravity).apply();
}
public static boolean readHasHardwareKeyboard(final Configuration conf) { public static boolean readHasHardwareKeyboard(final Configuration conf) {
// The standard way of finding out whether we have a hardware keyboard. This code is taken // The standard way of finding out whether we have a hardware keyboard. This code is taken
// from InputMethodService#onEvaluateInputShown, which canonically determines this. // from InputMethodService#onEvaluateInputShown, which canonically determines this.

View file

@ -82,6 +82,8 @@ public class SettingsValues {
public final boolean mDeleteSwipeEnabled; public final boolean mDeleteSwipeEnabled;
public final boolean mClipboardHistoryEnabled; public final boolean mClipboardHistoryEnabled;
public final long mClipboardHistoryRetentionTime; public final long mClipboardHistoryRetentionTime;
public final boolean mOneHandedModeEnabled;
public final int mOneHandedModeGravity;
// Use bigrams to predict the next word when there is no input for it yet // Use bigrams to predict the next word when there is no input for it yet
public final boolean mBigramPredictionEnabled; public final boolean mBigramPredictionEnabled;
public final boolean mGestureInputEnabled; public final boolean mGestureInputEnabled;
@ -237,6 +239,8 @@ public class SettingsValues {
mDeleteSwipeEnabled = Settings.readDeleteSwipeEnabled(prefs); mDeleteSwipeEnabled = Settings.readDeleteSwipeEnabled(prefs);
mClipboardHistoryEnabled = Settings.readClipboardHistoryEnabled(prefs); mClipboardHistoryEnabled = Settings.readClipboardHistoryEnabled(prefs);
mClipboardHistoryRetentionTime = Settings.readClipboardHistoryRetentionTime(prefs, res); mClipboardHistoryRetentionTime = Settings.readClipboardHistoryRetentionTime(prefs, res);
mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs);
mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs);
} }
public boolean isMetricsLoggingEnabled() { public boolean isMetricsLoggingEnabled() {

View file

@ -182,6 +182,15 @@ public final class ResourceUtils {
return matchedAll; return matchedAll;
} }
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_ratio,
defaultKeyboardWidth, defaultKeyboardWidth);
}
return defaultKeyboardWidth;
}
public static int getDefaultKeyboardWidth(final Resources res) { public static int getDefaultKeyboardWidth(final Resources res) {
final DisplayMetrics dm = res.getDisplayMetrics(); final DisplayMetrics dm = res.getDisplayMetrics();
return dm.widthPixels; return dm.widthPixels;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

View file

@ -37,9 +37,30 @@
<!-- To ensure that key preview popup is correctly placed when the current system locale is <!-- 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+. --> one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
<org.dslul.openboard.inputmethod.keyboard.MainKeyboardView <org.dslul.openboard.inputmethod.latin.KeyboardWrapperView
android:id="@+id/keyboard_view" android:id="@+id/keyboard_view_wrapper"
android:layoutDirection="ltr"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:layoutDirection="ltr" >
<org.dslul.openboard.inputmethod.keyboard.MainKeyboardView
android:id="@+id/keyboard_view"
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageButton
android:id="@+id/btn_stop_one_handed_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?attr/suggestionWordStyle" />
<ImageButton
android:id="@+id/btn_switch_one_handed_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?attr/suggestionWordStyle" />
</org.dslul.openboard.inputmethod.latin.KeyboardWrapperView>
</LinearLayout> </LinearLayout>

View file

@ -290,6 +290,9 @@
<attr name="iconClipboardActionKey" format="reference" /> <attr name="iconClipboardActionKey" format="reference" />
<attr name="iconClipboardNormalKey" format="reference" /> <attr name="iconClipboardNormalKey" format="reference" />
<attr name="iconClearClipboardKey" format="reference" /> <attr name="iconClearClipboardKey" format="reference" />
<attr name="iconStartOneHandedMode" format="reference" />
<attr name="iconStopOneHandedMode" format="reference" />
<attr name="iconSwitchOneHandedMode" format="reference" />
</declare-styleable> </declare-styleable>
<declare-styleable name="Keyboard_GridRows"> <declare-styleable name="Keyboard_GridRows">

View file

@ -27,6 +27,9 @@
<dimen name="config_key_hysteresis_distance">8.0dp</dimen> <dimen name="config_key_hysteresis_distance">8.0dp</dimen>
<!-- Amount of width reduction in one-handed mode -->
<fraction name="config_one_handed_mode_width_ratio">86%</fraction>
<!-- Preferable keyboard height in absolute scale: 1.285in --> <!-- Preferable keyboard height in absolute scale: 1.285in -->
<!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> <!-- This config_default_keyboard_height value should match with keyboard-heights.xml -->
<dimen name="config_default_keyboard_height">205.6dp</dimen> <dimen name="config_default_keyboard_height">205.6dp</dimen>

View file

@ -41,5 +41,8 @@
<item name="iconClipboardActionKey">@drawable/sym_keyboard_clipboard_holo_dark</item> <item name="iconClipboardActionKey">@drawable/sym_keyboard_clipboard_holo_dark</item>
<item name="iconClipboardNormalKey">@drawable/sym_keyboard_clipboard_holo_dark</item> <item name="iconClipboardNormalKey">@drawable/sym_keyboard_clipboard_holo_dark</item>
<item name="iconClearClipboardKey">@drawable/sym_keyboard_clear_clipboard_holo_dark</item> <item name="iconClearClipboardKey">@drawable/sym_keyboard_clear_clipboard_holo_dark</item>
<item name="iconStartOneHandedMode">@drawable/sym_keyboard_start_onehanded_holo_dark</item>
<item name="iconStopOneHandedMode">@drawable/sym_keyboard_stop_onehanded_holo_dark</item>
<item name="iconSwitchOneHandedMode">@drawable/sym_keyboard_switch_onehanded_holo_dark</item>
</style> </style>
</resources> </resources>

View file

@ -46,5 +46,8 @@
<item name="iconClipboardActionKey">@drawable/sym_keyboard_clipboard_lxx_dark</item> <item name="iconClipboardActionKey">@drawable/sym_keyboard_clipboard_lxx_dark</item>
<item name="iconClipboardNormalKey">@drawable/sym_keyboard_clipboard_lxx_dark</item> <item name="iconClipboardNormalKey">@drawable/sym_keyboard_clipboard_lxx_dark</item>
<item name="iconClearClipboardKey">@drawable/sym_keyboard_clear_clipboard_lxx_dark</item> <item name="iconClearClipboardKey">@drawable/sym_keyboard_clear_clipboard_lxx_dark</item>
<item name="iconStartOneHandedMode">@drawable/sym_keyboard_start_onehanded_lxx_dark</item>
<item name="iconStopOneHandedMode">@drawable/sym_keyboard_stop_onehanded_lxx_dark</item>
<item name="iconSwitchOneHandedMode">@drawable/sym_keyboard_switch_onehanded_lxx_dark</item>
</style> </style>
</resources> </resources>

View file

@ -46,5 +46,8 @@
<item name="iconClipboardActionKey">@drawable/sym_keyboard_clipboard_lxx_dark</item> <item name="iconClipboardActionKey">@drawable/sym_keyboard_clipboard_lxx_dark</item>
<item name="iconClipboardNormalKey">@drawable/sym_keyboard_clipboard_lxx_light</item> <item name="iconClipboardNormalKey">@drawable/sym_keyboard_clipboard_lxx_light</item>
<item name="iconClearClipboardKey">@drawable/sym_keyboard_clear_clipboard_lxx_light</item> <item name="iconClearClipboardKey">@drawable/sym_keyboard_clear_clipboard_lxx_light</item>
<item name="iconStartOneHandedMode">@drawable/sym_keyboard_start_onehanded_lxx_light</item>
<item name="iconStopOneHandedMode">@drawable/sym_keyboard_stop_onehanded_lxx_light</item>
<item name="iconSwitchOneHandedMode">@drawable/sym_keyboard_switch_onehanded_lxx_light</item>
</style> </style>
</resources> </resources>

View file

@ -35,36 +35,36 @@
latin:languageSwitchKeyEnabled="true" latin:languageSwitchKeyEnabled="true"
> >
<key-style <key-style
latin:styleName="settingsMoreKeysStyle" latin:styleName="settingsMoreKeysStyle"
latin:keyLabelFlags="hasPopupHint" latin:keyLabelFlags="hasPopupHint"
latin:additionalMoreKeys="!text/keyspec_settings,!text/keyspec_clipboard_normal_key" latin:additionalMoreKeys="!text/keyspec_settings,!text/keyspec_clipboard_normal_key,!text/keyspec_start_onehanded_mode"
latin:backgroundType="functional" /> latin:backgroundType="functional" />
</case> </case>
<case <case
latin:emojiKeyEnabled="false" latin:emojiKeyEnabled="false"
latin:languageSwitchKeyEnabled="true" latin:languageSwitchKeyEnabled="true"
> >
<key-style <key-style
latin:styleName="settingsMoreKeysStyle" latin:styleName="settingsMoreKeysStyle"
latin:keyLabelFlags="hasPopupHint" latin:keyLabelFlags="hasPopupHint"
latin:additionalMoreKeys="!text/keyspec_settings,!text/keyspec_clipboard_normal_key,!text/keyspec_emoji_normal_key" latin:additionalMoreKeys="!text/keyspec_settings,!text/keyspec_clipboard_normal_key,!text/keyspec_emoji_normal_key,!text/keyspec_start_onehanded_mode"
latin:backgroundType="functional" /> latin:backgroundType="functional" />
</case> </case>
<case <case
latin:emojiKeyEnabled="true" latin:emojiKeyEnabled="true"
latin:languageSwitchKeyEnabled="false" latin:languageSwitchKeyEnabled="false"
> >
<key-style <key-style
latin:styleName="settingsMoreKeysStyle" latin:styleName="settingsMoreKeysStyle"
latin:keyLabelFlags="hasPopupHint" latin:keyLabelFlags="hasPopupHint"
latin:additionalMoreKeys="!text/keyspec_settings,!icon/language_switch_key|!code/key_language_switch,!text/keyspec_clipboard_normal_key" latin:additionalMoreKeys="!text/keyspec_settings,!icon/language_switch_key|!code/key_language_switch,!text/keyspec_clipboard_normal_key,!text/keyspec_start_onehanded_mode"
latin:backgroundType="functional" /> latin:backgroundType="functional" />
</case> </case>
<default> <default>
<key-style <key-style
latin:styleName="settingsMoreKeysStyle" latin:styleName="settingsMoreKeysStyle"
latin:keyLabelFlags="hasPopupHint" latin:keyLabelFlags="hasPopupHint"
latin:additionalMoreKeys="!text/keyspec_settings,!icon/language_switch_key|!code/key_language_switch,!text/keyspec_clipboard_normal_key,!text/keyspec_emoji_normal_key" latin:additionalMoreKeys="!text/keyspec_settings,!icon/language_switch_key|!code/key_language_switch,!text/keyspec_clipboard_normal_key,!text/keyspec_emoji_normal_key,!text/keyspec_start_onehanded_mode"
latin:backgroundType="functional" /> latin:backgroundType="functional" />
</default> </default>
</switch> </switch>

View file

@ -262,6 +262,7 @@
<string name="keyspec_emoji_normal_key">!icon/emoji_normal_key|!code/key_emoji</string> <string name="keyspec_emoji_normal_key">!icon/emoji_normal_key|!code/key_emoji</string>
<string name="keyspec_clipboard_action_key">!icon/clipboard_action_key|!code/key_clipboard</string> <string name="keyspec_clipboard_action_key">!icon/clipboard_action_key|!code/key_clipboard</string>
<string name="keyspec_clipboard_normal_key">!icon/clipboard_normal_key|!code/key_clipboard</string> <string name="keyspec_clipboard_normal_key">!icon/clipboard_normal_key|!code/key_clipboard</string>
<string name="keyspec_start_onehanded_mode">!icon/start_onehanded_mode_key|!code/key_start_onehanded</string>
<string name="label_go_key">!string/label_go_key</string> <string name="label_go_key">!string/label_go_key</string>
<string name="label_send_key">!string/label_send_key</string> <string name="label_send_key">!string/label_send_key</string>
<string name="label_next_key">!string/label_next_key</string> <string name="label_next_key">!string/label_next_key</string>