mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Compare commits
14 Commits
2c632bd4bd
...
ccbd59ee80
Author | SHA1 | Date | |
---|---|---|---|
|
ccbd59ee80 | ||
|
2f8d847186 | ||
|
112b7a291a | ||
|
2e2bba2ac2 | ||
|
28f3c07d5f | ||
|
1c9c94b83d | ||
|
da95c69668 | ||
|
a668a3c801 | ||
|
c5cb5efabd | ||
|
7fdf54ec61 | ||
|
2ff68bf7cd | ||
|
3d8233be92 | ||
|
32b27b84c0 | ||
|
4566a37e16 |
@ -53,7 +53,7 @@
|
|||||||
<!-- Spoken description for the "To Alpha" keyboard key. -->
|
<!-- Spoken description for the "To Alpha" keyboard key. -->
|
||||||
<string name="spoken_description_to_alpha">Letters</string>
|
<string name="spoken_description_to_alpha">Letters</string>
|
||||||
<!-- Spoken description for the "To Numbers" keyboard key. -->
|
<!-- Spoken description for the "To Numbers" keyboard key. -->
|
||||||
<string name="spoken_description_to_numeric">Numbers</string>
|
<string name="spoken_description_to_numeric">Digits</string>
|
||||||
<!-- Spoken description for the "Settings" keyboard key. -->
|
<!-- Spoken description for the "Settings" keyboard key. -->
|
||||||
<string name="spoken_description_settings">Settings</string>
|
<string name="spoken_description_settings">Settings</string>
|
||||||
<!-- Spoken description for the "Tab" keyboard key. -->
|
<!-- Spoken description for the "Tab" keyboard key. -->
|
||||||
@ -77,6 +77,12 @@
|
|||||||
<!-- Spoken description for the "Previous" action keyboard key. -->
|
<!-- Spoken description for the "Previous" action keyboard key. -->
|
||||||
<string name="spoken_description_action_previous">Previous</string>
|
<string name="spoken_description_action_previous">Previous</string>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Spoken description for the "To Numbers" keyboard key. -->
|
||||||
|
<string name="spoken_description_to_alt_0">Page 1</string>
|
||||||
|
<string name="spoken_description_to_alt_1">Page 2</string>
|
||||||
|
<string name="spoken_description_to_alt_2">Page 3</string>
|
||||||
|
|
||||||
<!-- Spoken feedback after turning "Shift" mode on. -->
|
<!-- Spoken feedback after turning "Shift" mode on. -->
|
||||||
<string name="spoken_description_shiftmode_on">Shift enabled</string>
|
<string name="spoken_description_shiftmode_on">Shift enabled</string>
|
||||||
<!-- Spoken feedback after turning "Caps lock" mode on. -->
|
<!-- Spoken feedback after turning "Caps lock" mode on. -->
|
||||||
@ -91,6 +97,8 @@
|
|||||||
<string name="spoken_description_mode_phone">Phone mode</string>
|
<string name="spoken_description_mode_phone">Phone mode</string>
|
||||||
<!-- Spoken feedback after changing to the shifted phone dialer (symbols) keyboard. -->
|
<!-- Spoken feedback after changing to the shifted phone dialer (symbols) keyboard. -->
|
||||||
<string name="spoken_description_mode_phone_shift">Phone symbols mode</string>
|
<string name="spoken_description_mode_phone_shift">Phone symbols mode</string>
|
||||||
|
<!-- Spoken feedback after changing to the digits keyboard. -->
|
||||||
|
<string name="spoken_description_mode_digits">Digits mode</string>
|
||||||
|
|
||||||
<!-- Spoken feedback when the keyboard is hidden. -->
|
<!-- Spoken feedback when the keyboard is hidden. -->
|
||||||
<string name="announce_keyboard_hidden">Keyboard hidden</string>
|
<string name="announce_keyboard_hidden">Keyboard hidden</string>
|
||||||
|
@ -16,6 +16,11 @@
|
|||||||
<string name="more_actions_action_title">All Actions</string>
|
<string name="more_actions_action_title">All Actions</string>
|
||||||
<string name="bug_viewer_action_title">Bug Viewer</string>
|
<string name="bug_viewer_action_title">Bug Viewer</string>
|
||||||
|
|
||||||
|
<string name="left_handed_keyboard_action_title">Left-Handed Keyboard</string>
|
||||||
|
<string name="right_handed_keyboard_action_title">Right-Handed Keyboard</string>
|
||||||
|
<string name="split_keyboard_action_title">Split Keyboard</string>
|
||||||
|
<string name="floating_keyboard_action_title">Floating Keyboard</string>
|
||||||
|
|
||||||
<string name="action_kind_action_key">Action Key</string>
|
<string name="action_kind_action_key">Action Key</string>
|
||||||
<string name="action_kind_pinned_key">Pinned Action(s)</string>
|
<string name="action_kind_pinned_key">Pinned Action(s)</string>
|
||||||
<string name="action_kind_favorites">Favorite Actions</string>
|
<string name="action_kind_favorites">Favorite Actions</string>
|
||||||
|
@ -62,6 +62,10 @@ final class KeyCodeDescriptionMapper {
|
|||||||
mKeyCodeMap.put(Constants.CODE_SHIFT, R.string.spoken_description_shift);
|
mKeyCodeMap.put(Constants.CODE_SHIFT, R.string.spoken_description_shift);
|
||||||
mKeyCodeMap.put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic);
|
mKeyCodeMap.put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic);
|
||||||
mKeyCodeMap.put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
|
mKeyCodeMap.put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
|
||||||
|
mKeyCodeMap.put(Constants.CODE_TO_NUMBER_LAYOUT, R.string.spoken_description_to_numeric);
|
||||||
|
mKeyCodeMap.put(Constants.CODE_TO_ALT_0_LAYOUT, R.string.spoken_description_to_alt_0);
|
||||||
|
mKeyCodeMap.put(Constants.CODE_TO_ALT_1_LAYOUT, R.string.spoken_description_to_alt_1);
|
||||||
|
mKeyCodeMap.put(Constants.CODE_TO_ALT_2_LAYOUT, R.string.spoken_description_to_alt_2);
|
||||||
mKeyCodeMap.put(Constants.CODE_TAB, R.string.spoken_description_tab);
|
mKeyCodeMap.put(Constants.CODE_TAB, R.string.spoken_description_tab);
|
||||||
mKeyCodeMap.put(Constants.CODE_LANGUAGE_SWITCH,
|
mKeyCodeMap.put(Constants.CODE_LANGUAGE_SWITCH,
|
||||||
R.string.spoken_description_language_switch);
|
R.string.spoken_description_language_switch);
|
||||||
|
@ -181,7 +181,16 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
|
|||||||
node.setFocusable(true);
|
node.setFocusable(true);
|
||||||
node.setScreenReaderFocusable(true);
|
node.setScreenReaderFocusable(true);
|
||||||
|
|
||||||
if(k.isActionKey() || k.getCode() == Constants.CODE_SWITCH_ALPHA_SYMBOL || k.getCode() == Constants.CODE_EMOJI || k.getCode() == Constants.CODE_SYMBOL_SHIFT || (k.getCode() >= Constants.CODE_ACTION_0 && k.getCode() <= Constants.CODE_ACTION_MAX)) {
|
if(k.isActionKey() ||
|
||||||
|
k.getCode() == Constants.CODE_SWITCH_ALPHA_SYMBOL ||
|
||||||
|
k.getCode() == Constants.CODE_EMOJI ||
|
||||||
|
k.getCode() == Constants.CODE_SYMBOL_SHIFT ||
|
||||||
|
k.getCode() == Constants.CODE_TO_ALT_0_LAYOUT ||
|
||||||
|
k.getCode() == Constants.CODE_TO_ALT_1_LAYOUT ||
|
||||||
|
k.getCode() == Constants.CODE_TO_ALT_2_LAYOUT ||
|
||||||
|
k.getCode() == Constants.CODE_TO_NUMBER_LAYOUT ||
|
||||||
|
(k.getCode() >= Constants.CODE_ACTION_0 && k.getCode() <= Constants.CODE_ACTION_MAX)
|
||||||
|
) {
|
||||||
node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
|
node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
|
||||||
node.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK);
|
node.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK);
|
||||||
node.setClickable(true);
|
node.setClickable(true);
|
||||||
|
@ -18,10 +18,8 @@ package org.futo.inputmethod.accessibility;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import android.view.MotionEvent;
|
|
||||||
|
|
||||||
import org.futo.inputmethod.keyboard.Key;
|
import org.futo.inputmethod.keyboard.Key;
|
||||||
import org.futo.inputmethod.keyboard.KeyDetector;
|
import org.futo.inputmethod.keyboard.KeyDetector;
|
||||||
@ -30,7 +28,6 @@ import org.futo.inputmethod.keyboard.KeyboardId;
|
|||||||
import org.futo.inputmethod.keyboard.MainKeyboardView;
|
import org.futo.inputmethod.keyboard.MainKeyboardView;
|
||||||
import org.futo.inputmethod.keyboard.PointerTracker;
|
import org.futo.inputmethod.keyboard.PointerTracker;
|
||||||
import org.futo.inputmethod.latin.R;
|
import org.futo.inputmethod.latin.R;
|
||||||
import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a delegate that can be registered in {@link MainKeyboardView} to enhance
|
* This class represents a delegate that can be registered in {@link MainKeyboardView} to enhance
|
||||||
@ -191,6 +188,9 @@ public final class MainKeyboardAccessibilityDelegate
|
|||||||
case KeyboardId.ELEMENT_PHONE_SYMBOLS:
|
case KeyboardId.ELEMENT_PHONE_SYMBOLS:
|
||||||
resId = R.string.spoken_description_mode_phone_shift;
|
resId = R.string.spoken_description_mode_phone_shift;
|
||||||
break;
|
break;
|
||||||
|
case KeyboardId.ELEMENT_NUMBER:
|
||||||
|
resId = R.string.spoken_description_mode_digits;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import android.text.TextUtils;
|
|||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
|
||||||
import org.futo.inputmethod.compat.EditorInfoCompatUtils;
|
import org.futo.inputmethod.compat.EditorInfoCompatUtils;
|
||||||
import org.futo.inputmethod.latin.RichInputMethodSubtype;
|
|
||||||
import org.futo.inputmethod.latin.settings.LongPressKeySettings;
|
import org.futo.inputmethod.latin.settings.LongPressKeySettings;
|
||||||
import org.futo.inputmethod.latin.utils.InputTypeUtils;
|
import org.futo.inputmethod.latin.utils.InputTypeUtils;
|
||||||
|
|
||||||
|
@ -140,40 +140,16 @@ public final class KeyboardSwitcher implements SwitchActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final KeyboardSizingCalculator sizingCalculator = new KeyboardSizingCalculator(mLatinIMELegacy.getInputMethodService());
|
final KeyboardSizingCalculator sizingCalculator = ((LatinIME)mLatinIMELegacy.getInputMethodService()).getSizingCalculator();
|
||||||
final ComputedKeyboardSize computedSize = sizingCalculator.calculate(layoutSetName, settingsValues.mIsNumberRowEnabled);
|
final ComputedKeyboardSize computedSize = sizingCalculator.calculate(layoutSetName, settingsValues.mIsNumberRowEnabled);
|
||||||
|
|
||||||
int keyboardWidth = 0;
|
|
||||||
int keyboardHeight = 0;
|
|
||||||
|
|
||||||
int splitLayoutWidth = 0;
|
|
||||||
|
|
||||||
Rect padding = new Rect();
|
|
||||||
|
|
||||||
Window window = mLatinIMELegacy.getInputMethodService().getWindow().getWindow();
|
|
||||||
|
|
||||||
if(computedSize instanceof SplitKeyboardSize) {
|
|
||||||
keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(window, res);
|
|
||||||
keyboardHeight = ((SplitKeyboardSize) computedSize).getHeight();
|
|
||||||
splitLayoutWidth = ((SplitKeyboardSize) computedSize).getSplitLayoutWidth();
|
|
||||||
padding = ((SplitKeyboardSize) computedSize).getPadding();
|
|
||||||
}else if(computedSize instanceof RegularKeyboardSize) {
|
|
||||||
keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(window, res);
|
|
||||||
keyboardHeight = ((RegularKeyboardSize) computedSize).getHeight();
|
|
||||||
padding = ((RegularKeyboardSize) computedSize).getPadding();
|
|
||||||
}
|
|
||||||
|
|
||||||
final KeyboardLayoutSetV2Params params = new KeyboardLayoutSetV2Params(
|
final KeyboardLayoutSetV2Params params = new KeyboardLayoutSetV2Params(
|
||||||
keyboardWidth,
|
computedSize,
|
||||||
keyboardHeight,
|
|
||||||
padding,
|
|
||||||
layoutSetName,
|
layoutSetName,
|
||||||
subtype.getLocale(),
|
subtype.getLocale(),
|
||||||
editorInfo == null ? new EditorInfo() : editorInfo,
|
editorInfo == null ? new EditorInfo() : editorInfo,
|
||||||
settingsValues.mIsNumberRowEnabled,
|
settingsValues.mIsNumberRowEnabled,
|
||||||
sizingCalculator.calculateGap(),
|
sizingCalculator.calculateGap(),
|
||||||
splitLayoutWidth != 0,
|
|
||||||
splitLayoutWidth,
|
|
||||||
settingsValues.mShowsActionKey ? settingsValues.mActionKeyId : null,
|
settingsValues.mShowsActionKey ? settingsValues.mActionKeyId : null,
|
||||||
LongPressKeySettings.load(mThemeContext)
|
LongPressKeySettings.load(mThemeContext)
|
||||||
);
|
);
|
||||||
@ -353,9 +329,7 @@ public final class KeyboardSwitcher implements SwitchActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isShowingMoreKeysPanel() {
|
public boolean isShowingMoreKeysPanel() {
|
||||||
if (isShowingEmojiPalettes()) {
|
if(mKeyboardView == null) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mKeyboardView.isShowingMoreKeysPanel();
|
return mKeyboardView.isShowingMoreKeysPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,16 +132,8 @@ public final class KeyPreviewChoreographer {
|
|||||||
final int keyPreviewPosition;
|
final int keyPreviewPosition;
|
||||||
int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
|
int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
|
||||||
+ CoordinateUtils.x(originCoords);
|
+ CoordinateUtils.x(originCoords);
|
||||||
if (previewX < 0) {
|
keyPreviewPosition = KeyPreviewView.POSITION_MIDDLE;
|
||||||
previewX = 0;
|
final boolean hasMoreKeys = !key.getMoreKeys().isEmpty();
|
||||||
keyPreviewPosition = KeyPreviewView.POSITION_LEFT;
|
|
||||||
} else if (previewX > keyboardViewWidth - previewWidth) {
|
|
||||||
previewX = keyboardViewWidth - previewWidth;
|
|
||||||
keyPreviewPosition = KeyPreviewView.POSITION_RIGHT;
|
|
||||||
} else {
|
|
||||||
keyPreviewPosition = KeyPreviewView.POSITION_MIDDLE;
|
|
||||||
}
|
|
||||||
final boolean hasMoreKeys = (key.getMoreKeys() != null);
|
|
||||||
keyPreviewView.setPreviewBackground(hasMoreKeys, keyPreviewPosition);
|
keyPreviewView.setPreviewBackground(hasMoreKeys, keyPreviewPosition);
|
||||||
// The key preview is placed vertically above the top edge of the parent key with an
|
// The key preview is placed vertically above the top edge of the parent key with an
|
||||||
// arbitrary offset.
|
// arbitrary offset.
|
||||||
|
@ -126,8 +126,8 @@ internal data class SavedKeyboardState(
|
|||||||
class KeyboardState(private val switchActions: SwitchActions) {
|
class KeyboardState(private val switchActions: SwitchActions) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "KeyboardState"
|
private const val TAG = "KeyboardState"
|
||||||
private const val DEBUG_EVENT = true
|
private const val DEBUG_EVENT = false
|
||||||
private const val DEBUG_INTERNAL_ACTION = true
|
private const val DEBUG_INTERNAL_ACTION = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private val shiftKeyState = ShiftKeyState("Shift")
|
private val shiftKeyState = ShiftKeyState("Shift")
|
||||||
|
@ -12,6 +12,7 @@ import android.os.Bundle
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.CompletionInfo
|
import android.view.inputmethod.CompletionInfo
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InlineSuggestionsRequest
|
import android.view.inputmethod.InlineSuggestionsRequest
|
||||||
@ -21,10 +22,14 @@ import android.view.inputmethod.InputMethodSubtype
|
|||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.key
|
import androidx.compose.runtime.key
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clipToBounds
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
|
import androidx.compose.ui.platform.ComposeView
|
||||||
|
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@ -75,28 +80,27 @@ import org.futo.inputmethod.latin.uix.theme.applyWindowColors
|
|||||||
import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme
|
import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme
|
||||||
import org.futo.inputmethod.latin.xlm.LanguageModelFacilitator
|
import org.futo.inputmethod.latin.xlm.LanguageModelFacilitator
|
||||||
import org.futo.inputmethod.updates.scheduleUpdateCheckingJob
|
import org.futo.inputmethod.updates.scheduleUpdateCheckingJob
|
||||||
|
import org.futo.inputmethod.v2keyboard.ComputedKeyboardSize
|
||||||
|
import org.futo.inputmethod.v2keyboard.FloatingKeyboardSize
|
||||||
|
import org.futo.inputmethod.v2keyboard.KeyboardSettings
|
||||||
import org.futo.inputmethod.v2keyboard.KeyboardSizeSettingKind
|
import org.futo.inputmethod.v2keyboard.KeyboardSizeSettingKind
|
||||||
import org.futo.inputmethod.v2keyboard.KeyboardSizeStateProvider
|
import org.futo.inputmethod.v2keyboard.KeyboardSizeStateProvider
|
||||||
|
import org.futo.inputmethod.v2keyboard.KeyboardSizingCalculator
|
||||||
|
import org.futo.inputmethod.v2keyboard.getHeight
|
||||||
|
|
||||||
private class UnlockedBroadcastReceiver(val onDeviceUnlocked: () -> Unit) : BroadcastReceiver() {
|
private class UnlockedBroadcastReceiver(val onDeviceUnlocked: () -> Unit) : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
println("Unlocked Broadcast Receiver: ${intent?.action}")
|
|
||||||
if (intent?.action == Intent.ACTION_USER_UNLOCKED) {
|
if (intent?.action == Intent.ACTION_USER_UNLOCKED) {
|
||||||
onDeviceUnlocked()
|
onDeviceUnlocked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner,
|
open class InputMethodServiceCompose : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
|
||||||
LatinIMELegacy.SuggestionStripController, DynamicThemeProviderOwner, FoldStateProvider,
|
|
||||||
KeyboardSizeStateProvider {
|
|
||||||
|
|
||||||
private lateinit var mLifecycleRegistry: LifecycleRegistry
|
private lateinit var mLifecycleRegistry: LifecycleRegistry
|
||||||
private lateinit var mViewModelStore: ViewModelStore
|
private lateinit var mViewModelStore: ViewModelStore
|
||||||
private lateinit var mSavedStateRegistryController: SavedStateRegistryController
|
private lateinit var mSavedStateRegistryController: SavedStateRegistryController
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun setOwners() {
|
fun setOwners() {
|
||||||
val decorView = window.window?.decorView
|
val decorView = window.window?.decorView
|
||||||
if (decorView?.findViewTreeLifecycleOwner() == null) {
|
if (decorView?.findViewTreeLifecycleOwner() == null) {
|
||||||
@ -110,11 +114,59 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
mLifecycleRegistry = LifecycleRegistry(this)
|
||||||
|
mLifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
|
||||||
|
|
||||||
|
mViewModelStore = ViewModelStore()
|
||||||
|
|
||||||
|
mSavedStateRegistryController = SavedStateRegistryController.create(this)
|
||||||
|
mSavedStateRegistryController.performRestore(null)
|
||||||
|
|
||||||
|
mLifecycleRegistry.currentState = Lifecycle.State.CREATED
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartInputView(editorInfo: EditorInfo?, restarting: Boolean) {
|
||||||
|
super.onStartInputView(editorInfo, restarting)
|
||||||
|
|
||||||
|
mLifecycleRegistry.currentState = Lifecycle.State.STARTED
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
mLifecycleRegistry.currentState = Lifecycle.State.DESTROYED
|
||||||
|
}
|
||||||
|
|
||||||
|
override val lifecycle: Lifecycle
|
||||||
|
get() = mLifecycleRegistry
|
||||||
|
override val savedStateRegistry: SavedStateRegistry
|
||||||
|
get() = mSavedStateRegistryController.savedStateRegistry
|
||||||
|
override val viewModelStore: ViewModelStore
|
||||||
|
get() = mViewModelStore
|
||||||
|
|
||||||
|
internal var composeView: ComposeView? = null
|
||||||
|
|
||||||
|
override fun onCreateInputView(): View =
|
||||||
|
ComposeView(this).apply {
|
||||||
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||||
|
setParentCompositionContext(null)
|
||||||
|
|
||||||
|
setOwners()
|
||||||
|
|
||||||
|
composeView = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LatinIME : InputMethodServiceCompose(), LatinIMELegacy.SuggestionStripController,
|
||||||
|
DynamicThemeProviderOwner, FoldStateProvider, KeyboardSizeStateProvider {
|
||||||
val latinIMELegacy = LatinIMELegacy(
|
val latinIMELegacy = LatinIMELegacy(
|
||||||
this as InputMethodService,
|
this as InputMethodService,
|
||||||
this as LatinIMELegacy.SuggestionStripController
|
this as LatinIMELegacy.SuggestionStripController
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
val inputLogic get() = latinIMELegacy.mInputLogic
|
val inputLogic get() = latinIMELegacy.mInputLogic
|
||||||
|
|
||||||
lateinit var languageModelFacilitator: LanguageModelFacilitator
|
lateinit var languageModelFacilitator: LanguageModelFacilitator
|
||||||
@ -122,6 +174,8 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
val uixManager = UixManager(this)
|
val uixManager = UixManager(this)
|
||||||
lateinit var suggestionBlacklist: SuggestionBlacklist
|
lateinit var suggestionBlacklist: SuggestionBlacklist
|
||||||
|
|
||||||
|
val sizingCalculator = KeyboardSizingCalculator(this, uixManager)
|
||||||
|
|
||||||
private var activeThemeOption: ThemeOption? = null
|
private var activeThemeOption: ThemeOption? = null
|
||||||
private var activeColorScheme = VoiceInputTheme.obtainColors(this)
|
private var activeColorScheme = VoiceInputTheme.obtainColors(this)
|
||||||
private var pendingRecreateKeyboard: Boolean = false
|
private var pendingRecreateKeyboard: Boolean = false
|
||||||
@ -130,6 +184,13 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
val colorScheme get() = activeColorScheme
|
val colorScheme get() = activeColorScheme
|
||||||
val keyboardColor get() = drawableProvider?.primaryKeyboardColor?.let { androidx.compose.ui.graphics.Color(it) } ?: colorScheme.surface
|
val keyboardColor get() = drawableProvider?.primaryKeyboardColor?.let { androidx.compose.ui.graphics.Color(it) } ?: colorScheme.surface
|
||||||
|
|
||||||
|
val size: MutableState<ComputedKeyboardSize?> = mutableStateOf(null)
|
||||||
|
private fun calculateSize(): ComputedKeyboardSize
|
||||||
|
= sizingCalculator.calculate(
|
||||||
|
latinIMELegacy.mKeyboardSwitcher.keyboard?.mId?.mKeyboardLayoutSetName ?: "qwerty",
|
||||||
|
latinIMELegacy.mKeyboardSwitcher.keyboard?.mId?.mNumberRow ?: false
|
||||||
|
)
|
||||||
|
|
||||||
private var drawableProvider: DynamicThemeProvider? = null
|
private var drawableProvider: DynamicThemeProvider? = null
|
||||||
|
|
||||||
private var lastEditorInfo: EditorInfo? = null
|
private var lastEditorInfo: EditorInfo? = null
|
||||||
@ -204,7 +265,26 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onSizeUpdated() {
|
||||||
|
val newSize = calculateSize()
|
||||||
|
val shouldInvalidateKeyboard = size.value?.let { oldSize ->
|
||||||
|
when {
|
||||||
|
oldSize is FloatingKeyboardSize && newSize is FloatingKeyboardSize -> {
|
||||||
|
oldSize.width != newSize.width || oldSize.height != newSize.height
|
||||||
|
}
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
} ?: true
|
||||||
|
|
||||||
|
size.value = newSize
|
||||||
|
|
||||||
|
if(shouldInvalidateKeyboard) {
|
||||||
|
invalidateKeyboard(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun invalidateKeyboard(refreshSettings: Boolean = false) {
|
fun invalidateKeyboard(refreshSettings: Boolean = false) {
|
||||||
|
size.value = calculateSize()
|
||||||
settingsRefreshRequired = settingsRefreshRequired || refreshSettings
|
settingsRefreshRequired = settingsRefreshRequired || refreshSettings
|
||||||
|
|
||||||
if(!uixManager.isMainKeyboardHidden) {
|
if(!uixManager.isMainKeyboardHidden) {
|
||||||
@ -256,16 +336,6 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
val filter = IntentFilter(Intent.ACTION_USER_UNLOCKED)
|
val filter = IntentFilter(Intent.ACTION_USER_UNLOCKED)
|
||||||
registerReceiver(unlockReceiver, filter)
|
registerReceiver(unlockReceiver, filter)
|
||||||
|
|
||||||
mLifecycleRegistry = LifecycleRegistry(this)
|
|
||||||
mLifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
|
|
||||||
|
|
||||||
mViewModelStore = ViewModelStore()
|
|
||||||
|
|
||||||
mSavedStateRegistryController = SavedStateRegistryController.create(this)
|
|
||||||
mSavedStateRegistryController.performRestore(null)
|
|
||||||
|
|
||||||
mLifecycleRegistry.currentState = Lifecycle.State.CREATED
|
|
||||||
|
|
||||||
suggestionBlacklist = SuggestionBlacklist(latinIMELegacy.mSettings, this, lifecycleScope)
|
suggestionBlacklist = SuggestionBlacklist(latinIMELegacy.mSettings, this, lifecycleScope)
|
||||||
|
|
||||||
Subtypes.addDefaultSubtypesIfNecessary(this)
|
Subtypes.addDefaultSubtypesIfNecessary(this)
|
||||||
@ -348,6 +418,21 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Listen to size changes
|
||||||
|
launchJob {
|
||||||
|
val prev: MutableMap<KeyboardSizeSettingKind, String?> =
|
||||||
|
KeyboardSizeSettingKind.entries.associateWith { null }.toMutableMap()
|
||||||
|
|
||||||
|
dataStore.data.collect { data ->
|
||||||
|
prev.keys.toList().forEach {
|
||||||
|
if(data[KeyboardSettings[it]!!.key] != prev[it]) {
|
||||||
|
prev[it] = data[KeyboardSettings[it]!!.key]
|
||||||
|
onSizeUpdated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uixManager.onCreate()
|
uixManager.onCreate()
|
||||||
|
|
||||||
Settings.getInstance().settingsChangedListeners.add { oldSettings, newSettings ->
|
Settings.getInstance().settingsChangedListeners.add { oldSettings, newSettings ->
|
||||||
@ -364,7 +449,6 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
unregisterReceiver(unlockReceiver)
|
unregisterReceiver(unlockReceiver)
|
||||||
|
|
||||||
stopJobs()
|
stopJobs()
|
||||||
mLifecycleRegistry.currentState = Lifecycle.State.DESTROYED
|
|
||||||
viewModelStore.clear()
|
viewModelStore.clear()
|
||||||
|
|
||||||
languageModelFacilitator.saveHistoryLog()
|
languageModelFacilitator.saveHistoryLog()
|
||||||
@ -381,6 +465,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
Log.w("LatinIME", "Configuration changed")
|
Log.w("LatinIME", "Configuration changed")
|
||||||
|
size.value = calculateSize()
|
||||||
latinIMELegacy.onConfigurationChanged(newConfig)
|
latinIMELegacy.onConfigurationChanged(newConfig)
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
}
|
}
|
||||||
@ -390,22 +475,22 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var legacyInputView: View? = null
|
private var legacyInputView: View? = null
|
||||||
private var touchableHeight: Int = 0
|
|
||||||
override fun onCreateInputView(): View {
|
override fun onCreateInputView(): View {
|
||||||
Log.w("LatinIME", "Create input view")
|
val composeView = super.onCreateInputView()
|
||||||
legacyInputView = latinIMELegacy.onCreateInputView()
|
|
||||||
|
|
||||||
val composeView = uixManager.createComposeView()
|
legacyInputView = latinIMELegacy.onCreateInputView()
|
||||||
latinIMELegacy.setComposeInputView(composeView)
|
latinIMELegacy.setComposeInputView(composeView)
|
||||||
|
|
||||||
|
uixManager.setContent()
|
||||||
|
|
||||||
return composeView
|
return composeView
|
||||||
}
|
}
|
||||||
|
|
||||||
private var inputViewHeight: Int = -1
|
private var inputViewHeight: Int = -1
|
||||||
|
|
||||||
// Both called by UixManager
|
|
||||||
fun updateTouchableHeight(to: Int) { touchableHeight = to }
|
|
||||||
fun getInputViewHeight(): Int = inputViewHeight
|
fun getInputViewHeight(): Int = inputViewHeight
|
||||||
|
fun getViewHeight(): Int = composeView?.height ?: resources.displayMetrics.heightPixels
|
||||||
|
fun getViewWidth(): Int = composeView?.width ?: resources.displayMetrics.widthPixels
|
||||||
|
|
||||||
private var isInputModal = false
|
private var isInputModal = false
|
||||||
fun setInputModal(to: Boolean) {
|
fun setInputModal(to: Boolean) {
|
||||||
@ -426,11 +511,11 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
key(legacyInputView) {
|
key(legacyInputView) {
|
||||||
AndroidView(factory = {
|
AndroidView(factory = {
|
||||||
legacyInputView!!
|
legacyInputView!!.also {
|
||||||
|
if(it.parent != null) (it.parent as ViewGroup).removeView(it)
|
||||||
|
}
|
||||||
}, modifier = modifier, onRelease = {
|
}, modifier = modifier, onRelease = {
|
||||||
val view = it as InputView
|
val view = it as InputView
|
||||||
view.deallocateMemory()
|
view.deallocateMemory()
|
||||||
@ -445,7 +530,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
legacyInputView = newView
|
legacyInputView = newView
|
||||||
|
|
||||||
uixManager.setContent()
|
uixManager.setContent()
|
||||||
uixManager.getComposeView()?.let {
|
composeView?.let {
|
||||||
latinIMELegacy.setComposeInputView(it)
|
latinIMELegacy.setComposeInputView(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,7 +540,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
override fun setInputView(view: View?) {
|
override fun setInputView(view: View?) {
|
||||||
super.setInputView(view)
|
super.setInputView(view)
|
||||||
|
|
||||||
uixManager.getComposeView()?.let {
|
composeView?.let {
|
||||||
latinIMELegacy.setComposeInputView(it)
|
latinIMELegacy.setComposeInputView(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,8 +558,6 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
|
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
|
||||||
mLifecycleRegistry.currentState = Lifecycle.State.STARTED
|
|
||||||
|
|
||||||
lastEditorInfo = info
|
lastEditorInfo = info
|
||||||
|
|
||||||
super.onStartInputView(info, restarting)
|
super.onStartInputView(info, restarting)
|
||||||
@ -561,36 +644,55 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onComputeInsets(outInsets: Insets?) {
|
override fun onComputeInsets(outInsets: Insets?) {
|
||||||
val composeView = uixManager.getComposeView()
|
|
||||||
|
|
||||||
// This method may be called before {@link #setInputView(View)}.
|
// This method may be called before {@link #setInputView(View)}.
|
||||||
if (legacyInputView == null || composeView == null) {
|
if (legacyInputView == null || composeView == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val inputHeight: Int = composeView.height
|
val viewHeight = composeView!!.height
|
||||||
if (latinIMELegacy.isImeSuppressedByHardwareKeyboard && !legacyInputView!!.isShown) {
|
val size = size.value ?: return
|
||||||
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
|
|
||||||
// no visual element will be shown on the screen.
|
|
||||||
latinIMELegacy.setInsets(outInsets!!.apply {
|
|
||||||
contentTopInsets = inputHeight
|
|
||||||
visibleTopInsets = inputHeight
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val visibleTopY = inputHeight - touchableHeight
|
|
||||||
|
|
||||||
val touchLeft = 0
|
|
||||||
val touchTop = if(isInputModal) { 0 } else { visibleTopY }
|
|
||||||
val touchRight = composeView.width
|
|
||||||
val touchBottom = inputHeight
|
|
||||||
|
|
||||||
latinIMELegacy.setInsets(outInsets!!.apply {
|
latinIMELegacy.setInsets(outInsets!!.apply {
|
||||||
touchableInsets = Insets.TOUCHABLE_INSETS_REGION;
|
when(size) {
|
||||||
touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom);
|
is FloatingKeyboardSize -> {
|
||||||
contentTopInsets = visibleTopY
|
val height = uixManager.touchableHeight
|
||||||
visibleTopInsets = visibleTopY
|
|
||||||
|
val left = size.bottomOrigin.first
|
||||||
|
val bottomYFromBottom = size.bottomOrigin.second
|
||||||
|
var bottom = viewHeight - bottomYFromBottom
|
||||||
|
var top = bottom - height
|
||||||
|
val right = left + size.width
|
||||||
|
|
||||||
|
if(top < 0) {
|
||||||
|
bottom -= top
|
||||||
|
top -= top
|
||||||
|
}
|
||||||
|
|
||||||
|
touchableInsets = Insets.TOUCHABLE_INSETS_REGION
|
||||||
|
touchableRegion.set(left, top, right, bottom)
|
||||||
|
contentTopInsets = viewHeight
|
||||||
|
visibleTopInsets = viewHeight
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
touchableInsets = Insets.TOUCHABLE_INSETS_CONTENT
|
||||||
|
|
||||||
|
val touchableHeight = uixManager.touchableHeight
|
||||||
|
val topInset = if(touchableHeight < 1 || touchableHeight >= viewHeight - 1) {
|
||||||
|
val actionBarHeight = sizingCalculator.calculateTotalActionBarHeightPx()
|
||||||
|
|
||||||
|
viewHeight - size.getHeight() - actionBarHeight
|
||||||
|
} else {
|
||||||
|
viewHeight - touchableHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
contentTopInsets = topInset
|
||||||
|
visibleTopInsets = topInset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isInputModal || latinIMELegacy.mKeyboardSwitcher?.isShowingMoreKeysPanel == true) {
|
||||||
|
touchableInsets = Insets.TOUCHABLE_INSETS_REGION
|
||||||
|
touchableRegion.set(0, 0, composeView!!.width, composeView!!.height)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,14 +839,6 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
return super.getCurrentInputConnection()
|
return super.getCurrentInputConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val lifecycle: Lifecycle
|
|
||||||
get() = mLifecycleRegistry
|
|
||||||
override val savedStateRegistry: SavedStateRegistry
|
|
||||||
get() = mSavedStateRegistryController.savedStateRegistry
|
|
||||||
override val viewModelStore: ViewModelStore
|
|
||||||
get() = mViewModelStore
|
|
||||||
|
|
||||||
|
|
||||||
private fun onDeviceUnlocked() {
|
private fun onDeviceUnlocked() {
|
||||||
Log.i("LatinIME", "DEVICE has UNLOCKED!!! Reloading settings...")
|
Log.i("LatinIME", "DEVICE has UNLOCKED!!! Reloading settings...")
|
||||||
// Every place that called getDefaultSharedPreferences now needs to be refreshed or call it again
|
// Every place that called getDefaultSharedPreferences now needs to be refreshed or call it again
|
||||||
|
@ -1617,10 +1617,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
// punctuation suggestions (if it's disabled).
|
// punctuation suggestions (if it's disabled).
|
||||||
@Override
|
@Override
|
||||||
public void setNeutralSuggestionStrip() {
|
public void setNeutralSuggestionStrip() {
|
||||||
final SettingsValues currentSettings = mSettings.getCurrent();
|
final SuggestedWords neutralSuggestions = SuggestedWords.getEmptyInstance();
|
||||||
final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled
|
|
||||||
? SuggestedWords.getEmptyInstance()
|
|
||||||
: currentSettings.mSpacingAndPunctuations.mSuggestPuncList;
|
|
||||||
setSuggestedWords(neutralSuggestions);
|
setSuggestedWords(neutralSuggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,6 +439,7 @@ public final class InputLogic {
|
|||||||
// The cursor has been moved : we now accept to perform recapitalization
|
// The cursor has been moved : we now accept to perform recapitalization
|
||||||
mRecapitalizeStatus.enable();
|
mRecapitalizeStatus.enable();
|
||||||
// We moved the cursor. If we are touching a word, we need to resume suggestion.
|
// We moved the cursor. If we are touching a word, we need to resume suggestion.
|
||||||
|
mIsAutoCorrectionIndicatorOn = false;
|
||||||
mLatinIMELegacy.mHandler.postResumeSuggestions(true /* shouldDelay */);
|
mLatinIMELegacy.mHandler.postResumeSuggestions(true /* shouldDelay */);
|
||||||
// Stop the last recapitalization, if started.
|
// Stop the last recapitalization, if started.
|
||||||
mRecapitalizeStatus.stop();
|
mRecapitalizeStatus.stop();
|
||||||
|
@ -164,8 +164,8 @@ public class SettingsValues {
|
|||||||
final String autoCorrectionThresholdRawValue = mAutoCorrectEnabled
|
final String autoCorrectionThresholdRawValue = mAutoCorrectEnabled
|
||||||
? res.getString(R.string.auto_correction_threshold_mode_index_modest)
|
? res.getString(R.string.auto_correction_threshold_mode_index_modest)
|
||||||
: res.getString(R.string.auto_correction_threshold_mode_index_off);
|
: res.getString(R.string.auto_correction_threshold_mode_index_off);
|
||||||
mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
|
|
||||||
mTransformerPredictionEnabled = readTransformerPredictionEnabled(prefs, res);
|
mTransformerPredictionEnabled = readTransformerPredictionEnabled(prefs, res);
|
||||||
|
mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res) || mTransformerPredictionEnabled;
|
||||||
mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
|
mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
|
||||||
mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
|
mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
|
||||||
mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true);
|
mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true);
|
||||||
|
@ -15,7 +15,9 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import org.futo.inputmethod.latin.LatinIME
|
import org.futo.inputmethod.latin.LatinIME
|
||||||
|
import org.futo.inputmethod.latin.SuggestionBlacklist
|
||||||
import org.futo.inputmethod.latin.uix.theme.ThemeOption
|
import org.futo.inputmethod.latin.uix.theme.ThemeOption
|
||||||
|
import org.futo.inputmethod.v2keyboard.KeyboardSizingCalculator
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
interface ActionInputTransaction {
|
interface ActionInputTransaction {
|
||||||
@ -66,8 +68,11 @@ interface KeyboardManagerForAction {
|
|||||||
fun activateAction(action: Action)
|
fun activateAction(action: Action)
|
||||||
fun showActionEditor()
|
fun showActionEditor()
|
||||||
|
|
||||||
|
fun getSuggestionBlacklist(): SuggestionBlacklist
|
||||||
fun getLatinIMEForDebug(): LatinIME
|
fun getLatinIMEForDebug(): LatinIME
|
||||||
fun isDeviceLocked(): Boolean
|
fun isDeviceLocked(): Boolean
|
||||||
|
|
||||||
|
fun getSizingCalculator(): KeyboardSizingCalculator
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActionWindow {
|
interface ActionWindow {
|
||||||
|
@ -72,6 +72,7 @@ import androidx.compose.ui.text.ExperimentalTextApi
|
|||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.drawText
|
import androidx.compose.ui.text.drawText
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.rememberTextMeasurer
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
@ -87,6 +88,7 @@ import org.futo.inputmethod.latin.SuggestedWords
|
|||||||
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
|
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
|
||||||
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_EMOJI_SUGGESTION
|
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_EMOJI_SUGGESTION
|
||||||
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_TYPED
|
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_TYPED
|
||||||
|
import org.futo.inputmethod.latin.SuggestionBlacklist
|
||||||
import org.futo.inputmethod.latin.common.Constants
|
import org.futo.inputmethod.latin.common.Constants
|
||||||
import org.futo.inputmethod.latin.suggestions.SuggestionStripViewListener
|
import org.futo.inputmethod.latin.suggestions.SuggestionStripViewListener
|
||||||
import org.futo.inputmethod.latin.uix.actions.FavoriteActions
|
import org.futo.inputmethod.latin.uix.actions.FavoriteActions
|
||||||
@ -98,7 +100,6 @@ import org.futo.inputmethod.latin.uix.settings.useDataStoreValue
|
|||||||
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
|
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
|
||||||
import org.futo.inputmethod.latin.uix.theme.Typography
|
import org.futo.inputmethod.latin.uix.theme.Typography
|
||||||
import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
|
import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
|
||||||
import java.lang.Integer.min
|
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -220,17 +221,9 @@ fun AutoFitText(
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RowScope.SuggestionItem(words: SuggestedWords, idx: Int, isPrimary: Boolean, onClick: () -> Unit, onLongClick: () -> Unit) {
|
fun RowScope.SuggestionItem(words: SuggestedWords, idx: Int, isPrimary: Boolean, onClick: () -> Unit, onLongClick: () -> Unit) {
|
||||||
val word = try {
|
val wordInfo = words.getInfoOrNull(idx)
|
||||||
words.getWord(idx)
|
val isVerbatim = wordInfo?.kind == KIND_TYPED
|
||||||
} catch(e: IndexOutOfBoundsException) {
|
val word = wordInfo?.mWord
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val wordInfo = try {
|
|
||||||
words.getInfo(idx)
|
|
||||||
} catch(e: IndexOutOfBoundsException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val actualIsPrimary = isPrimary && (words.mWillAutoCorrect || ((wordInfo?.isExactMatch) == true))
|
val actualIsPrimary = isPrimary && (words.mWillAutoCorrect || ((wordInfo?.isExactMatch) == true))
|
||||||
|
|
||||||
@ -277,9 +270,12 @@ fun RowScope.SuggestionItem(words: SuggestedWords, idx: Int, isPrimary: Boolean,
|
|||||||
) {
|
) {
|
||||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
|
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
|
||||||
if (word != null) {
|
if (word != null) {
|
||||||
AutoFitText(word, style = textStyle, modifier = textModifier
|
val modifier = textModifier.align(Center).padding(2.dp)
|
||||||
.align(Center)
|
if(isVerbatim) {
|
||||||
.padding(2.dp))
|
AutoFitText('"' + word + '"', style = textStyle.copy(fontStyle = FontStyle.Italic), modifier = modifier)
|
||||||
|
} else {
|
||||||
|
AutoFitText(word, style = textStyle, modifier = modifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,87 +292,137 @@ fun RowScope.SuggestionItem(words: SuggestedWords, idx: Int, isPrimary: Boolean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class SuggestionLayout(
|
||||||
|
/** Set to the word to be autocorrected to */
|
||||||
|
val autocorrectMatch: SuggestedWordInfo?,
|
||||||
|
|
||||||
// Show the most probable in the middle, then left, then right
|
/** Other words, sorted by likelihood */
|
||||||
val ORDER_OF_SUGGESTIONS = listOf(1, 0, 2)
|
val sortedMatches: List<SuggestedWordInfo>,
|
||||||
|
|
||||||
|
/** Emoji suggestions if they are to be shown */
|
||||||
|
val emojiMatches: List<SuggestedWordInfo>,
|
||||||
|
|
||||||
|
/** The exact word the user typed */
|
||||||
|
val verbatimWord: SuggestedWordInfo?,
|
||||||
|
|
||||||
|
/** Set to true if the best match is so unlikely that we should show verbatim instead */
|
||||||
|
val areSuggestionsClueless: Boolean,
|
||||||
|
|
||||||
|
/** Set to true if this is a gesture update, and we should only show one suggestion */
|
||||||
|
val isGestureBatch: Boolean,
|
||||||
|
|
||||||
|
val presentableSuggestions: List<SuggestedWordInfo>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun SuggestedWords.getInfoOrNull(idx: Int): SuggestedWordInfo? = try {
|
||||||
|
getInfo(idx)
|
||||||
|
} catch(e: IndexOutOfBoundsException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeSuggestionLayout(words: SuggestedWords, blacklist: SuggestionBlacklist): SuggestionLayout {
|
||||||
|
val typedWord = words.getInfoOrNull(SuggestedWords.INDEX_OF_TYPED_WORD)?.let {
|
||||||
|
if(it.kind == KIND_TYPED) { it } else { null }
|
||||||
|
}?.let {
|
||||||
|
if(blacklist.isSuggestedWordOk(it)) {
|
||||||
|
it
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val autocorrectMatch = words.getInfoOrNull(SuggestedWords.INDEX_OF_AUTO_CORRECTION)?.let {
|
||||||
|
if(words.mWillAutoCorrect) { it } else { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
// We actually have to avoid sorting these because they are provided sorted in an important order
|
||||||
|
|
||||||
|
val emojiMatches = words.mSuggestedWordInfoList.filter {
|
||||||
|
it.kind == KIND_EMOJI_SUGGESTION
|
||||||
|
}
|
||||||
|
|
||||||
|
val sortedMatches = words.mSuggestedWordInfoList.filter {
|
||||||
|
it != typedWord && it.kind != KIND_TYPED && it != autocorrectMatch && !emojiMatches.contains(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val areSuggestionsClueless = (autocorrectMatch ?: sortedMatches.getOrNull(0))?.let {
|
||||||
|
it.mOriginatesFromTransformerLM && it.mScore < -50
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
val isGestureBatch = words.mInputStyle == SuggestedWords.INPUT_STYLE_UPDATE_BATCH
|
||||||
|
|
||||||
|
val presentableSuggestions = (
|
||||||
|
listOf(
|
||||||
|
typedWord,
|
||||||
|
autocorrectMatch,
|
||||||
|
) + sortedMatches
|
||||||
|
).filterNotNull()
|
||||||
|
|
||||||
|
return SuggestionLayout(
|
||||||
|
autocorrectMatch = autocorrectMatch,
|
||||||
|
sortedMatches = sortedMatches,
|
||||||
|
emojiMatches = emojiMatches,
|
||||||
|
verbatimWord = typedWord,
|
||||||
|
areSuggestionsClueless = areSuggestionsClueless,
|
||||||
|
isGestureBatch = isGestureBatch,
|
||||||
|
presentableSuggestions = presentableSuggestions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RowScope.SuggestionItems(words: SuggestedWords, onClick: (i: Int) -> Unit, onLongClick: (i: Int) -> Unit) {
|
fun RowScope.SuggestionItems(words: SuggestedWords, onClick: (i: Int) -> Unit, onLongClick: (i: Int) -> Unit) {
|
||||||
val maxSuggestions = min(ORDER_OF_SUGGESTIONS.size, words.size())
|
val layout = makeSuggestionLayout(words, LocalManager.current.getSuggestionBlacklist())
|
||||||
|
|
||||||
if(maxSuggestions == 0) {
|
val suggestionItem = @Composable { suggestion: SuggestedWordInfo? ->
|
||||||
Spacer(modifier = Modifier.weight(1.0f))
|
if(suggestion != null) {
|
||||||
return
|
val idx = words.indexOf(suggestion)
|
||||||
}
|
SuggestionItem(
|
||||||
|
words,
|
||||||
if(maxSuggestions == 1 || words.mInputStyle == SuggestedWords.INPUT_STYLE_UPDATE_BATCH) {
|
idx,
|
||||||
SuggestionItem(
|
isPrimary = idx == SuggestedWords.INDEX_OF_AUTO_CORRECTION,
|
||||||
words,
|
onClick = { onClick(idx) },
|
||||||
0,
|
onLongClick = { onLongClick(idx) }
|
||||||
isPrimary = true,
|
)
|
||||||
onClick = { onClick(0) },
|
} else {
|
||||||
onLongClick = { onLongClick(0) }
|
Spacer(Modifier.weight(1.0f))
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
} else if(words.mInputStyle == SuggestedWords.INPUT_STYLE_TAIL_BATCH && maxSuggestions > 1) {
|
|
||||||
//words.mSuggestedWordInfoList.removeAt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var offset = 0
|
|
||||||
|
|
||||||
try {
|
|
||||||
val info = words.getInfo(0)
|
|
||||||
if (info.kind == KIND_TYPED && !info.isExactMatch && !info.isExactMatchWithIntentionalOmission) {
|
|
||||||
offset = 1
|
|
||||||
}
|
}
|
||||||
} catch(_: IndexOutOfBoundsException) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for "clueless" suggestions, and display typed word in center if so
|
println(layout)
|
||||||
try {
|
when {
|
||||||
if(offset == 1) {
|
layout.isGestureBatch ||
|
||||||
val info = words.getInfo(1)
|
layout.presentableSuggestions.size <= 1 -> suggestionItem(layout.presentableSuggestions.firstOrNull())
|
||||||
if(info.mOriginatesFromTransformerLM && info.mScore < -50) {
|
|
||||||
offset = 0;
|
layout.autocorrectMatch != null -> {
|
||||||
|
var supplementalSuggestionIndex = 0
|
||||||
|
if(layout.emojiMatches.isEmpty()) {
|
||||||
|
suggestionItem(layout.sortedMatches.getOrNull(supplementalSuggestionIndex++))
|
||||||
|
} else {
|
||||||
|
suggestionItem(layout.emojiMatches[0])
|
||||||
|
}
|
||||||
|
SuggestionSeparator()
|
||||||
|
suggestionItem(layout.autocorrectMatch)
|
||||||
|
SuggestionSeparator()
|
||||||
|
|
||||||
|
if(layout.verbatimWord != null && layout.verbatimWord.mWord != layout.autocorrectMatch.mWord) {
|
||||||
|
suggestionItem(layout.verbatimWord)
|
||||||
|
} else {
|
||||||
|
suggestionItem(layout.sortedMatches.getOrNull(supplementalSuggestionIndex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(_: IndexOutOfBoundsException) {
|
|
||||||
|
|
||||||
}
|
else -> {
|
||||||
|
var supplementalSuggestionIndex = 1
|
||||||
|
if(layout.emojiMatches.isEmpty()) {
|
||||||
val suggestionOrder = mutableListOf(
|
suggestionItem(layout.sortedMatches.getOrNull(supplementalSuggestionIndex++))
|
||||||
ORDER_OF_SUGGESTIONS[0] + offset,
|
} else {
|
||||||
ORDER_OF_SUGGESTIONS[1] + offset,
|
suggestionItem(layout.emojiMatches[0])
|
||||||
if(offset == 1) { 0 - offset } else { ORDER_OF_SUGGESTIONS[2] } + offset,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Find emoji
|
|
||||||
try {
|
|
||||||
for(i in 0 until words.size()) {
|
|
||||||
val info = words.getInfo(i)
|
|
||||||
if(info.mKindAndFlags == KIND_EMOJI_SUGGESTION && i > 2) {
|
|
||||||
suggestionOrder[0] = i
|
|
||||||
}
|
}
|
||||||
|
SuggestionSeparator()
|
||||||
|
suggestionItem(layout.sortedMatches.getOrNull(0))
|
||||||
|
SuggestionSeparator()
|
||||||
|
suggestionItem(layout.sortedMatches.getOrNull(supplementalSuggestionIndex))
|
||||||
}
|
}
|
||||||
} catch(_: IndexOutOfBoundsException) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (i in 0 until maxSuggestions) {
|
|
||||||
SuggestionItem(
|
|
||||||
words,
|
|
||||||
suggestionOrder[i],
|
|
||||||
isPrimary = i == (maxSuggestions / 2),
|
|
||||||
onClick = { onClick(suggestionOrder[i]) },
|
|
||||||
onLongClick = { onLongClick(suggestionOrder[i]) }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (i < maxSuggestions - 1) SuggestionSeparator()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.scale
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
@ -30,6 +29,7 @@ import org.futo.inputmethod.latin.R
|
|||||||
import org.futo.inputmethod.v2keyboard.KeyboardLayoutSetV2
|
import org.futo.inputmethod.v2keyboard.KeyboardLayoutSetV2
|
||||||
import org.futo.inputmethod.v2keyboard.KeyboardLayoutSetV2Params
|
import org.futo.inputmethod.v2keyboard.KeyboardLayoutSetV2Params
|
||||||
import org.futo.inputmethod.v2keyboard.LayoutManager
|
import org.futo.inputmethod.v2keyboard.LayoutManager
|
||||||
|
import org.futo.inputmethod.v2keyboard.RegularKeyboardSize
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -76,23 +76,8 @@ fun KeyboardLayoutPreview(id: String, width: Dp = 172.dp, locale: Locale? = null
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val configuration = LocalConfiguration.current
|
val widthPx: Int = (320.0 * context.resources.displayMetrics.density).roundToInt()
|
||||||
val isLandscape = false//configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
val heightPx: Int = (200.0 * context.resources.displayMetrics.density).roundToInt()
|
||||||
|
|
||||||
val widthPx: Int
|
|
||||||
val heightPx: Int
|
|
||||||
|
|
||||||
when {
|
|
||||||
isLandscape -> {
|
|
||||||
widthPx = (500.0 * context.resources.displayMetrics.density).roundToInt()
|
|
||||||
heightPx = (180.0 * context.resources.displayMetrics.density).roundToInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
widthPx = (320.0 * context.resources.displayMetrics.density).roundToInt()
|
|
||||||
heightPx = (200.0 * context.resources.displayMetrics.density).roundToInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val keyboard = remember { mutableStateOf<Keyboard?>(null) }
|
val keyboard = remember { mutableStateOf<Keyboard?>(null) }
|
||||||
|
|
||||||
@ -107,16 +92,12 @@ fun KeyboardLayoutPreview(id: String, width: Dp = 172.dp, locale: Locale? = null
|
|||||||
val layoutSet = KeyboardLayoutSetV2(
|
val layoutSet = KeyboardLayoutSetV2(
|
||||||
context,
|
context,
|
||||||
KeyboardLayoutSetV2Params(
|
KeyboardLayoutSetV2Params(
|
||||||
width = widthPx,
|
computedSize = RegularKeyboardSize(width = widthPx, height = heightPx, padding = Rect()),
|
||||||
height = heightPx,
|
|
||||||
padding = Rect(),
|
|
||||||
gap = 4.0f,
|
gap = 4.0f,
|
||||||
keyboardLayoutSet = id,
|
keyboardLayoutSet = id,
|
||||||
locale = loc ?: Locale.ENGLISH,
|
locale = loc ?: Locale.ENGLISH,
|
||||||
editorInfo = editorInfo,
|
editorInfo = editorInfo,
|
||||||
numberRow = numberRow,
|
numberRow = numberRow,
|
||||||
useSplitLayout = isLandscape,
|
|
||||||
splitLayoutWidth = widthPx * 2 / 3,
|
|
||||||
bottomActionKey = null
|
bottomActionKey = null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
130
java/src/org/futo/inputmethod/latin/uix/ResizerRect.kt
Normal file
130
java/src/org/futo/inputmethod/latin/uix/ResizerRect.kt
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package org.futo.inputmethod.latin.uix
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.gestures.detectDragGestures
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.toSize
|
||||||
|
|
||||||
|
|
||||||
|
enum class CurrentDraggingTarget {
|
||||||
|
TopLeft,
|
||||||
|
TopRight,
|
||||||
|
BottomLeft,
|
||||||
|
BottomRight,
|
||||||
|
Center
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CurrentDraggingTarget.computeOffset(size: Size): Offset = when(this) {
|
||||||
|
CurrentDraggingTarget.TopLeft -> Offset(0.0f, 0.0f)
|
||||||
|
CurrentDraggingTarget.TopRight -> Offset(size.width, 0.0f)
|
||||||
|
CurrentDraggingTarget.BottomLeft -> Offset(0.0f, size.height)
|
||||||
|
CurrentDraggingTarget.BottomRight -> Offset(size.width, size.height)
|
||||||
|
CurrentDraggingTarget.Center -> Offset(size.width * 0.5f, size.height * 0.5f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CurrentDraggingTarget.computeOffset(size: IntSize): Offset =
|
||||||
|
computeOffset(size.toSize())
|
||||||
|
|
||||||
|
private fun CurrentDraggingTarget.dragDelta(offset: Offset): DragDelta = when(this) {
|
||||||
|
CurrentDraggingTarget.TopLeft -> DragDelta(left = offset.x, top = offset.y)
|
||||||
|
CurrentDraggingTarget.TopRight -> DragDelta(right = offset.x, top = offset.y)
|
||||||
|
CurrentDraggingTarget.BottomLeft -> DragDelta(left = offset.x, bottom = offset.y)
|
||||||
|
CurrentDraggingTarget.BottomRight -> DragDelta(right = offset.x, bottom = offset.y)
|
||||||
|
CurrentDraggingTarget.Center -> DragDelta(
|
||||||
|
left = offset.x,
|
||||||
|
right = offset.x,
|
||||||
|
top = offset.y,
|
||||||
|
bottom = offset.y
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DragDelta(
|
||||||
|
val left: Float = 0.0f,
|
||||||
|
val top: Float = 0.0f,
|
||||||
|
val right: Float = 0.0f,
|
||||||
|
val bottom: Float = 0.0f
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BoxScope.ResizerRect(onDragged: (DragDelta) -> Boolean, showResetApply: Boolean, onApply: () -> Unit, onReset: () -> Unit) {
|
||||||
|
val shape = RectangleShape
|
||||||
|
|
||||||
|
val draggingState = remember { mutableStateOf<CurrentDraggingTarget?>(null) }
|
||||||
|
val wasAccepted = remember { mutableStateOf(true) }
|
||||||
|
|
||||||
|
Box(Modifier
|
||||||
|
.matchParentSize()
|
||||||
|
.background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f), shape)
|
||||||
|
.border(3.dp, MaterialTheme.colorScheme.primary, shape)
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectDragGestures(
|
||||||
|
onDragStart = { offset ->
|
||||||
|
draggingState.value = CurrentDraggingTarget.entries.minBy {
|
||||||
|
offset.minus(it.computeOffset(size)).getDistanceSquared()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDrag = { _, amount ->
|
||||||
|
draggingState.value?.let {
|
||||||
|
wasAccepted.value = onDragged(it.dragDelta(amount))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
draggingState.value = null
|
||||||
|
wasAccepted.value = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
|
val primaryInverseColor = MaterialTheme.colorScheme.inversePrimary
|
||||||
|
val errorColor = MaterialTheme.colorScheme.error
|
||||||
|
val radius = with(LocalDensity.current) { 24.dp.toPx() }
|
||||||
|
|
||||||
|
Canvas(Modifier.matchParentSize(), onDraw = {
|
||||||
|
CurrentDraggingTarget.entries.forEach {
|
||||||
|
drawCircle(
|
||||||
|
color = if (!wasAccepted.value) {
|
||||||
|
errorColor
|
||||||
|
} else if (draggingState.value == it) {
|
||||||
|
primaryInverseColor
|
||||||
|
} else {
|
||||||
|
primaryColor
|
||||||
|
},
|
||||||
|
radius = radius,
|
||||||
|
center = it.computeOffset(size)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (showResetApply) {
|
||||||
|
Row(Modifier.align(Alignment.BottomCenter).padding(16.dp)) {
|
||||||
|
TextButton({ onReset() }) { Text("Reset") }
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
TextButton({ onApply() }) { Text("Apply") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import android.app.Activity
|
|||||||
import android.content.ClipDescription
|
import android.content.ClipDescription
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Rect
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.VibrationEffect
|
import android.os.VibrationEffect
|
||||||
@ -23,22 +24,33 @@ import androidx.compose.animation.fadeOut
|
|||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.detectDragGestures
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.absoluteOffset
|
||||||
|
import androidx.compose.foundation.layout.absolutePadding
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.requiredWidth
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Menu
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@ -46,17 +58,24 @@ import androidx.compose.runtime.MutableState
|
|||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.staticCompositionLocalOf
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
@ -75,6 +94,7 @@ import org.futo.inputmethod.latin.LatinIME
|
|||||||
import org.futo.inputmethod.latin.R
|
import org.futo.inputmethod.latin.R
|
||||||
import org.futo.inputmethod.latin.SuggestedWords
|
import org.futo.inputmethod.latin.SuggestedWords
|
||||||
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
|
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
|
||||||
|
import org.futo.inputmethod.latin.SuggestionBlacklist
|
||||||
import org.futo.inputmethod.latin.common.Constants
|
import org.futo.inputmethod.latin.common.Constants
|
||||||
import org.futo.inputmethod.latin.inputlogic.InputLogic
|
import org.futo.inputmethod.latin.inputlogic.InputLogic
|
||||||
import org.futo.inputmethod.latin.suggestions.SuggestionStripViewListener
|
import org.futo.inputmethod.latin.suggestions.SuggestionStripViewListener
|
||||||
@ -96,6 +116,15 @@ import org.futo.inputmethod.updates.deferManualUpdate
|
|||||||
import org.futo.inputmethod.updates.isManualUpdateTimeExpired
|
import org.futo.inputmethod.updates.isManualUpdateTimeExpired
|
||||||
import org.futo.inputmethod.updates.openManualUpdateCheck
|
import org.futo.inputmethod.updates.openManualUpdateCheck
|
||||||
import org.futo.inputmethod.updates.retrieveSavedLastUpdateCheckResult
|
import org.futo.inputmethod.updates.retrieveSavedLastUpdateCheckResult
|
||||||
|
import org.futo.inputmethod.v2keyboard.ComputedKeyboardSize
|
||||||
|
import org.futo.inputmethod.v2keyboard.FloatingKeyboardSize
|
||||||
|
import org.futo.inputmethod.v2keyboard.KeyboardSizingCalculator
|
||||||
|
import org.futo.inputmethod.v2keyboard.OneHandedDirection
|
||||||
|
import org.futo.inputmethod.v2keyboard.OneHandedKeyboardSize
|
||||||
|
import org.futo.inputmethod.v2keyboard.RegularKeyboardSize
|
||||||
|
import org.futo.inputmethod.v2keyboard.SplitKeyboardSize
|
||||||
|
import org.futo.inputmethod.v2keyboard.getPadding
|
||||||
|
import org.futo.inputmethod.v2keyboard.getWidth
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
val LocalManager = staticCompositionLocalOf<KeyboardManagerForAction> {
|
val LocalManager = staticCompositionLocalOf<KeyboardManagerForAction> {
|
||||||
@ -279,7 +308,7 @@ class UixActionKeyboardManager(val uixManager: UixManager, val latinIME: LatinIM
|
|||||||
override fun announce(s: String) {
|
override fun announce(s: String) {
|
||||||
AccessibilityUtils.init(getContext())
|
AccessibilityUtils.init(getContext())
|
||||||
if(AccessibilityUtils.getInstance().isAccessibilityEnabled) {
|
if(AccessibilityUtils.getInstance().isAccessibilityEnabled) {
|
||||||
AccessibilityUtils.getInstance().announceForAccessibility(uixManager.getComposeView(), s)
|
AccessibilityUtils.getInstance().announceForAccessibility(uixManager.composeView, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +324,12 @@ class UixActionKeyboardManager(val uixManager: UixManager, val latinIME: LatinIM
|
|||||||
return getContext().isDeviceLocked
|
return getContext().isDeviceLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getSizingCalculator(): KeyboardSizingCalculator =
|
||||||
|
latinIME.sizingCalculator
|
||||||
|
|
||||||
override fun getLatinIMEForDebug(): LatinIME = latinIME
|
override fun getLatinIMEForDebug(): LatinIME = latinIME
|
||||||
|
|
||||||
|
override fun getSuggestionBlacklist(): SuggestionBlacklist = latinIME.suggestionBlacklist
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ActiveDialogRequest(
|
data class ActiveDialogRequest(
|
||||||
@ -305,11 +339,12 @@ data class ActiveDialogRequest(
|
|||||||
)
|
)
|
||||||
|
|
||||||
class UixManager(private val latinIME: LatinIME) {
|
class UixManager(private val latinIME: LatinIME) {
|
||||||
|
internal val composeView: ComposeView?
|
||||||
|
get() = latinIME.composeView
|
||||||
|
|
||||||
private var shouldShowSuggestionStrip: Boolean = true
|
private var shouldShowSuggestionStrip: Boolean = true
|
||||||
private var suggestedWords: SuggestedWords? = null
|
private var suggestedWords: SuggestedWords? = null
|
||||||
|
|
||||||
private var composeView: ComposeView? = null
|
|
||||||
|
|
||||||
private var currWindowAction: Action? = null
|
private var currWindowAction: Action? = null
|
||||||
private var persistentStates: HashMap<Action, PersistentActionState?> = hashMapOf()
|
private var persistentStates: HashMap<Action, PersistentActionState?> = hashMapOf()
|
||||||
|
|
||||||
@ -327,6 +362,9 @@ class UixManager(private val latinIME: LatinIME) {
|
|||||||
latinIME.deferSetSetting(ActionBarExpanded, isActionsExpanded.value)
|
latinIME.deferSetSetting(ActionBarExpanded, isActionsExpanded.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val actionsExpanded: Boolean
|
||||||
|
get() = isActionsExpanded.value
|
||||||
|
|
||||||
private var isShowingActionEditor = mutableStateOf(false)
|
private var isShowingActionEditor = mutableStateOf(false)
|
||||||
fun showActionEditor() {
|
fun showActionEditor() {
|
||||||
isShowingActionEditor.value = true
|
isShowingActionEditor.value = true
|
||||||
@ -337,6 +375,12 @@ class UixManager(private val latinIME: LatinIME) {
|
|||||||
var isInputOverridden = mutableStateOf(false)
|
var isInputOverridden = mutableStateOf(false)
|
||||||
|
|
||||||
var currWindowActionWindow: ActionWindow? = null
|
var currWindowActionWindow: ActionWindow? = null
|
||||||
|
val isActionWindowDocked: Boolean
|
||||||
|
get() = currWindowActionWindow != null
|
||||||
|
|
||||||
|
private var measuredTouchableHeight = 0
|
||||||
|
val touchableHeight: Int
|
||||||
|
get() = measuredTouchableHeight
|
||||||
|
|
||||||
val isMainKeyboardHidden get() = mainKeyboardHidden
|
val isMainKeyboardHidden get() = mainKeyboardHidden
|
||||||
|
|
||||||
@ -626,46 +670,249 @@ class UixManager(private val latinIME: LatinIME) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setContent() {
|
@Composable
|
||||||
composeView?.setContent {
|
private fun OffsetPositioner(offset: Offset, content: @Composable () -> Unit) {
|
||||||
UixThemeWrapper(latinIME.colorScheme) {
|
Column(modifier = Modifier.fillMaxHeight().absoluteOffset { IntOffset(offset.x.toInt(), 0) }) {
|
||||||
DataStoreCacheProvider {
|
Spacer(Modifier.weight(1.0f))
|
||||||
CompositionLocalProvider(LocalManager provides keyboardManagerForAction) {
|
content()
|
||||||
CompositionLocalProvider(LocalThemeProvider provides latinIME.getDrawableProvider()) {
|
Spacer(Modifier.height(with(LocalDensity.current) { offset.y.toDp() }))
|
||||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
}
|
||||||
CompositionLocalProvider(LocalFoldingState provides foldingOptions.value) {
|
}
|
||||||
InputDarkener(isInputOverridden.value || isShowingActionEditor.value) {
|
|
||||||
closeActionWindow()
|
|
||||||
isShowingActionEditor.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
@Composable
|
||||||
Spacer(modifier = Modifier.weight(1.0f))
|
private fun KeyboardSurface(
|
||||||
Surface(modifier = Modifier.onSizeChanged {
|
requiredWidthPx: Int,
|
||||||
latinIME.updateTouchableHeight(it.height)
|
backgroundColor: Color,
|
||||||
}, color = latinIME.keyboardColor) {
|
modifier: Modifier = Modifier,
|
||||||
Box {
|
shape: Shape = RectangleShape,
|
||||||
Column {
|
padding: Rect = Rect(),
|
||||||
when {
|
content: @Composable BoxScope.() -> Unit
|
||||||
currWindowActionWindow != null -> ActionViewWithHeader(
|
) = with(LocalDensity.current) {
|
||||||
currWindowActionWindow!!
|
Box(modifier
|
||||||
)
|
.onSizeChanged { measuredTouchableHeight = it.height}
|
||||||
|
.background(backgroundColor, shape)
|
||||||
|
.requiredWidth(requiredWidthPx.toDp())
|
||||||
|
.absolutePadding(
|
||||||
|
left = padding.left.toDp(),
|
||||||
|
top = padding.top.toDp(),
|
||||||
|
right = padding.right.toDp(),
|
||||||
|
bottom = padding.bottom.toDp(),
|
||||||
|
)
|
||||||
|
.clipToBounds()
|
||||||
|
) {
|
||||||
|
CompositionLocalProvider(LocalContentColor provides contentColorFor(backgroundColor)) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> MainKeyboardViewWithActionBar()
|
@Composable
|
||||||
}
|
private fun FloatingKeyboardContents(
|
||||||
|
pointerInputKey: Any?,
|
||||||
|
onDragged: (Offset) -> Unit,
|
||||||
|
onDragEnd: () -> Unit,
|
||||||
|
onResizerOpen: () -> Unit,
|
||||||
|
content: @Composable BoxScope.(actionBarGap: Dp) -> Unit
|
||||||
|
) {
|
||||||
|
// Content
|
||||||
|
Box(Modifier.fillMaxWidth()) {
|
||||||
|
content(4.dp)
|
||||||
|
}
|
||||||
|
|
||||||
latinIME.LegacyKeyboardView(hidden = isMainKeyboardHidden)
|
// Bottom drag bar
|
||||||
}
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
Box(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(20.dp)
|
||||||
|
.pointerInput(pointerInputKey) {
|
||||||
|
detectDragGestures(
|
||||||
|
onDrag = { _, dragAmount -> onDragged(dragAmount)},
|
||||||
|
onDragEnd = { onDragEnd() })
|
||||||
|
}) {
|
||||||
|
|
||||||
ForgetWordDialog()
|
IconButton(onClick = {
|
||||||
}
|
onResizerOpen()
|
||||||
|
}, Modifier.align(Alignment.CenterEnd)) {
|
||||||
|
Icon(Icons.Default.Menu, contentDescription = "resize")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
Box(
|
||||||
|
modifier = Modifier.fillMaxWidth(0.6f).height(4.dp)
|
||||||
|
.align(Alignment.TopCenter).background(
|
||||||
|
MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f),
|
||||||
|
RoundedCornerShape(100)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
@Composable
|
||||||
|
private fun FloatingKeyboardWindow(
|
||||||
|
size: FloatingKeyboardSize,
|
||||||
|
content: @Composable BoxScope.(actionBarGap: Dp) -> Unit
|
||||||
|
) = with(LocalDensity.current) {
|
||||||
|
val offset = remember(size) { mutableStateOf(Offset(size.bottomOrigin.first.toFloat(), size.bottomOrigin.second.toFloat())) }
|
||||||
|
|
||||||
ActionEditorHost()
|
val resizing = remember { mutableStateOf(false) }
|
||||||
}
|
|
||||||
|
val onDragDelta: (DragDelta) -> Boolean = remember { { delta ->
|
||||||
|
// Matching the necessary coordinate space
|
||||||
|
var deltaX = delta.left
|
||||||
|
var deltaY = -delta.bottom
|
||||||
|
var deltaWidth = delta.right - delta.left
|
||||||
|
var deltaHeight = delta.bottom - delta.top
|
||||||
|
|
||||||
|
var result = true
|
||||||
|
|
||||||
|
// TODO: Limit the values so that we do not go off-screen
|
||||||
|
// If we have reached a minimum limit, return false
|
||||||
|
|
||||||
|
// Basic limiting for minimum size
|
||||||
|
val currSettings = latinIME.sizingCalculator.getSavedSettings()
|
||||||
|
val currSize = Size(
|
||||||
|
currSettings.floatingWidthDp.dp.toPx(),
|
||||||
|
currSettings.floatingHeightDp.dp.toPx()
|
||||||
|
)
|
||||||
|
|
||||||
|
if(currSize.width + deltaWidth < 200.dp.toPx()) {
|
||||||
|
deltaWidth = deltaWidth.coerceAtLeast(200.dp.toPx() - currSize.width)
|
||||||
|
deltaX = 0.0f
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if(currSize.height + deltaHeight < 160.dp.toPx()) {
|
||||||
|
deltaHeight = deltaHeight.coerceAtLeast(160.dp.toPx() - currSize.height)
|
||||||
|
deltaY = 0.0f
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
|
||||||
|
latinIME.sizingCalculator.editSavedSettings { settings ->
|
||||||
|
settings.copy(
|
||||||
|
floatingBottomOriginDp = Pair(
|
||||||
|
settings.floatingBottomOriginDp.first + deltaX.toDp().value,
|
||||||
|
settings.floatingBottomOriginDp.second + deltaY.toDp().value
|
||||||
|
),
|
||||||
|
floatingWidthDp = settings.floatingWidthDp + deltaWidth.toDp().value,
|
||||||
|
floatingHeightDp = settings.floatingHeightDp + deltaHeight.toDp().value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
} }
|
||||||
|
|
||||||
|
OffsetPositioner(offset.value) {
|
||||||
|
KeyboardSurface(
|
||||||
|
requiredWidthPx = size.width,
|
||||||
|
backgroundColor = latinIME.keyboardColor,
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
padding = size.decorationPadding
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
FloatingKeyboardContents(
|
||||||
|
pointerInputKey = size,
|
||||||
|
onDragged = { dragAmount ->
|
||||||
|
var newOffset = offset.value.copy(
|
||||||
|
x = offset.value.x + dragAmount.x,
|
||||||
|
y = offset.value.y - dragAmount.y
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure we are not out of bounds
|
||||||
|
newOffset = newOffset.copy(
|
||||||
|
newOffset.x.coerceAtLeast(0.0f),
|
||||||
|
newOffset.y.coerceAtLeast(0.0f)
|
||||||
|
)
|
||||||
|
newOffset = newOffset.copy(
|
||||||
|
newOffset.x.coerceAtMost(
|
||||||
|
latinIME.getViewWidth().toFloat() - size.width
|
||||||
|
),
|
||||||
|
newOffset.y.coerceAtMost(
|
||||||
|
latinIME.getViewHeight().toFloat() - measuredTouchableHeight
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
offset.value = newOffset
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
latinIME.sizingCalculator.editSavedSettings { settings ->
|
||||||
|
settings.copy(
|
||||||
|
floatingBottomOriginDp = Pair(
|
||||||
|
offset.value.x.toDp().value,
|
||||||
|
offset.value.y.toDp().value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onResizerOpen = {
|
||||||
|
resizing.value = true
|
||||||
|
},
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resizing.value) {
|
||||||
|
ResizerRect(onDragDelta, showResetApply = true, onApply = {
|
||||||
|
resizing.value = false
|
||||||
|
}, onReset = { })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun NonFloatingKeyboardWindow(
|
||||||
|
size: ComputedKeyboardSize,
|
||||||
|
content: @Composable BoxScope.(actionBarGap: Dp) -> Unit
|
||||||
|
) = with(LocalDensity.current) {
|
||||||
|
OffsetPositioner(Offset(0.0f, 0.0f)) {
|
||||||
|
KeyboardSurface(
|
||||||
|
requiredWidthPx = size.getWidth(),
|
||||||
|
backgroundColor = latinIME.keyboardColor,
|
||||||
|
padding = size.getPadding()
|
||||||
|
) {
|
||||||
|
val actionBarGap = 4.dp
|
||||||
|
|
||||||
|
when(size) {
|
||||||
|
is OneHandedKeyboardSize -> {
|
||||||
|
Box(modifier = Modifier.width(size.layoutWidth.toDp()).align(
|
||||||
|
when(size.direction) {
|
||||||
|
OneHandedDirection.Left -> Alignment.CenterStart
|
||||||
|
OneHandedDirection.Right -> Alignment.CenterEnd
|
||||||
|
}
|
||||||
|
)) {
|
||||||
|
content(actionBarGap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
content(actionBarGap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun KeyboardWindowSelector(content: @Composable BoxScope.(actionBarGap: Dp) -> Unit) {
|
||||||
|
val size = latinIME.size.value
|
||||||
|
when(size) {
|
||||||
|
is FloatingKeyboardSize -> FloatingKeyboardWindow(size, content)
|
||||||
|
|
||||||
|
is OneHandedKeyboardSize,
|
||||||
|
is RegularKeyboardSize,
|
||||||
|
is SplitKeyboardSize -> NonFloatingKeyboardWindow(size, content)
|
||||||
|
|
||||||
|
null -> return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ProvidersAndWrapper(content: @Composable () -> Unit) {
|
||||||
|
UixThemeWrapper(latinIME.colorScheme) {
|
||||||
|
DataStoreCacheProvider {
|
||||||
|
CompositionLocalProvider(LocalManager provides keyboardManagerForAction) {
|
||||||
|
CompositionLocalProvider(LocalThemeProvider provides latinIME.getDrawableProvider()) {
|
||||||
|
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||||
|
CompositionLocalProvider(LocalFoldingState provides foldingOptions.value) {
|
||||||
|
content()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -674,6 +921,37 @@ class UixManager(private val latinIME: LatinIME) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setContent() {
|
||||||
|
composeView?.setContent {
|
||||||
|
ProvidersAndWrapper {
|
||||||
|
InputDarkener(isInputOverridden.value || isShowingActionEditor.value) {
|
||||||
|
closeActionWindow()
|
||||||
|
isShowingActionEditor.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardWindowSelector { gap ->
|
||||||
|
Column {
|
||||||
|
when {
|
||||||
|
currWindowActionWindow != null -> ActionViewWithHeader(
|
||||||
|
currWindowActionWindow!!
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> MainKeyboardViewWithActionBar()
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(gap))
|
||||||
|
|
||||||
|
latinIME.LegacyKeyboardView(hidden = isMainKeyboardHidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
ForgetWordDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionEditorHost()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun showUpdateNoticeIfNeeded() {
|
suspend fun showUpdateNoticeIfNeeded() {
|
||||||
if(!BuildConfig.UPDATE_CHECKING) return
|
if(!BuildConfig.UPDATE_CHECKING) return
|
||||||
|
|
||||||
@ -734,28 +1012,6 @@ class UixManager(private val latinIME: LatinIME) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createComposeView(): View {
|
|
||||||
if(composeView != null) {
|
|
||||||
composeView = null
|
|
||||||
//throw IllegalStateException("Attempted to create compose view, when one is already created!")
|
|
||||||
}
|
|
||||||
|
|
||||||
composeView = ComposeView(latinIME).apply {
|
|
||||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
|
||||||
setParentCompositionContext(null)
|
|
||||||
|
|
||||||
latinIME.setOwners()
|
|
||||||
}
|
|
||||||
|
|
||||||
setContent()
|
|
||||||
|
|
||||||
return composeView!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getComposeView(): View? {
|
|
||||||
return composeView
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onColorSchemeChanged() {
|
fun onColorSchemeChanged() {
|
||||||
setContent()
|
setContent()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
package org.futo.inputmethod.latin.uix.actions
|
||||||
|
|
||||||
|
import org.futo.inputmethod.latin.R
|
||||||
|
import org.futo.inputmethod.latin.uix.Action
|
||||||
|
import org.futo.inputmethod.v2keyboard.KeyboardMode
|
||||||
|
import org.futo.inputmethod.v2keyboard.OneHandedDirection
|
||||||
|
|
||||||
|
val LeftHandedKeyboardAction = Action(
|
||||||
|
icon = R.drawable.arrow_left,
|
||||||
|
name = R.string.left_handed_keyboard_action_title,
|
||||||
|
simplePressImpl = { manager, _ ->
|
||||||
|
manager.getSizingCalculator().editSavedSettings {
|
||||||
|
if(it.currentMode == KeyboardMode.OneHanded
|
||||||
|
&& it.oneHandedDirection == OneHandedDirection.Left) {
|
||||||
|
it.copy(
|
||||||
|
currentMode = KeyboardMode.Regular
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it.copy(
|
||||||
|
oneHandedDirection = OneHandedDirection.Left,
|
||||||
|
currentMode = KeyboardMode.OneHanded
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
windowImpl = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val RightHandedKeyboardAction = Action(
|
||||||
|
icon = R.drawable.arrow_right,
|
||||||
|
name = R.string.right_handed_keyboard_action_title,
|
||||||
|
simplePressImpl = { manager, _ ->
|
||||||
|
manager.getSizingCalculator().editSavedSettings {
|
||||||
|
if(it.currentMode == KeyboardMode.OneHanded
|
||||||
|
&& it.oneHandedDirection == OneHandedDirection.Right) {
|
||||||
|
it.copy(
|
||||||
|
currentMode = KeyboardMode.Regular
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it.copy(
|
||||||
|
oneHandedDirection = OneHandedDirection.Right,
|
||||||
|
currentMode = KeyboardMode.OneHanded
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
windowImpl = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val SplitKeyboardAction = Action(
|
||||||
|
icon = R.drawable.arrow_down,
|
||||||
|
name = R.string.split_keyboard_action_title,
|
||||||
|
simplePressImpl = { manager, _ ->
|
||||||
|
manager.getSizingCalculator().editSavedSettings {
|
||||||
|
if(it.currentMode == KeyboardMode.Split) {
|
||||||
|
it.copy(
|
||||||
|
currentMode = KeyboardMode.Regular
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it.copy(
|
||||||
|
currentMode = KeyboardMode.Split
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
windowImpl = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val FloatingKeyboardAction = Action(
|
||||||
|
icon = R.drawable.arrow_up,
|
||||||
|
name = R.string.floating_keyboard_action_title,
|
||||||
|
simplePressImpl = { manager, _ ->
|
||||||
|
manager.getSizingCalculator().editSavedSettings {
|
||||||
|
if(it.currentMode == KeyboardMode.Floating) {
|
||||||
|
it.copy(
|
||||||
|
currentMode = KeyboardMode.Regular
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it.copy(
|
||||||
|
currentMode = KeyboardMode.Floating
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
windowImpl = null,
|
||||||
|
)
|
@ -31,7 +31,11 @@ val AllActionsMap = mapOf(
|
|||||||
"copy" to CopyAction,
|
"copy" to CopyAction,
|
||||||
"select_all" to SelectAllAction,
|
"select_all" to SelectAllAction,
|
||||||
"more" to MoreActionsAction,
|
"more" to MoreActionsAction,
|
||||||
"bugs" to BugViewerAction
|
"bugs" to BugViewerAction,
|
||||||
|
"onehanded_left" to LeftHandedKeyboardAction,
|
||||||
|
"onehanded_right" to RightHandedKeyboardAction,
|
||||||
|
"split_keyboard" to SplitKeyboardAction,
|
||||||
|
"floating_keyboard" to FloatingKeyboardAction
|
||||||
)
|
)
|
||||||
|
|
||||||
val ActionToId = AllActionsMap.entries.associate { it.value to it.key }
|
val ActionToId = AllActionsMap.entries.associate { it.value to it.key }
|
||||||
|
@ -240,7 +240,7 @@ public class LanguageModelFacilitator(
|
|||||||
try {
|
try {
|
||||||
inputLogic.mWordComposer.setAutoCorrection(null)
|
inputLogic.mWordComposer.setAutoCorrection(null)
|
||||||
|
|
||||||
if(values.composedData.mTypedWord.length > BinaryDictionary.DICTIONARY_MAX_WORD_LENGTH) {
|
if(values.composedData.mTypedWord.length > BinaryDictionary.DICTIONARY_MAX_WORD_LENGTH-1) {
|
||||||
inputLogic.mSuggestionStripViewAccessor.setNeutralSuggestionStrip()
|
inputLogic.mSuggestionStripViewAccessor.setNeutralSuggestionStrip()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -76,16 +76,12 @@ fun getPrimaryLayoutOverride(editorInfo: EditorInfo?): String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class KeyboardLayoutSetV2Params(
|
data class KeyboardLayoutSetV2Params(
|
||||||
val width: Int,
|
val computedSize: ComputedKeyboardSize,
|
||||||
val height: Int,
|
|
||||||
val padding: Rect,
|
|
||||||
val keyboardLayoutSet: String,
|
val keyboardLayoutSet: String,
|
||||||
val locale: Locale,
|
val locale: Locale,
|
||||||
val editorInfo: EditorInfo?,
|
val editorInfo: EditorInfo?,
|
||||||
val numberRow: Boolean,
|
val numberRow: Boolean,
|
||||||
val gap: Float = 4.0f,
|
val gap: Float = 4.0f,
|
||||||
val useSplitLayout: Boolean,
|
|
||||||
val splitLayoutWidth: Int,
|
|
||||||
val bottomActionKey: Int?,
|
val bottomActionKey: Int?,
|
||||||
val longPressKeySettings: LongPressKeySettings? = null
|
val longPressKeySettings: LongPressKeySettings? = null
|
||||||
)
|
)
|
||||||
@ -184,14 +180,15 @@ class KeyboardLayoutSetV2 internal constructor(
|
|||||||
NumberRowMode.AlwaysDisabled -> false
|
NumberRowMode.AlwaysDisabled -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
private val widthMinusPadding = params.width - params.padding.left - params.padding.right
|
private val height = params.computedSize.getHeight()
|
||||||
private val heightMinusPadding = params.height - params.padding.top - params.padding.bottom
|
|
||||||
|
private val padding = params.computedSize.getPadding()
|
||||||
|
|
||||||
|
private val widthMinusPadding = params.computedSize.getTotalKeyboardWidth()
|
||||||
|
private val heightMinusPadding = height - padding.top - padding.bottom
|
||||||
|
|
||||||
private val singularRowHeight: Double
|
private val singularRowHeight: Double
|
||||||
get() = heightMinusPadding?.let { it / 4.0 } ?: run {
|
get() = heightMinusPadding / 4.0
|
||||||
(ResourceUtils.getDefaultKeyboardHeight(context.resources) / 4.0) *
|
|
||||||
keyboardHeightMultiplier
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getKeyboard(element: KeyboardLayoutElement): Keyboard {
|
fun getKeyboard(element: KeyboardLayoutElement): Keyboard {
|
||||||
|
|
||||||
@ -221,10 +218,8 @@ class KeyboardLayoutSetV2 internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val layoutParams = LayoutParams(
|
val layoutParams = LayoutParams(
|
||||||
|
size = params.computedSize,
|
||||||
gap = params.gap.dp,
|
gap = params.gap.dp,
|
||||||
useSplitLayout = params.useSplitLayout,
|
|
||||||
splitLayoutWidth = params.splitLayoutWidth,
|
|
||||||
padding = params.padding,
|
|
||||||
standardRowHeight = singularRowHeight,
|
standardRowHeight = singularRowHeight,
|
||||||
element = element
|
element = element
|
||||||
)
|
)
|
||||||
|
@ -2,12 +2,27 @@ package org.futo.inputmethod.v2keyboard
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import androidx.datastore.preferences.core.floatPreferencesKey
|
|
||||||
import androidx.window.layout.FoldingFeature
|
import androidx.window.layout.FoldingFeature
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Serializer
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.element
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.encoding.CompositeDecoder
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlinx.serialization.encoding.decodeStructure
|
||||||
|
import kotlinx.serialization.encoding.encodeStructure
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import org.futo.inputmethod.latin.FoldStateProvider
|
import org.futo.inputmethod.latin.FoldStateProvider
|
||||||
|
import org.futo.inputmethod.latin.LatinIME
|
||||||
import org.futo.inputmethod.latin.uix.SettingsKey
|
import org.futo.inputmethod.latin.uix.SettingsKey
|
||||||
|
import org.futo.inputmethod.latin.uix.UixManager
|
||||||
import org.futo.inputmethod.latin.uix.getSettingBlocking
|
import org.futo.inputmethod.latin.uix.getSettingBlocking
|
||||||
|
import org.futo.inputmethod.latin.uix.setSettingBlocking
|
||||||
import org.futo.inputmethod.latin.utils.ResourceUtils
|
import org.futo.inputmethod.latin.utils.ResourceUtils
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -17,13 +32,140 @@ interface KeyboardSizeStateProvider {
|
|||||||
|
|
||||||
sealed class ComputedKeyboardSize()
|
sealed class ComputedKeyboardSize()
|
||||||
|
|
||||||
class RegularKeyboardSize(val height: Int, val padding: Rect) : ComputedKeyboardSize()
|
class RegularKeyboardSize(val height: Int, val width: Int, val padding: Rect) : ComputedKeyboardSize()
|
||||||
|
|
||||||
class SplitKeyboardSize(val height: Int, val padding: Rect, val splitLayoutWidth: Int) : ComputedKeyboardSize()
|
class SplitKeyboardSize(val height: Int, val width: Int, val padding: Rect, val splitLayoutWidth: Int) : ComputedKeyboardSize()
|
||||||
|
|
||||||
//class OneHandedKeyboardSize(val height: Int, val offset: Int, val sideInset: Int, val isLeft: Boolean, val width: Int): ComputedKeyboardSize()
|
enum class OneHandedDirection {
|
||||||
//class FloatingKeyboardSize(val x: Int, val y: Int, val width: Int, val height: Int): ComputedKeyboardSize()
|
Left,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
|
||||||
|
class OneHandedKeyboardSize(val height: Int, val width: Int, val padding: Rect, val layoutWidth: Int, val direction: OneHandedDirection): ComputedKeyboardSize()
|
||||||
|
|
||||||
|
class FloatingKeyboardSize(
|
||||||
|
val bottomOrigin: Pair<Int, Int>,
|
||||||
|
val width: Int,
|
||||||
|
val height: Int,
|
||||||
|
val decorationPadding: Rect
|
||||||
|
): ComputedKeyboardSize()
|
||||||
|
|
||||||
|
fun ComputedKeyboardSize.getHeight(): Int = when(this) {
|
||||||
|
is FloatingKeyboardSize -> height
|
||||||
|
is OneHandedKeyboardSize -> height
|
||||||
|
is RegularKeyboardSize -> height
|
||||||
|
is SplitKeyboardSize -> height
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ComputedKeyboardSize.getWidth(): Int = when(this) {
|
||||||
|
is FloatingKeyboardSize -> width
|
||||||
|
is OneHandedKeyboardSize -> width
|
||||||
|
is RegularKeyboardSize -> width
|
||||||
|
is SplitKeyboardSize -> width
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ComputedKeyboardSize.getPadding(): Rect = when(this) {
|
||||||
|
is FloatingKeyboardSize -> decorationPadding
|
||||||
|
is OneHandedKeyboardSize -> padding
|
||||||
|
is RegularKeyboardSize -> padding
|
||||||
|
is SplitKeyboardSize -> padding
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ComputedKeyboardSize.getTotalKeyboardWidth(): Int = when(this) {
|
||||||
|
is FloatingKeyboardSize -> width - decorationPadding.left - decorationPadding.right
|
||||||
|
is OneHandedKeyboardSize -> layoutWidth
|
||||||
|
is RegularKeyboardSize -> width - padding.left - padding.right
|
||||||
|
is SplitKeyboardSize -> width - padding.left - padding.right
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class KeyboardMode {
|
||||||
|
Regular,
|
||||||
|
Split,
|
||||||
|
OneHanded,
|
||||||
|
Floating
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Serializer(forClass = Rect::class)
|
||||||
|
object RectSerializer : KSerializer<Rect> {
|
||||||
|
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Rect") {
|
||||||
|
element<Int>("left")
|
||||||
|
element<Int>("top")
|
||||||
|
element<Int>("right")
|
||||||
|
element<Int>("bottom")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: Rect) {
|
||||||
|
encoder.encodeStructure(descriptor) {
|
||||||
|
encodeIntElement(descriptor, 0, value.left)
|
||||||
|
encodeIntElement(descriptor, 1, value.top)
|
||||||
|
encodeIntElement(descriptor, 2, value.right)
|
||||||
|
encodeIntElement(descriptor, 3, value.bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): Rect {
|
||||||
|
return decoder.decodeStructure(descriptor) {
|
||||||
|
var left = 0
|
||||||
|
var top = 0
|
||||||
|
var right = 0
|
||||||
|
var bottom = 0
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
when (val index = decodeElementIndex(descriptor)) {
|
||||||
|
0 -> left = decodeIntElement(descriptor, 0)
|
||||||
|
1 -> top = decodeIntElement(descriptor, 1)
|
||||||
|
2 -> right = decodeIntElement(descriptor, 2)
|
||||||
|
3 -> bottom = decodeIntElement(descriptor, 3)
|
||||||
|
CompositeDecoder.DECODE_DONE -> break
|
||||||
|
else -> error("Unexpected index: $index")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect(left, top, right, bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SavedKeyboardSizingSettings(
|
||||||
|
val currentMode: KeyboardMode,
|
||||||
|
val heightMultiplier: Float,
|
||||||
|
val paddingDp: @Serializable(RectSerializer::class) Rect,
|
||||||
|
|
||||||
|
// Split
|
||||||
|
val splitWidthFraction: Float,
|
||||||
|
|
||||||
|
// One handed, values with respect to left handed mode
|
||||||
|
// left = padding
|
||||||
|
// right = width + padding
|
||||||
|
// bottom = padding for bottom
|
||||||
|
val oneHandedRectDp: @Serializable(RectSerializer::class) Rect,
|
||||||
|
val oneHandedDirection: OneHandedDirection,
|
||||||
|
|
||||||
|
// Floating
|
||||||
|
// bottom left of the floating keyboard, relative to bottom left of screen, .second is Y up
|
||||||
|
val floatingBottomOriginDp: Pair<Float, Float>,
|
||||||
|
val floatingWidthDp: Float,
|
||||||
|
val floatingHeightDp: Float
|
||||||
|
) {
|
||||||
|
fun toJsonString(): String =
|
||||||
|
Json.encodeToString(this)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun fromJsonString(s: String): SavedKeyboardSizingSettings? =
|
||||||
|
try {
|
||||||
|
Json.decodeFromString(s)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
//e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class KeyboardSizeSettingKind {
|
enum class KeyboardSizeSettingKind {
|
||||||
Portrait,
|
Portrait,
|
||||||
@ -31,117 +173,230 @@ enum class KeyboardSizeSettingKind {
|
|||||||
FoldableInnerDisplay
|
FoldableInnerDisplay
|
||||||
}
|
}
|
||||||
|
|
||||||
val SplitKeyboardSettings = mapOf(
|
val DefaultKeyboardSettings = mapOf(
|
||||||
KeyboardSizeSettingKind.Portrait to SettingsKey(
|
KeyboardSizeSettingKind.Portrait to SavedKeyboardSizingSettings(
|
||||||
booleanPreferencesKey("split_keyboard_portrait"), false),
|
currentMode = KeyboardMode.Regular,
|
||||||
KeyboardSizeSettingKind.Landscape to SettingsKey(
|
heightMultiplier = 1.0f,
|
||||||
booleanPreferencesKey("split_keyboard_landscape"), true),
|
paddingDp = Rect(2, 4, 2, 10),
|
||||||
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
|
splitWidthFraction = 4.0f / 5.0f,
|
||||||
booleanPreferencesKey("split_keyboard_fold"), true),
|
oneHandedDirection = OneHandedDirection.Right,
|
||||||
|
oneHandedRectDp = Rect(4, 4, 364, 30),
|
||||||
|
floatingBottomOriginDp = Pair(0.0f, 0.0f),
|
||||||
|
floatingHeightDp = 240.0f,
|
||||||
|
floatingWidthDp = 360.0f
|
||||||
|
),
|
||||||
|
|
||||||
|
KeyboardSizeSettingKind.Landscape to SavedKeyboardSizingSettings(
|
||||||
|
currentMode = KeyboardMode.Split,
|
||||||
|
heightMultiplier = 0.9f,
|
||||||
|
paddingDp = Rect(8, 2, 8, 2),
|
||||||
|
splitWidthFraction = 3.0f / 5.0f,
|
||||||
|
oneHandedDirection = OneHandedDirection.Right,
|
||||||
|
oneHandedRectDp = Rect(4, 4, 364, 30),
|
||||||
|
floatingBottomOriginDp = Pair(0.0f, 0.0f),
|
||||||
|
floatingHeightDp = 240.0f,
|
||||||
|
floatingWidthDp = 360.0f
|
||||||
|
),
|
||||||
|
|
||||||
|
KeyboardSizeSettingKind.FoldableInnerDisplay to SavedKeyboardSizingSettings(
|
||||||
|
currentMode = KeyboardMode.Split,
|
||||||
|
heightMultiplier = 0.67f,
|
||||||
|
paddingDp = Rect(44, 4, 44, 8),
|
||||||
|
splitWidthFraction = 3.0f / 5.0f,
|
||||||
|
oneHandedDirection = OneHandedDirection.Right,
|
||||||
|
oneHandedRectDp = Rect(4, 4, 364, 30),
|
||||||
|
floatingBottomOriginDp = Pair(0.0f, 0.0f),
|
||||||
|
floatingHeightDp = 240.0f,
|
||||||
|
floatingWidthDp = 360.0f
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
val KeyboardHeightSettings = mapOf(
|
val KeyboardSettings = mapOf(
|
||||||
KeyboardSizeSettingKind.Portrait to SettingsKey(
|
KeyboardSizeSettingKind.Portrait to SettingsKey(
|
||||||
floatPreferencesKey("keyboardHeightMultiplier"), 1.0f),
|
stringPreferencesKey("keyboard_settings_portrait"), ""),
|
||||||
KeyboardSizeSettingKind.Landscape to SettingsKey(
|
KeyboardSizeSettingKind.Landscape to SettingsKey(
|
||||||
floatPreferencesKey("keyboard_height_landscape"), 0.9f),
|
stringPreferencesKey("keyboard_settings_landscape"), ""),
|
||||||
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
|
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
|
||||||
floatPreferencesKey("keyboard_height_fold"), 0.67f),
|
stringPreferencesKey("keyboard_settings_fold"), ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
val KeyboardOffsetSettings = mapOf(
|
class KeyboardSizingCalculator(val context: Context, val uixManager: UixManager) {
|
||||||
KeyboardSizeSettingKind.Portrait to SettingsKey(
|
val sizeStateProvider = context as KeyboardSizeStateProvider
|
||||||
floatPreferencesKey("keyboard_offset_portrait"), 8.0f),
|
val foldStateProvider = context as FoldStateProvider
|
||||||
KeyboardSizeSettingKind.Landscape to SettingsKey(
|
|
||||||
floatPreferencesKey("keyboard_offset_landscape"), 0.0f),
|
|
||||||
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
|
|
||||||
floatPreferencesKey("keyboard_offset_fold"), 8.0f),
|
|
||||||
)
|
|
||||||
|
|
||||||
val KeyboardSideInsetSettings = mapOf(
|
private fun dp(v: Number): Int =
|
||||||
KeyboardSizeSettingKind.Portrait to SettingsKey(
|
(v.toFloat() * context.resources.displayMetrics.density).toInt()
|
||||||
floatPreferencesKey("keyboard_inset_portrait"), 2.0f),
|
|
||||||
KeyboardSizeSettingKind.Landscape to SettingsKey(
|
private fun dp(v: Rect): Rect =
|
||||||
floatPreferencesKey("keyboard_inset_landscape"), 8.0f),
|
Rect(dp(v.left), dp(v.top), dp(v.right), dp(v.bottom))
|
||||||
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
|
|
||||||
floatPreferencesKey("keyboard_inset_fold"), 44.0f),
|
private fun limitFloating(rectPx: Rect): Rect {
|
||||||
)
|
val width = rectPx.width()
|
||||||
|
val height = rectPx.height()
|
||||||
|
|
||||||
|
val minWidth = dp(160)
|
||||||
|
val minHeight = dp(160)
|
||||||
|
|
||||||
|
if(width < minWidth) {
|
||||||
|
val delta = minWidth - width
|
||||||
|
rectPx.left -= delta / 2
|
||||||
|
rectPx.right += delta / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if(height < minHeight) {
|
||||||
|
val delta = minHeight - height
|
||||||
|
rectPx.top -= delta
|
||||||
|
rectPx.bottom += delta
|
||||||
|
}
|
||||||
|
|
||||||
|
val maxWidth = context.resources.displayMetrics.widthPixels * 2 / 3
|
||||||
|
val maxHeight = context.resources.displayMetrics.heightPixels * 2 / 3
|
||||||
|
|
||||||
|
if(width > maxWidth) {
|
||||||
|
val delta = width - maxWidth
|
||||||
|
rectPx.left += delta / 2
|
||||||
|
rectPx.right -= delta / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if(height > maxHeight) {
|
||||||
|
val delta = height - maxHeight
|
||||||
|
rectPx.top += delta / 2
|
||||||
|
rectPx.bottom -= delta / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class KeyboardSizingCalculator(val context: Context) {
|
val originX = rectPx.left
|
||||||
private val sizeStateProvider = context as KeyboardSizeStateProvider
|
val originY = rectPx.top
|
||||||
private val foldStateProvider = context as FoldStateProvider
|
|
||||||
|
|
||||||
private fun isSplitKeyboard(mode: KeyboardSizeSettingKind): Boolean =
|
if(originX < 0){
|
||||||
context.getSettingBlocking(SplitKeyboardSettings[mode]!!)
|
rectPx.left -= originX
|
||||||
|
rectPx.right -= originX
|
||||||
|
}
|
||||||
|
|
||||||
private fun heightMultiplier(mode: KeyboardSizeSettingKind): Float =
|
if(originY < 0) {
|
||||||
context.getSettingBlocking(KeyboardHeightSettings[mode]!!)
|
rectPx.top -= originY
|
||||||
|
rectPx.bottom -= originY
|
||||||
|
}
|
||||||
|
|
||||||
private fun bottomOffsetPx(mode: KeyboardSizeSettingKind): Int =
|
if(rectPx.right > context.resources.displayMetrics.widthPixels) {
|
||||||
(context.getSettingBlocking(KeyboardOffsetSettings[mode]!!) * context.resources.displayMetrics.density).toInt()
|
val delta = rectPx.right - context.resources.displayMetrics.widthPixels
|
||||||
|
rectPx.right -= delta
|
||||||
|
rectPx.left -= delta
|
||||||
|
}
|
||||||
|
if(rectPx.bottom < 0) {
|
||||||
|
val delta = rectPx.bottom
|
||||||
|
rectPx.top -= delta
|
||||||
|
rectPx.bottom -= delta
|
||||||
|
}
|
||||||
|
|
||||||
private fun sideInsetPx(mode: KeyboardSizeSettingKind): Int =
|
return rectPx
|
||||||
(context.getSettingBlocking(KeyboardSideInsetSettings[mode]!!) * context.resources.displayMetrics.density).toInt()
|
}
|
||||||
|
|
||||||
private fun topPaddingPx(mode: KeyboardSizeSettingKind): Int =
|
fun getSavedSettings(): SavedKeyboardSizingSettings =
|
||||||
(when(mode) {
|
SavedKeyboardSizingSettings.fromJsonString(context.getSettingBlocking(
|
||||||
KeyboardSizeSettingKind.Portrait -> 4.0f
|
KeyboardSettings[sizeStateProvider.currentSizeState]!!
|
||||||
KeyboardSizeSettingKind.Landscape -> 0.0f
|
)) ?: DefaultKeyboardSettings[sizeStateProvider.currentSizeState]!!
|
||||||
KeyboardSizeSettingKind.FoldableInnerDisplay -> 8.0f
|
|
||||||
} * context.resources.displayMetrics.density).toInt()
|
fun editSavedSettings(transform: (SavedKeyboardSizingSettings) -> SavedKeyboardSizingSettings) {
|
||||||
|
val sizeState = sizeStateProvider.currentSizeState
|
||||||
|
|
||||||
|
val savedSettings = SavedKeyboardSizingSettings.fromJsonString(context.getSettingBlocking(
|
||||||
|
KeyboardSettings[sizeState]!!
|
||||||
|
)) ?: DefaultKeyboardSettings[sizeState]!!
|
||||||
|
|
||||||
|
val transformed = transform(savedSettings)
|
||||||
|
|
||||||
|
if(transformed != savedSettings) {
|
||||||
|
context.setSettingBlocking(KeyboardSettings[sizeState]!!.key, transformed.toJsonString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun calculate(layoutName: String, isNumberRowActive: Boolean): ComputedKeyboardSize {
|
fun calculate(layoutName: String, isNumberRowActive: Boolean): ComputedKeyboardSize {
|
||||||
|
val savedSettings = getSavedSettings()
|
||||||
|
|
||||||
val layout = LayoutManager.getLayout(context, layoutName)
|
val layout = LayoutManager.getLayout(context, layoutName)
|
||||||
val effectiveRowCount = layout.effectiveRows.size
|
val effectiveRowCount = layout.effectiveRows.size
|
||||||
|
|
||||||
val configuration = context.resources.configuration
|
|
||||||
val displayMetrics = context.resources.displayMetrics
|
val displayMetrics = context.resources.displayMetrics
|
||||||
|
|
||||||
val mode = sizeStateProvider.currentSizeState
|
val singularRowHeight = (ResourceUtils.getDefaultKeyboardHeight(context.resources) / 4.0) *
|
||||||
|
savedSettings.heightMultiplier
|
||||||
val isSplit = isSplitKeyboard(mode)
|
|
||||||
val heightMultiplier = heightMultiplier(mode)
|
|
||||||
val bottomOffset = bottomOffsetPx(mode)
|
|
||||||
val sideInset = sideInsetPx(mode)
|
|
||||||
val topPadding = topPaddingPx(mode)
|
|
||||||
|
|
||||||
val singularRowHeight = (ResourceUtils.getDefaultKeyboardHeight(context.resources) / 4.0) * heightMultiplier
|
|
||||||
|
|
||||||
val numRows = 4.0 +
|
val numRows = 4.0 +
|
||||||
((effectiveRowCount - 5) / 2.0).coerceAtLeast(0.0) +
|
((effectiveRowCount - 5) / 2.0).coerceAtLeast(0.0) +
|
||||||
if(isNumberRowActive) { 0.5 } else { 0.0 }
|
if(isNumberRowActive) { 0.5 } else { 0.0 }
|
||||||
|
|
||||||
println("Num rows; $numRows, $effectiveRowCount ($layoutName) ($layout)")
|
|
||||||
|
|
||||||
val recommendedHeight = numRows * singularRowHeight
|
val recommendedHeight = numRows * singularRowHeight
|
||||||
|
|
||||||
|
|
||||||
val foldState = foldStateProvider.foldState.feature
|
val foldState = foldStateProvider.foldState.feature
|
||||||
|
|
||||||
|
val window = (context as LatinIME).window.window
|
||||||
|
val width = ResourceUtils.getDefaultKeyboardWidth(window, context.resources)
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
// Special case: 50% screen height no matter the row count or settings
|
// Special case: 50% screen height no matter the row count or settings
|
||||||
foldState != null && foldState.state == FoldingFeature.State.HALF_OPENED && foldState.orientation == FoldingFeature.Orientation.HORIZONTAL ->
|
foldState != null && foldState.state == FoldingFeature.State.HALF_OPENED && foldState.orientation == FoldingFeature.Orientation.HORIZONTAL ->
|
||||||
SplitKeyboardSize(
|
SplitKeyboardSize(
|
||||||
displayMetrics.heightPixels / 2 - (displayMetrics.density * 80.0f).toInt(),
|
height = displayMetrics.heightPixels / 2 - (displayMetrics.density * 80.0f).toInt(),
|
||||||
Rect(
|
width = width,
|
||||||
|
padding = Rect(
|
||||||
(displayMetrics.density * 44.0f).roundToInt(),
|
(displayMetrics.density * 44.0f).roundToInt(),
|
||||||
(displayMetrics.density * 20.0f).roundToInt(),
|
(displayMetrics.density * 50.0f).roundToInt(),
|
||||||
(displayMetrics.density * 44.0f).roundToInt(),
|
(displayMetrics.density * 44.0f).roundToInt(),
|
||||||
(displayMetrics.density * 12.0f).roundToInt(),
|
(displayMetrics.density * 12.0f).roundToInt(),
|
||||||
),
|
),
|
||||||
displayMetrics.widthPixels * 3 / 5
|
splitLayoutWidth = displayMetrics.widthPixels * 3 / 5
|
||||||
)
|
)
|
||||||
|
|
||||||
isSplit -> SplitKeyboardSize(
|
savedSettings.currentMode == KeyboardMode.Split ->
|
||||||
recommendedHeight.roundToInt(),
|
SplitKeyboardSize(
|
||||||
Rect(sideInset, topPadding, sideInset, bottomOffset),
|
height = recommendedHeight.roundToInt(),
|
||||||
displayMetrics.widthPixels * 3 / 5)
|
width = width,
|
||||||
|
padding = dp(savedSettings.paddingDp),
|
||||||
|
splitLayoutWidth = (displayMetrics.widthPixels * savedSettings.splitWidthFraction).toInt()
|
||||||
|
)
|
||||||
|
|
||||||
else -> RegularKeyboardSize(
|
savedSettings.currentMode == KeyboardMode.OneHanded ->
|
||||||
recommendedHeight.roundToInt(),
|
OneHandedKeyboardSize(
|
||||||
Rect(sideInset, topPadding, sideInset, bottomOffset),
|
height = recommendedHeight.roundToInt(),
|
||||||
)
|
width = width,
|
||||||
|
padding = dp(savedSettings.oneHandedRectDp).let { rect ->
|
||||||
|
when(savedSettings.oneHandedDirection) {
|
||||||
|
OneHandedDirection.Left -> Rect(rect.left, rect.top, rect.left, rect.bottom)
|
||||||
|
OneHandedDirection.Right -> Rect(rect.left, rect.top, rect.left, rect.bottom)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layoutWidth = dp(savedSettings.oneHandedRectDp.width()).coerceAtMost(displayMetrics.widthPixels * 9 / 10),
|
||||||
|
direction = savedSettings.oneHandedDirection
|
||||||
|
)
|
||||||
|
|
||||||
|
savedSettings.currentMode == KeyboardMode.Floating -> {
|
||||||
|
val singularRowHeightFloat = dp(savedSettings.floatingHeightDp) / 4.0f
|
||||||
|
val recommendedHeightFloat = singularRowHeightFloat * numRows
|
||||||
|
FloatingKeyboardSize(
|
||||||
|
bottomOrigin = Pair(
|
||||||
|
dp(savedSettings.floatingBottomOriginDp.first),
|
||||||
|
dp(savedSettings.floatingBottomOriginDp.second)
|
||||||
|
),
|
||||||
|
width = dp(savedSettings.floatingWidthDp),
|
||||||
|
height = recommendedHeightFloat.toInt(),
|
||||||
|
decorationPadding = dp(
|
||||||
|
Rect(
|
||||||
|
8,
|
||||||
|
8,
|
||||||
|
8,
|
||||||
|
8
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else ->
|
||||||
|
RegularKeyboardSize(
|
||||||
|
height = recommendedHeight.roundToInt(),
|
||||||
|
width = width,
|
||||||
|
padding = dp(savedSettings.paddingDp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,4 +410,14 @@ class KeyboardSizingCalculator(val context: Context) {
|
|||||||
|
|
||||||
return (minDp / 100.0f).coerceIn(3.0f, 6.0f)
|
return (minDp / 100.0f).coerceIn(3.0f, 6.0f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateSuggestionBarHeightDp(): Float {
|
||||||
|
return 40.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateTotalActionBarHeightPx(): Int =
|
||||||
|
when {
|
||||||
|
uixManager.actionsExpanded -> dp(2 * calculateSuggestionBarHeightDp())
|
||||||
|
else -> dp(calculateSuggestionBarHeightDp())
|
||||||
|
}
|
||||||
}
|
}
|
@ -81,10 +81,8 @@ data class LayoutRow(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class LayoutParams(
|
data class LayoutParams(
|
||||||
|
val size: ComputedKeyboardSize,
|
||||||
val gap: Dp,
|
val gap: Dp,
|
||||||
val useSplitLayout: Boolean,
|
|
||||||
val splitLayoutWidth: Int,
|
|
||||||
val padding: Rect,
|
|
||||||
val standardRowHeight: Double,
|
val standardRowHeight: Double,
|
||||||
val element: KeyboardLayoutElement,
|
val element: KeyboardLayoutElement,
|
||||||
)
|
)
|
||||||
@ -131,8 +129,6 @@ data class LayoutEngine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun computeRowHeight(): Double {
|
private fun computeRowHeight(): Double {
|
||||||
//val normalKeyboardHeight = ((rowHeight.value + verticalGap.value) * density) * 3
|
|
||||||
|
|
||||||
val normalKeyboardHeight = totalRowHeight
|
val normalKeyboardHeight = totalRowHeight
|
||||||
|
|
||||||
// divide by total row height
|
// divide by total row height
|
||||||
@ -140,19 +136,29 @@ data class LayoutEngine(
|
|||||||
BottomRowHeightMode.Fixed -> ((normalKeyboardHeight - layoutParams.standardRowHeight) / rows.filter { !it.isBottomRow }.sumOf { it.rowHeight })
|
BottomRowHeightMode.Fixed -> ((normalKeyboardHeight - layoutParams.standardRowHeight) / rows.filter { !it.isBottomRow }.sumOf { it.rowHeight })
|
||||||
BottomRowHeightMode.Flexible -> (normalKeyboardHeight) / rows.sumOf { it.rowHeight }
|
BottomRowHeightMode.Flexible -> (normalKeyboardHeight) / rows.sumOf { it.rowHeight }
|
||||||
}
|
}
|
||||||
//return ((normalKeyboardHeight - bottomRowHeightPx) / rows.filter { !it.isBottomRow }.sumOf { it.rowHeight })
|
|
||||||
//return (normalKeyboardHeight) / rows.sumOf { it.rowHeight }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val isSplitLayout = layoutParams.useSplitLayout
|
private val isSplitLayout = layoutParams.size is SplitKeyboardSize
|
||||||
|
private val isOneHandedLayout = layoutParams.size is OneHandedKeyboardSize
|
||||||
|
|
||||||
private val layoutWidth = if(isSplitLayout) {
|
private val layoutWidth = if(isSplitLayout) {
|
||||||
layoutParams.splitLayoutWidth
|
(layoutParams.size as SplitKeyboardSize).splitLayoutWidth
|
||||||
|
} else if(isOneHandedLayout) {
|
||||||
|
(layoutParams.size as OneHandedKeyboardSize).layoutWidth
|
||||||
} else {
|
} else {
|
||||||
params.mId.mWidth
|
params.mId.mWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
private val unsplitLayoutWidth = params.mId.mWidth
|
private val unsplitLayoutWidth = if(isSplitLayout) {
|
||||||
|
params.mId.mWidth
|
||||||
|
} else {
|
||||||
|
layoutWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove
|
||||||
|
private val padding = Rect(0, 0, 0, 0)
|
||||||
|
private val xOffset = 0
|
||||||
|
|
||||||
private val minimumBottomFunctionalKeyWidth = (layoutWidth * keyboard.minimumBottomRowFunctionalKeyWidth)
|
private val minimumBottomFunctionalKeyWidth = (layoutWidth * keyboard.minimumBottomRowFunctionalKeyWidth)
|
||||||
|
|
||||||
private val regularKeyWidth = computeRegularKeyWidth()
|
private val regularKeyWidth = computeRegularKeyWidth()
|
||||||
@ -551,10 +557,10 @@ data class LayoutEngine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addRowAlignLeft(row: List<LayoutEntry>, y: Int, height: Int)
|
private fun addRowAlignLeft(row: List<LayoutEntry>, y: Int, height: Int)
|
||||||
= addRow(row, 0.0f + layoutParams.padding.left, y, height)
|
= addRow(row, 0.0f + padding.left + xOffset, y, height)
|
||||||
|
|
||||||
private fun addRowAlignRight(row: List<LayoutEntry>, y: Int, height: Int) {
|
private fun addRowAlignRight(row: List<LayoutEntry>, y: Int, height: Int) {
|
||||||
val startingOffset = params.mId.mWidth - row.sumOf { it.widthPx.toDouble() }.toFloat() + layoutParams.padding.left
|
val startingOffset = params.mId.mWidth - row.sumOf { it.widthPx.toDouble() }.toFloat() + padding.left
|
||||||
addRow(row, startingOffset, y, height)
|
addRow(row, startingOffset, y, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,7 +576,7 @@ data class LayoutEngine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addKeys(rows: List<LayoutRow>): Int {
|
private fun addKeys(rows: List<LayoutRow>): Int {
|
||||||
var currentY = 0.0f + layoutParams.padding.top
|
var currentY = 0.0f + padding.top
|
||||||
rows.forEach { row ->
|
rows.forEach { row ->
|
||||||
addRow(row, currentY.toInt())
|
addRow(row, currentY.toInt())
|
||||||
currentY += row.height
|
currentY += row.height
|
||||||
@ -588,14 +594,14 @@ data class LayoutEngine(
|
|||||||
|
|
||||||
val rows = computeRows(this.rows)
|
val rows = computeRows(this.rows)
|
||||||
|
|
||||||
val totalKeyboardHeight = addKeys(rows).let { totalRowHeight.roundToInt() } + layoutParams.padding.top + layoutParams.padding.bottom
|
val totalKeyboardHeight = addKeys(rows).let { totalRowHeight.roundToInt() } + padding.top + padding.bottom
|
||||||
|
|
||||||
params.mOccupiedHeight = totalKeyboardHeight - verticalGapPx.roundToInt()
|
params.mOccupiedHeight = totalKeyboardHeight - verticalGapPx.roundToInt()
|
||||||
params.mOccupiedWidth = params.mId.mWidth + layoutParams.padding.left + layoutParams.padding.right
|
params.mOccupiedWidth = params.mId.mWidth + padding.left + padding.right
|
||||||
params.mTopPadding = 0//layoutParams.padding.top
|
params.mTopPadding = 0
|
||||||
params.mBottomPadding = 0//layoutParams.padding.bottom
|
params.mBottomPadding = 0
|
||||||
params.mLeftPadding = 0//layoutParams.padding.left
|
params.mLeftPadding = 0
|
||||||
params.mRightPadding = 0//layoutParams.padding.right
|
params.mRightPadding = 0
|
||||||
|
|
||||||
params.mBaseWidth = params.mOccupiedWidth
|
params.mBaseWidth = params.mOccupiedWidth
|
||||||
params.mDefaultKeyWidth = regularKeyWidth.roundToInt()
|
params.mDefaultKeyWidth = regularKeyWidth.roundToInt()
|
||||||
|
@ -33,7 +33,7 @@ object LayoutManager {
|
|||||||
|
|
||||||
private fun getAllLayoutPaths(assetManager: AssetManager): List<String> {
|
private fun getAllLayoutPaths(assetManager: AssetManager): List<String> {
|
||||||
return listFilesRecursively(assetManager, "layouts").filter {
|
return listFilesRecursively(assetManager, "layouts").filter {
|
||||||
(it.endsWith(".yml") || it.endsWith(".yaml")) && it != "mapping.yaml"
|
(it.endsWith(".yml") || it.endsWith(".yaml")) && it != "layouts/mapping.yaml"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,9 +43,7 @@ private fun symsForCoord(keyCoordinate: KeyCoordinate): String {
|
|||||||
if(centeredCol < 0) return ""
|
if(centeredCol < 0) return ""
|
||||||
|
|
||||||
val letter = row.getOrNull(centeredCol)
|
val letter = row.getOrNull(centeredCol)
|
||||||
if(letter == 'ñ') {
|
|
||||||
println("It's ñ")
|
|
||||||
}
|
|
||||||
return if(letter != null) {
|
return if(letter != null) {
|
||||||
"!text/qwertysyms_$letter"
|
"!text/qwertysyms_$letter"
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user