Compare commits

..

2 Commits

Author SHA1 Message Date
Aleksandras Kostarevas
f795a7228e Fix width too long in landscape mode 2024-09-21 23:16:37 +03:00
Aleksandras Kostarevas
d692e7b96a Add KeyboardSizingCalculator 2024-09-21 19:31:34 +03:00
19 changed files with 355 additions and 89 deletions

View File

@ -227,6 +227,7 @@ dependencies {
implementation 'androidx.datastore:datastore-preferences:1.1.1'
implementation 'androidx.autofill:autofill:1.1.0'
implementation 'androidx.window:window:1.3.0'
stableImplementation 'ch.acra:acra-mail:5.11.1'
stableImplementation 'ch.acra:acra-dialog:5.11.1'

View File

@ -0,0 +1,11 @@
package org.futo.inputmethod.latin
import androidx.window.layout.FoldingFeature
data class FoldingOptions(
val feature: FoldingFeature?
)
interface FoldStateProvider {
val foldState: FoldingOptions
}

View File

@ -22,7 +22,7 @@
<resources>
<!-- Preferable keyboard height in absolute scale: 58.0mm -->
<!-- This config_default_keyboard_height value should match with keyboard-heights.xml -->
<dimen name="config_default_keyboard_height">365.4dp</dimen>
<dimen name="config_default_keyboard_height">302.4dp</dimen>
<fraction name="config_min_keyboard_height">35%p</fraction>
<fraction name="config_keyboard_top_padding_holo">1.896%p</fraction>

View File

@ -22,7 +22,7 @@
<resources>
<!-- Preferable keyboard height in absolute scale: 48.0mm -->
<!-- This config_default_keyboard_height value should match with keyboard-heights.xml -->
<dimen name="config_default_keyboard_height">302.4dp</dimen>
<dimen name="config_default_keyboard_height">365.4dp</dimen>
<fraction name="config_max_keyboard_height">46%p</fraction>
<fraction name="config_min_keyboard_height">-35.0%p</fraction>

View File

@ -16,6 +16,7 @@
package org.futo.inputmethod.keyboard;
import android.graphics.Rect;
import android.util.SparseArray;
import org.futo.inputmethod.keyboard.internal.KeyVisualAttributes;
@ -64,8 +65,8 @@ public class Keyboard {
/** Base width of the keyboard, used to calculate keys' width */
public final int mBaseWidth;
/** The padding above the keyboard */
public final int mTopPadding;
/** left, right, top, bottom specify respective padding */
public final Rect mPadding;
/** Default gap between rows */
public final int mVerticalGap;
@ -112,7 +113,7 @@ public class Keyboard {
mMoreKeysTemplate = params.mMoreKeysTemplate;
mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
mKeyVisualAttributes = params.mKeyVisualAttributes;
mTopPadding = params.mTopPadding;
mPadding = new Rect(params.mLeftPadding, params.mTopPadding, params.mRightPadding, params.mBottomPadding);
mVerticalGap = params.mVerticalGap;
mSortedKeys = Collections.unmodifiableList(new ArrayList<>(params.mSortedKeys));
@ -140,7 +141,7 @@ public class Keyboard {
mMoreKeysTemplate = keyboard.mMoreKeysTemplate;
mMaxMoreKeysKeyboardColumn = keyboard.mMaxMoreKeysKeyboardColumn;
mKeyVisualAttributes = keyboard.mKeyVisualAttributes;
mTopPadding = keyboard.mTopPadding;
mPadding = keyboard.mPadding;
mVerticalGap = keyboard.mVerticalGap;
mSortedKeys = keyboard.mSortedKeys;
@ -178,7 +179,6 @@ public class Keyboard {
/**
* Return the sorted list of keys of this keyboard.
* The keys are sorted from top-left to bottom-right order.
* The list may contain {@link Key.Spacer} object as well.
* @return the sorted unmodifiable list of {@link Key}s of this keyboard.
*/
@Nonnull

View File

@ -17,12 +17,13 @@
package org.futo.inputmethod.keyboard;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.inputmethod.EditorInfo;
import androidx.annotation.NonNull;
@ -46,8 +47,13 @@ import org.futo.inputmethod.latin.settings.SettingsValues;
import org.futo.inputmethod.latin.utils.LanguageOnSpacebarUtils;
import org.futo.inputmethod.latin.utils.ResourceUtils;
import org.futo.inputmethod.latin.utils.ScriptUtils;
import org.futo.inputmethod.v2keyboard.ComputedKeyboardSize;
import org.futo.inputmethod.v2keyboard.KeyboardLayoutSetKt;
import org.futo.inputmethod.v2keyboard.KeyboardLayoutSetV2;
import org.futo.inputmethod.v2keyboard.KeyboardLayoutSetV2Params;
import org.futo.inputmethod.v2keyboard.KeyboardSizingCalculator;
import org.futo.inputmethod.v2keyboard.RegularKeyboardSize;
import org.futo.inputmethod.v2keyboard.SplitKeyboardSize;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -124,18 +130,50 @@ public final class KeyboardSwitcher implements SwitchActions {
final int currentAutoCapsState, final int currentRecapitalizeState) {
final Resources res = mThemeContext.getResources();
final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
final RichInputMethodSubtype subtype = mRichImm.getCurrentSubtype();
String layoutSetName = subtype.getKeyboardLayoutSetName();
String overrideLayoutSet = KeyboardLayoutSetKt.getPrimaryLayoutOverride(editorInfo);
if(overrideLayoutSet != null) {
layoutSetName = overrideLayoutSet;
}
final KeyboardSizingCalculator sizingCalculator = new KeyboardSizingCalculator(mLatinIMELegacy.getInputMethodService());
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(
keyboardWidth,
null, // Auto keyboard height
subtype.getKeyboardLayoutSetName(),
keyboardHeight,
padding,
layoutSetName,
subtype.getLocale(),
editorInfo == null ? new EditorInfo() : editorInfo,
settingsValues.mIsNumberRowEnabled,
4.0f,
res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE,
sizingCalculator.calculateGap(),
splitLayoutWidth != 0,
splitLayoutWidth,
settingsValues.mShowsActionKey ? settingsValues.mActionKeyId : null,
LongPressKeySettings.load(mThemeContext)
);
@ -176,7 +214,6 @@ public final class KeyboardSwitcher implements SwitchActions {
final Keyboard oldKeyboard = keyboardView.getKeyboard();
final Keyboard newKeyboard = mKeyboardLayoutSet.getKeyboard(element);
keyboardView.setKeyboard(newKeyboard);
mCurrentInputView.setKeyboardTopPadding(newKeyboard.mTopPadding);
keyboardView.setKeyPreviewPopupEnabled(
currentSettingsValues.mKeyPreviewPopupOn,
currentSettingsValues.mKeyPreviewPopupDismissDelay);

View File

@ -401,6 +401,8 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
} else {
mAccessibilityDelegate = null;
}
setPadding(keyboard.mPadding.left, keyboard.mPadding.top, keyboard.mPadding.right, keyboard.mPadding.bottom);
}
/**

View File

@ -41,10 +41,6 @@ public final class InputView extends FrameLayout {
mMainKeyboardView = (MainKeyboardView) findViewById(R.id.keyboard_view);
}
public void setKeyboardTopPadding(final int keyboardTopPadding) {
}
@Override
protected boolean dispatchHoverEvent(final MotionEvent event) {
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()

View File

@ -19,10 +19,8 @@ import android.view.inputmethod.InlineSuggestionsResponse
import android.view.inputmethod.InputConnection
import android.view.inputmethod.InputMethodSubtype
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
@ -58,7 +56,6 @@ import org.futo.inputmethod.latin.uix.DynamicThemeProvider
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner
import org.futo.inputmethod.latin.uix.EmojiTracker.unuseEmoji
import org.futo.inputmethod.latin.uix.EmojiTracker.useEmoji
import org.futo.inputmethod.latin.uix.KeyboardBottomOffsetSetting
import org.futo.inputmethod.latin.uix.KeyboardColorScheme
import org.futo.inputmethod.latin.uix.SUGGESTION_BLACKLIST
import org.futo.inputmethod.latin.uix.THEME_KEY
@ -70,7 +67,6 @@ import org.futo.inputmethod.latin.uix.deferSetSetting
import org.futo.inputmethod.latin.uix.differsFrom
import org.futo.inputmethod.latin.uix.getSetting
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.getSettingFlow
import org.futo.inputmethod.latin.uix.isDirectBootUnlocked
import org.futo.inputmethod.latin.uix.setSetting
import org.futo.inputmethod.latin.uix.theme.ThemeOption
@ -79,6 +75,8 @@ import org.futo.inputmethod.latin.uix.theme.applyWindowColors
import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme
import org.futo.inputmethod.latin.xlm.LanguageModelFacilitator
import org.futo.inputmethod.updates.scheduleUpdateCheckingJob
import org.futo.inputmethod.v2keyboard.KeyboardSizeSettingKind
import org.futo.inputmethod.v2keyboard.KeyboardSizeStateProvider
private class UnlockedBroadcastReceiver(val onDeviceUnlocked: () -> Unit) : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
@ -90,7 +88,8 @@ private class UnlockedBroadcastReceiver(val onDeviceUnlocked: () -> Unit) : Broa
}
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner,
LatinIMELegacy.SuggestionStripController, DynamicThemeProviderOwner {
LatinIMELegacy.SuggestionStripController, DynamicThemeProviderOwner, FoldStateProvider,
KeyboardSizeStateProvider {
private lateinit var mLifecycleRegistry: LifecycleRegistry
private lateinit var mViewModelStore: ViewModelStore
@ -205,7 +204,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
}
private fun invalidateKeyboard(refreshSettings: Boolean = false) {
fun invalidateKeyboard(refreshSettings: Boolean = false) {
settingsRefreshRequired = settingsRefreshRequired || refreshSettings
if(!uixManager.isMainKeyboardHidden) {
@ -427,12 +426,12 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
}
val padding = getSettingFlow(KeyboardBottomOffsetSetting).collectAsState(initial = 0.0f)
key(legacyInputView) {
AndroidView(factory = {
legacyInputView!!
}, modifier = modifier.padding(0.dp, 0.dp, 0.dp, padding.value.dp), onRelease = {
}, modifier = modifier, onRelease = {
val view = it as InputView
view.deallocateMemory()
view.removeAllViews()
@ -762,4 +761,19 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
// TODO: Spell checker service
}
override val foldState: FoldingOptions
get() = uixManager.foldingOptions.value
override val currentSizeState: KeyboardSizeSettingKind
get() = when {
foldState.feature != null ->
KeyboardSizeSettingKind.FoldableInnerDisplay
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE ->
KeyboardSizeSettingKind.Landscape
else ->
KeyboardSizeSettingKind.Portrait
}
}

View File

@ -1,6 +1,6 @@
package org.futo.inputmethod.latin.uix
import android.content.res.Configuration
import android.graphics.Rect
import android.view.ContextThemeWrapper
import android.view.inputmethod.EditorInfo
import androidx.compose.foundation.layout.Box
@ -109,12 +109,14 @@ fun KeyboardLayoutPreview(id: String, width: Dp = 172.dp, locale: Locale? = null
KeyboardLayoutSetV2Params(
width = widthPx,
height = heightPx,
padding = Rect(),
gap = 4.0f,
keyboardLayoutSet = id,
locale = loc ?: Locale.ENGLISH,
editorInfo = editorInfo,
numberRow = numberRow,
useSplitLayout = isLandscape,
splitLayoutWidth = widthPx * 2 / 3,
bottomActionKey = null
)
)

View File

@ -1,7 +1,6 @@
package org.futo.inputmethod.latin.uix
import android.app.Activity
import android.app.KeyguardManager
import android.content.ClipDescription
import android.content.Context
import android.content.Intent
@ -62,11 +61,15 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.lifecycleScope
import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.futo.inputmethod.accessibility.AccessibilityUtils
import org.futo.inputmethod.latin.AudioAndHapticFeedbackManager
import org.futo.inputmethod.latin.BuildConfig
import org.futo.inputmethod.latin.FoldingOptions
import org.futo.inputmethod.latin.LanguageSwitcherDialog
import org.futo.inputmethod.latin.LatinIME
import org.futo.inputmethod.latin.R
@ -103,6 +106,12 @@ val LocalThemeProvider = compositionLocalOf<DynamicThemeProvider> {
error("No LocalThemeProvider provided")
}
val LocalFoldingState = compositionLocalOf<FoldingOptions> {
FoldingOptions(null)
}
private class LatinIMEActionInputTransaction(
private val inputLogic: InputLogic,
shouldApplySpace: Boolean,
@ -323,6 +332,8 @@ class UixManager(private val latinIME: LatinIME) {
isShowingActionEditor.value = true
}
val foldingOptions = mutableStateOf(FoldingOptions(null))
var isInputOverridden = mutableStateOf(false)
var currWindowActionWindow: ActionWindow? = null
@ -622,35 +633,39 @@ class UixManager(private val latinIME: LatinIME) {
CompositionLocalProvider(LocalManager provides keyboardManagerForAction) {
CompositionLocalProvider(LocalThemeProvider provides latinIME.getDrawableProvider()) {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
InputDarkener(isInputOverridden.value || isShowingActionEditor.value) {
closeActionWindow()
isShowingActionEditor.value = false
}
CompositionLocalProvider(LocalFoldingState provides foldingOptions.value) {
InputDarkener(isInputOverridden.value || isShowingActionEditor.value) {
closeActionWindow()
isShowingActionEditor.value = false
}
Column {
Spacer(modifier = Modifier.weight(1.0f))
Surface(modifier = Modifier.onSizeChanged {
latinIME.updateTouchableHeight(it.height)
}, color = latinIME.keyboardColor) {
Box {
Column {
when {
currWindowActionWindow != null -> ActionViewWithHeader(
currWindowActionWindow!!
)
Column {
Spacer(modifier = Modifier.weight(1.0f))
Surface(modifier = Modifier.onSizeChanged {
latinIME.updateTouchableHeight(it.height)
}, color = latinIME.keyboardColor) {
Box {
Column {
when {
currWindowActionWindow != null -> ActionViewWithHeader(
currWindowActionWindow!!
)
else -> MainKeyboardViewWithActionBar()
else -> MainKeyboardViewWithActionBar()
}
latinIME.LegacyKeyboardView(hidden = isMainKeyboardHidden)
}
latinIME.LegacyKeyboardView(hidden = isMainKeyboardHidden)
ForgetWordDialog()
}
ForgetWordDialog()
}
}
}
ActionEditorHost()
}
ActionEditorHost()
}
}
}
}
@ -852,6 +867,13 @@ class UixManager(private val latinIME: LatinIME) {
}
isActionsExpanded.value = latinIME.getSettingBlocking(ActionBarExpanded)
latinIME.lifecycleScope.launch(Dispatchers.Main) {
WindowInfoTracker.getOrCreate(latinIME).windowLayoutInfo(latinIME).collect {
foldingOptions.value = FoldingOptions(it.displayFeatures.filterIsInstance<FoldingFeature>().firstOrNull())
latinIME.invalidateKeyboard(true)
}
}
}
fun onPersistentStatesUnlocked() {

View File

@ -22,10 +22,11 @@ import kotlinx.coroutines.delay
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.Action
import org.futo.inputmethod.latin.uix.ActionWindow
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
import org.futo.inputmethod.latin.uix.LocalFoldingState
import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.theme.ThemeOptions
import org.futo.inputmethod.latin.uix.theme.Typography
import org.futo.inputmethod.v2keyboard.KeyboardSizeStateProvider
val DebugLabel = Typography.labelSmall.copy(fontFamily = FontFamily.Monospace)
val DebugTitle = Typography.titleSmall.copy(fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold)
@ -190,6 +191,8 @@ val MemoryDebugAction = Action(
}
}
val foldingState = LocalFoldingState.current
ScrollableList {
Text("Editor Info", style = DebugTitle)
latinIme.currentInputEditorInfo?.let { info ->
@ -228,6 +231,16 @@ val MemoryDebugAction = Action(
Spacer(modifier = Modifier.height(8.dp))
Text("Screen State Info", style = DebugTitle)
Text("size mode = ${(manager.getContext() as KeyboardSizeStateProvider).currentSizeState}", style = DebugLabel)
Text("Fold State", style = DebugTitle)
Text("state = ${foldingState.feature?.state}", style = DebugLabel)
Text("orientation = ${foldingState.feature?.orientation}", style = DebugLabel)
Text("isSeparating = ${foldingState.feature?.isSeparating}", style = DebugLabel)
Text("occlusionType = ${foldingState.feature?.occlusionType}", style = DebugLabel)
Spacer(modifier = Modifier.height(8.dp))
Text("Memory Use", style = DebugTitle)
state.value.forEach {
val value = it.value.toInt().toFloat() / 1000.0f

View File

@ -18,11 +18,15 @@ package org.futo.inputmethod.latin.utils;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Insets;
import android.os.Build;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowMetrics;
import org.futo.inputmethod.annotations.UsedForTesting;
import org.futo.inputmethod.latin.R;
@ -182,9 +186,14 @@ public final class ResourceUtils {
return matchedAll;
}
public static int getDefaultKeyboardWidth(final Resources res) {
final DisplayMetrics dm = res.getDisplayMetrics();
return dm.widthPixels;
public static int getDefaultKeyboardWidth(final Window window, final Resources res) {
if(window != null && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)) {
WindowMetrics metrics = window.getWindowManager().getCurrentWindowMetrics();
Insets insets = metrics.getWindowInsets().getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout());
return metrics.getBounds().width() - (insets.left + insets.right);
}
return (int)(res.getConfiguration().screenWidthDp * res.getDisplayMetrics().density);
}
public static int getKeyboardHeight(final Resources res, final SettingsValues settingsValues) {

View File

@ -1,6 +1,7 @@
package org.futo.inputmethod.v2keyboard
import android.content.Context
import android.graphics.Rect
import android.text.InputType
import android.util.Log
import android.view.inputmethod.EditorInfo
@ -20,7 +21,6 @@ import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.utils.InputTypeUtils
import org.futo.inputmethod.latin.utils.ResourceUtils
import java.util.Locale
import kotlin.math.roundToInt
@Serializable
enum class Script(val id: Int, val iso4letterCode: String) {
@ -71,24 +71,25 @@ private fun EditorInfo.getPrivateImeOptions(): Map<String, String> {
return options
}
private fun EditorInfo.getPrimaryLayoutOverride(): String? =
getPrivateImeOptions()["org.futo.inputmethod.latin.ForceLayout"]
fun getPrimaryLayoutOverride(editorInfo: EditorInfo?): String? {
return editorInfo?.getPrivateImeOptions()?.get("org.futo.inputmethod.latin.ForceLayout")
}
data class KeyboardLayoutSetV2Params(
val width: Int,
val height: Int?,
val height: Int,
val padding: Rect,
val keyboardLayoutSet: String,
val locale: Locale,
val editorInfo: EditorInfo?,
val numberRow: Boolean,
val gap: Float = 4.0f,
val useSplitLayout: Boolean,
val splitLayoutWidth: Int,
val bottomActionKey: Int?,
val longPressKeySettings: LongPressKeySettings? = null
)
class KeyboardLayoutSetV2 internal constructor(
private val context: Context,
private val params: KeyboardLayoutSetV2Params
@ -183,31 +184,22 @@ class KeyboardLayoutSetV2 internal constructor(
NumberRowMode.AlwaysDisabled -> false
}
private val widthMinusPadding = params.width - params.padding.left - params.padding.right
private val heightMinusPadding = params.height - params.padding.top - params.padding.bottom
private val singularRowHeight: Double
get() = params.height?.let { it / 4.0 } ?: run {
get() = heightMinusPadding?.let { it / 4.0 } ?: run {
(ResourceUtils.getDefaultKeyboardHeight(context.resources) / 4.0) *
keyboardHeightMultiplier
}
private fun getRecommendedKeyboardHeight(): Int {
val numRows = 4.0 +
((mainLayout.effectiveRows.size - 5) / 2.0).coerceAtLeast(0.0) +
if(isNumberRowActive) { 0.5 } else { 0.0 }
// Clamp if necessary (disabled for now)
if(false && params.height == null) {
return ResourceUtils.clampKeyboardHeight(context.resources, (singularRowHeight * numRows).roundToInt())
} else {
return (singularRowHeight * numRows).roundToInt()
}
}
fun getKeyboard(element: KeyboardLayoutElement): Keyboard {
val keyboardId = KeyboardId(
params.keyboardLayoutSet,
forcedLocale ?: params.locale,
params.width,
params.height ?: getRecommendedKeyboardHeight(),
widthMinusPadding,
heightMinusPadding,
keyboardMode,
element.elementId,
editorInfo,
@ -231,6 +223,8 @@ class KeyboardLayoutSetV2 internal constructor(
val layoutParams = LayoutParams(
gap = params.gap.dp,
useSplitLayout = params.useSplitLayout,
splitLayoutWidth = params.splitLayoutWidth,
padding = params.padding,
standardRowHeight = singularRowHeight,
element = element
)

View File

@ -0,0 +1,158 @@
package org.futo.inputmethod.v2keyboard
import android.content.Context
import android.graphics.Rect
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.window.layout.FoldingFeature
import org.futo.inputmethod.latin.FoldStateProvider
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.utils.ResourceUtils
import kotlin.math.roundToInt
interface KeyboardSizeStateProvider {
val currentSizeState: KeyboardSizeSettingKind
}
sealed class ComputedKeyboardSize()
class RegularKeyboardSize(val height: Int, val padding: Rect) : ComputedKeyboardSize()
class SplitKeyboardSize(val height: 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()
//class FloatingKeyboardSize(val x: Int, val y: Int, val width: Int, val height: Int): ComputedKeyboardSize()
enum class KeyboardSizeSettingKind {
Portrait,
Landscape,
FoldableInnerDisplay
}
val SplitKeyboardSettings = mapOf(
KeyboardSizeSettingKind.Portrait to SettingsKey(
booleanPreferencesKey("split_keyboard_portrait"), false),
KeyboardSizeSettingKind.Landscape to SettingsKey(
booleanPreferencesKey("split_keyboard_landscape"), true),
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
booleanPreferencesKey("split_keyboard_fold"), true),
)
val KeyboardHeightSettings = mapOf(
KeyboardSizeSettingKind.Portrait to SettingsKey(
floatPreferencesKey("keyboardHeightMultiplier"), 1.0f),
KeyboardSizeSettingKind.Landscape to SettingsKey(
floatPreferencesKey("keyboard_height_landscape"), 0.9f),
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
floatPreferencesKey("keyboard_height_fold"), 0.67f),
)
val KeyboardOffsetSettings = mapOf(
KeyboardSizeSettingKind.Portrait to SettingsKey(
floatPreferencesKey("keyboard_offset_portrait"), 8.0f),
KeyboardSizeSettingKind.Landscape to SettingsKey(
floatPreferencesKey("keyboard_offset_landscape"), 0.0f),
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
floatPreferencesKey("keyboard_offset_fold"), 8.0f),
)
val KeyboardSideInsetSettings = mapOf(
KeyboardSizeSettingKind.Portrait to SettingsKey(
floatPreferencesKey("keyboard_inset_portrait"), 2.0f),
KeyboardSizeSettingKind.Landscape to SettingsKey(
floatPreferencesKey("keyboard_inset_landscape"), 8.0f),
KeyboardSizeSettingKind.FoldableInnerDisplay to SettingsKey(
floatPreferencesKey("keyboard_inset_fold"), 44.0f),
)
class KeyboardSizingCalculator(val context: Context) {
private val sizeStateProvider = context as KeyboardSizeStateProvider
private val foldStateProvider = context as FoldStateProvider
private fun isSplitKeyboard(mode: KeyboardSizeSettingKind): Boolean =
context.getSettingBlocking(SplitKeyboardSettings[mode]!!)
private fun heightMultiplier(mode: KeyboardSizeSettingKind): Float =
context.getSettingBlocking(KeyboardHeightSettings[mode]!!)
private fun bottomOffsetPx(mode: KeyboardSizeSettingKind): Int =
(context.getSettingBlocking(KeyboardOffsetSettings[mode]!!) * context.resources.displayMetrics.density).toInt()
private fun sideInsetPx(mode: KeyboardSizeSettingKind): Int =
(context.getSettingBlocking(KeyboardSideInsetSettings[mode]!!) * context.resources.displayMetrics.density).toInt()
private fun topPaddingPx(mode: KeyboardSizeSettingKind): Int =
(when(mode) {
KeyboardSizeSettingKind.Portrait -> 4.0f
KeyboardSizeSettingKind.Landscape -> 0.0f
KeyboardSizeSettingKind.FoldableInnerDisplay -> 8.0f
} * context.resources.displayMetrics.density).toInt()
fun calculate(layoutName: String, isNumberRowActive: Boolean): ComputedKeyboardSize {
val layout = LayoutManager.getLayout(context, layoutName)
val effectiveRowCount = layout.effectiveRows.size
val configuration = context.resources.configuration
val displayMetrics = context.resources.displayMetrics
val mode = sizeStateProvider.currentSizeState
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 +
((effectiveRowCount - 5) / 2.0).coerceAtLeast(0.0) +
if(isNumberRowActive) { 0.5 } else { 0.0 }
println("Num rows; $numRows, $effectiveRowCount ($layoutName) ($layout)")
val recommendedHeight = numRows * singularRowHeight
val foldState = foldStateProvider.foldState.feature
return when {
// 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 ->
SplitKeyboardSize(
displayMetrics.heightPixels / 2 - (displayMetrics.density * 80.0f).toInt(),
Rect(
(displayMetrics.density * 44.0f).roundToInt(),
(displayMetrics.density * 20.0f).roundToInt(),
(displayMetrics.density * 44.0f).roundToInt(),
(displayMetrics.density * 12.0f).roundToInt(),
),
displayMetrics.widthPixels * 3 / 5
)
isSplit -> SplitKeyboardSize(
recommendedHeight.roundToInt(),
Rect(sideInset, topPadding, sideInset, bottomOffset),
displayMetrics.widthPixels * 3 / 5)
else -> RegularKeyboardSize(
recommendedHeight.roundToInt(),
Rect(sideInset, topPadding, sideInset, bottomOffset),
)
}
}
fun calculateGap(): Float {
val displayMetrics = context.resources.displayMetrics
val widthDp = displayMetrics.widthPixels / displayMetrics.density
val heightDp = displayMetrics.heightPixels / displayMetrics.density
val minDp = Math.min(widthDp, heightDp)
return (minDp / 100.0f).coerceIn(3.0f, 6.0f)
}
}

View File

@ -1,6 +1,7 @@
package org.futo.inputmethod.v2keyboard
import android.content.Context
import android.graphics.Rect
import androidx.compose.ui.unit.Dp
import org.futo.inputmethod.keyboard.KeyConsts
import org.futo.inputmethod.keyboard.internal.KeyboardLayoutElement
@ -82,6 +83,8 @@ data class LayoutRow(
data class LayoutParams(
val gap: Dp,
val useSplitLayout: Boolean,
val splitLayoutWidth: Int,
val padding: Rect,
val standardRowHeight: Double,
val element: KeyboardLayoutElement,
)
@ -144,7 +147,7 @@ data class LayoutEngine(
private val isSplitLayout = layoutParams.useSplitLayout
private val layoutWidth = if(isSplitLayout) {
params.mId.mWidth * 2 / 3
layoutParams.splitLayoutWidth
} else {
params.mId.mWidth
}
@ -526,10 +529,10 @@ data class LayoutEngine(
}
private fun addRowAlignLeft(row: List<LayoutEntry>, y: Int, height: Int)
= addRow(row, 0.0f, y, height)
= addRow(row, 0.0f + layoutParams.padding.left, y, height)
private fun addRowAlignRight(row: List<LayoutEntry>, y: Int, height: Int) {
val startingOffset = params.mId.mWidth - row.sumOf { it.widthPx.toDouble() }.toFloat()
val startingOffset = params.mId.mWidth - row.sumOf { it.widthPx.toDouble() }.toFloat() + layoutParams.padding.left
addRow(row, startingOffset, y, height)
}
@ -545,7 +548,7 @@ data class LayoutEngine(
}
private fun addKeys(rows: List<LayoutRow>): Int {
var currentY = 0.0f
var currentY = 0.0f + layoutParams.padding.top
rows.forEach { row ->
addRow(row, currentY.toInt())
currentY += row.height
@ -563,14 +566,14 @@ data class LayoutEngine(
val rows = computeRows(this.rows)
val totalKeyboardHeight = addKeys(rows).let { totalRowHeight.roundToInt() }
val totalKeyboardHeight = addKeys(rows).let { totalRowHeight.roundToInt() } + layoutParams.padding.top + layoutParams.padding.bottom
params.mOccupiedHeight = totalKeyboardHeight - verticalGapPx.roundToInt()
params.mOccupiedWidth = params.mId.mWidth
params.mTopPadding = 0
params.mBottomPadding = 0
params.mLeftPadding = 0
params.mRightPadding = 0
params.mOccupiedWidth = params.mId.mWidth + layoutParams.padding.left + layoutParams.padding.right
params.mTopPadding = 0//layoutParams.padding.top
params.mBottomPadding = 0//layoutParams.padding.bottom
params.mLeftPadding = 0//layoutParams.padding.left
params.mRightPadding = 0//layoutParams.padding.right
params.mBaseWidth = params.mOccupiedWidth
params.mDefaultKeyWidth = regularKeyWidth.roundToInt()

View File

@ -18,6 +18,7 @@ package org.futo.inputmethod.keyboard;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.test.AndroidTestCase;
import android.view.ContextThemeWrapper;
import android.view.inputmethod.EditorInfo;
@ -142,7 +143,7 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase {
final boolean languageSwitchKeyEnabled, final boolean splitLayoutEnabled) {
final Context context = getContext();
final Resources res = context.getResources();
final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(null, res);
final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
final RichInputMethodSubtype richInputMethodSubtype = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
@ -150,11 +151,11 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase {
return new KeyboardLayoutSetV2(
context,
new KeyboardLayoutSetV2Params(
keyboardWidth, keyboardHeight,
keyboardWidth, keyboardHeight, new Rect(),
richInputMethodSubtype.getKeyboardLayoutSetName(),
richInputMethodSubtype.getLocale(),
editorInfo, false,
4.0f, splitLayoutEnabled,
4.0f, splitLayoutEnabled, 0,
languageSwitchKeyEnabled ? ActionRegistry.INSTANCE.actionStringIdToIdx("switch_language") : null,
LongPressKeySettings.forTest()
)

View File

@ -1,5 +1,6 @@
package org.futo.inputmethod.keyboard.internal
import android.graphics.Rect
import android.test.AndroidTestCase
import android.view.inputmethod.EditorInfo
import androidx.datastore.preferences.core.edit
@ -14,12 +15,14 @@ import kotlin.math.absoluteValue
class KeyboardLayoutSetV2Tests : AndroidTestCase() {
private val layoutParams = KeyboardLayoutSetV2Params(
width = 1024,
height = null,
height = 1024,
padding = Rect(),
keyboardLayoutSet = "qwerty",
locale = Locale.ENGLISH,
editorInfo = EditorInfo(),
numberRow = false,
useSplitLayout = false,
splitLayoutWidth = 0,
bottomActionKey = null
)