mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Add experimental swipe typing
This commit is contained in:
parent
854e1295cc
commit
4f15ff4a73
@ -18,5 +18,5 @@
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<bool name="config_gesture_input_enabled_by_build_config">false</bool>
|
||||
<bool name="config_gesture_input_enabled_by_build_config">true</bool>
|
||||
</resources>
|
||||
|
@ -162,7 +162,7 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
|
||||
// TODO: Make this parameter customizable by user via settings.
|
||||
private int mGestureFloatingPreviewTextLingerTimeout;
|
||||
|
||||
private final KeyDetector mKeyDetector;
|
||||
public final KeyDetector mKeyDetector;
|
||||
private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
|
||||
|
||||
private final TimerHandler mTimerHandler;
|
||||
|
@ -784,6 +784,8 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
||||
}
|
||||
|
||||
public boolean isImeSuppressedByHardwareKeyboard() {
|
||||
if(true) return false; // TODO: This function returning true causes some initialization issues
|
||||
|
||||
final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
|
||||
return !onEvaluateInputViewShown() && switcher.isImeSuppressedByHardwareKeyboard(
|
||||
mSettings.getCurrent(), switcher.getKeyboardSwitchState());
|
||||
|
@ -124,7 +124,7 @@ public final class Suggest {
|
||||
|| 0 != trailingSingleQuotesCount) {
|
||||
for (int i = 0; i < suggestionsCount; ++i) {
|
||||
final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
|
||||
final Locale wordLocale = wordInfo.mSourceDict.mLocale;
|
||||
final Locale wordLocale = (wordInfo.mSourceDict != null) ? wordInfo.mSourceDict.mLocale : null;
|
||||
final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
|
||||
wordInfo, null == wordLocale ? defaultLocale : wordLocale,
|
||||
shouldMakeSuggestionsAllUpperCase, isOnlyFirstCharCapitalized,
|
||||
@ -318,7 +318,7 @@ public final class Suggest {
|
||||
if (isFirstCharCapitalized || isAllUpperCase) {
|
||||
for (int i = 0; i < suggestionsCount; ++i) {
|
||||
final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
|
||||
final Locale wordlocale = wordInfo.mSourceDict.mLocale;
|
||||
final Locale wordlocale = (wordInfo.mSourceDict != null) ? wordInfo.mSourceDict.mLocale : null;
|
||||
final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
|
||||
wordInfo, null == wordlocale ? locale : wordlocale, isAllUpperCase,
|
||||
isFirstCharCapitalized, 0 /* trailingSingleQuotesCount */);
|
||||
@ -407,7 +407,7 @@ public final class Suggest {
|
||||
* @return whether it's fine to auto-correct to this.
|
||||
*/
|
||||
private static boolean isAllowedByAutoCorrectionWithSpaceFilter(final SuggestedWordInfo info) {
|
||||
final Locale locale = info.mSourceDict.mLocale;
|
||||
final Locale locale = (info.mSourceDict != null) ? info.mSourceDict.mLocale : null;
|
||||
if (null == locale) {
|
||||
return true;
|
||||
}
|
||||
|
@ -491,6 +491,11 @@ public final class InputLogic {
|
||||
return inputTransaction;
|
||||
}
|
||||
|
||||
public void showBatchSuggestions(final SuggestedWords suggestedWordsForBatchInput,
|
||||
final boolean isTailBatchInput) {
|
||||
mInputLogicHandler.showGestureSuggestionsWithPreviewVisuals(suggestedWordsForBatchInput, isTailBatchInput);
|
||||
}
|
||||
|
||||
public void onStartBatchInput(final SettingsValues settingsValues,
|
||||
final KeyboardSwitcher keyboardSwitcher, final LatinIMELegacy.UIHandler handler) {
|
||||
mWordBeingCorrectedByCursor = null;
|
||||
@ -560,7 +565,9 @@ public final class InputLogic {
|
||||
*/
|
||||
private int mAutoCommitSequenceNumber = 1;
|
||||
public void onUpdateBatchInput(final InputPointers batchPointers) {
|
||||
Log.d(TAG, "InputLogic has received batch input update, now we call for " + mInputLogicHandler.toString());
|
||||
mInputLogicHandler.onUpdateBatchInput(batchPointers, mAutoCommitSequenceNumber);
|
||||
Log.d(TAG, "Finished calling onUpddateBatchInput");
|
||||
}
|
||||
|
||||
public void onEndBatchInput(final InputPointers batchPointers) {
|
||||
|
@ -0,0 +1,63 @@
|
||||
package org.futo.inputmethod.latin.xlm
|
||||
|
||||
import org.futo.inputmethod.keyboard.KeyDetector
|
||||
import kotlin.math.sqrt
|
||||
|
||||
private fun normalize(pair: Pair<Int, Int>): Pair<Float, Float> {
|
||||
val magnitude = sqrt((pair.first * pair.first + pair.second * pair.second).toDouble())
|
||||
|
||||
if(magnitude == 0.0) {
|
||||
return Pair(Float.NaN, Float.NaN)
|
||||
}
|
||||
|
||||
return Pair((pair.first.toFloat() / magnitude).toFloat(), (pair.second.toFloat() / magnitude).toFloat())
|
||||
}
|
||||
|
||||
private fun dot(pair1: Pair<Float, Float>, pair2: Pair<Float, Float>): Float {
|
||||
return pair1.first * pair2.first + pair1.second * pair2.second
|
||||
}
|
||||
|
||||
object BatchInputConverter {
|
||||
fun convertToString(x: IntArray, y: IntArray, size: Int, keyDetector: KeyDetector): String {
|
||||
var coords = x.zip(y).toMutableList()
|
||||
|
||||
var s = ""
|
||||
for(i in 0 until size){
|
||||
if((i == 0) || (i == (size - 1))) {
|
||||
val key =
|
||||
keyDetector.detectHitKey(coords[i].first, coords[i].second)?.label ?: continue
|
||||
if(s.isNotEmpty() && s.last() == key.first()) continue
|
||||
s += key
|
||||
continue
|
||||
}
|
||||
|
||||
val currCoord = coords[i]
|
||||
val lastCoord = coords[i - 1]
|
||||
val nextCoord = coords[i + 1]
|
||||
|
||||
val directionFromLastCoord = normalize(Pair(currCoord.first - lastCoord.first, currCoord.second - lastCoord.second))
|
||||
val directionFromNextCoord = normalize(Pair(nextCoord.first - currCoord.first, nextCoord.second - currCoord.second))
|
||||
|
||||
if(directionFromLastCoord.first.isNaN() || directionFromLastCoord.second.isNaN()) continue
|
||||
if(directionFromNextCoord.first.isNaN() || directionFromNextCoord.second.isNaN()) continue
|
||||
|
||||
val dot = dot(directionFromLastCoord, directionFromNextCoord)
|
||||
|
||||
// TODO: Figure out a good threshold
|
||||
if(dot < 0.95) {
|
||||
val key =
|
||||
keyDetector.detectHitKey(coords[i].first, coords[i].second)?.label ?: continue
|
||||
if(s.isNotEmpty() && s.last() == key.first()) continue
|
||||
s += key
|
||||
//println("Adding $key, dot $dot, dirs $directionFromLastCoord $directionFromNextCoord, coords $lastCoord $currCoord $nextCoord")
|
||||
} else {
|
||||
// Simplify
|
||||
coords[i] = lastCoord
|
||||
}
|
||||
}
|
||||
|
||||
println("Transformed string: [$s]")
|
||||
|
||||
return s.lowercase()
|
||||
}
|
||||
}
|
@ -3,34 +3,24 @@ package org.futo.inputmethod.latin.xlm;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.futo.inputmethod.latin.Dictionary;
|
||||
import org.futo.inputmethod.keyboard.KeyDetector;
|
||||
import org.futo.inputmethod.latin.NgramContext;
|
||||
import org.futo.inputmethod.latin.R;
|
||||
import org.futo.inputmethod.latin.SuggestedWords;
|
||||
import org.futo.inputmethod.latin.common.ComposedData;
|
||||
import org.futo.inputmethod.latin.common.InputPointers;
|
||||
import org.futo.inputmethod.latin.settings.SettingsValuesForSuggestion;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.function.IntPredicate;
|
||||
|
||||
// TODO: Avoid loading the LanguageModel if the setting is disabled
|
||||
public class LanguageModel extends Dictionary {
|
||||
public class LanguageModel {
|
||||
static long mNativeState = 0;
|
||||
|
||||
Context context = null;
|
||||
Thread initThread = null;
|
||||
Locale locale = null;
|
||||
public LanguageModel(Context context, String dictType, Locale locale) {
|
||||
super(dictType, locale);
|
||||
|
||||
this.context = context;
|
||||
this.locale = locale;
|
||||
}
|
||||
@ -64,11 +54,10 @@ public class LanguageModel extends Dictionary {
|
||||
initThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<SuggestedWords.SuggestedWordInfo> getSuggestions(
|
||||
ComposedData composedData,
|
||||
NgramContext ngramContext,
|
||||
long proximityInfoHandle,
|
||||
KeyDetector keyDetector,
|
||||
SettingsValuesForSuggestion settingsValuesForSuggestion,
|
||||
int sessionId,
|
||||
float weightForLocale,
|
||||
@ -108,6 +97,16 @@ public class LanguageModel extends Dictionary {
|
||||
context = context.substring(0, context.length() - partialWord.length()).trim();
|
||||
}
|
||||
|
||||
if(isGesture) {
|
||||
// Partial word is gonna be derived from batch data
|
||||
partialWord = BatchInputConverter.INSTANCE.convertToString(
|
||||
composedData.mInputPointers.getXCoordinates(),
|
||||
composedData.mInputPointers.getYCoordinates(),
|
||||
inputSize,
|
||||
keyDetector
|
||||
);
|
||||
}
|
||||
|
||||
if(!partialWord.isEmpty()) {
|
||||
partialWord = partialWord.trim();
|
||||
}
|
||||
@ -160,7 +159,7 @@ public class LanguageModel extends Dictionary {
|
||||
String[] outStrings = new String[maxResults];
|
||||
|
||||
// TOOD: Pass multiple previous words information for n-gram.
|
||||
getSuggestionsNative(mNativeState, proximityInfoHandle, context, partialWord, xCoords, yCoords, outStrings, outProbabilities);
|
||||
getSuggestionsNative(mNativeState, 0L, context, partialWord, xCoords, yCoords, outStrings, outProbabilities);
|
||||
|
||||
final ArrayList<SuggestedWords.SuggestedWordInfo> suggestions = new ArrayList<>();
|
||||
|
||||
@ -197,7 +196,7 @@ public class LanguageModel extends Dictionary {
|
||||
currKind |= SuggestedWords.SuggestedWordInfo.KIND_FLAG_EXACT_MATCH;
|
||||
}
|
||||
|
||||
suggestions.add(new SuggestedWords.SuggestedWordInfo( word, context, (int)(outProbabilities[i] * 100.0f), currKind, this, 0, 0 ));
|
||||
suggestions.add(new SuggestedWords.SuggestedWordInfo( word, context, (int)(outProbabilities[i] * 100.0f), currKind, null, 0, 0 ));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -235,7 +234,6 @@ public class LanguageModel extends Dictionary {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
@ -245,13 +243,6 @@ public class LanguageModel extends Dictionary {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInDictionary(String word) {
|
||||
// TODO: Provide the word spelling to the model and see if the probability of correcting it to that is beyond a certain limit
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static native long openNative(String sourceDir);
|
||||
private static native void closeNative(long state);
|
||||
private static native void getSuggestionsNative(
|
||||
|
@ -1,119 +1,30 @@
|
||||
package org.futo.inputmethod.latin.xlm;
|
||||
|
||||
import android.content.ComponentCallbacks2
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.inputmethodservice.InputMethodService
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.CompletionInfo
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InlineSuggestion
|
||||
import android.view.inputmethod.InlineSuggestionsRequest
|
||||
import android.view.inputmethod.InlineSuggestionsResponse
|
||||
import android.view.inputmethod.InputMethodSubtype
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
import androidx.lifecycle.findViewTreeViewModelStoreOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||
import androidx.lifecycle.setViewTreeViewModelStoreOwner
|
||||
import androidx.savedstate.SavedStateRegistry
|
||||
import androidx.savedstate.SavedStateRegistryController
|
||||
import androidx.savedstate.SavedStateRegistryOwner
|
||||
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
|
||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||
import androidx.work.WorkManager
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.futo.inputmethod.latin.common.Constants
|
||||
import org.futo.inputmethod.latin.common.ComposedData
|
||||
import org.futo.inputmethod.latin.uix.Action
|
||||
import org.futo.inputmethod.latin.uix.ActionBar
|
||||
import org.futo.inputmethod.latin.uix.ActionInputTransaction
|
||||
import org.futo.inputmethod.latin.uix.ActionWindow
|
||||
import org.futo.inputmethod.latin.uix.BasicThemeProvider
|
||||
import org.futo.inputmethod.latin.uix.DynamicThemeProvider
|
||||
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner
|
||||
import org.futo.inputmethod.latin.uix.KeyboardManagerForAction
|
||||
import org.futo.inputmethod.latin.uix.PersistentActionState
|
||||
import org.futo.inputmethod.latin.uix.THEME_KEY
|
||||
import org.futo.inputmethod.latin.uix.actions.VoiceInputAction
|
||||
import org.futo.inputmethod.latin.uix.createInlineSuggestionsRequest
|
||||
import org.futo.inputmethod.latin.uix.deferGetSetting
|
||||
import org.futo.inputmethod.latin.uix.deferSetSetting
|
||||
import org.futo.inputmethod.latin.uix.differsFrom
|
||||
import org.futo.inputmethod.latin.uix.inflateInlineSuggestion
|
||||
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
|
||||
import org.futo.inputmethod.latin.uix.theme.ThemeOption
|
||||
import org.futo.inputmethod.latin.uix.theme.ThemeOptions
|
||||
import org.futo.inputmethod.latin.uix.theme.Typography
|
||||
import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
|
||||
import org.futo.inputmethod.latin.uix.theme.presets.ClassicMaterialDark
|
||||
import org.futo.inputmethod.latin.uix.theme.presets.DynamicSystemTheme
|
||||
import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme
|
||||
import org.futo.inputmethod.latin.settings.SettingsValues;
|
||||
import org.futo.inputmethod.latin.settings.SettingsValuesForSuggestion
|
||||
import org.futo.inputmethod.latin.settings.Settings
|
||||
import org.futo.inputmethod.latin.xlm.LanguageModel;
|
||||
import org.futo.inputmethod.latin.utils.SuggestionResults
|
||||
import org.futo.inputmethod.latin.NgramContext
|
||||
import org.futo.inputmethod.latin.LatinIMELegacy
|
||||
import org.futo.inputmethod.latin.inputlogic.InputLogic
|
||||
import org.futo.inputmethod.keyboard.KeyboardSwitcher
|
||||
import org.futo.inputmethod.latin.DictionaryFacilitator
|
||||
import org.futo.inputmethod.latin.NgramContext
|
||||
import org.futo.inputmethod.latin.Suggest
|
||||
import org.futo.inputmethod.latin.SuggestedWords
|
||||
import org.futo.inputmethod.keyboard.KeyboardSwitcher
|
||||
import org.futo.inputmethod.latin.common.ComposedData
|
||||
import org.futo.inputmethod.latin.inputlogic.InputLogic
|
||||
import org.futo.inputmethod.latin.settings.Settings
|
||||
import org.futo.inputmethod.latin.settings.SettingsValuesForSuggestion
|
||||
import org.futo.inputmethod.latin.utils.SuggestionResults
|
||||
|
||||
public class LanguageModelFacilitator(
|
||||
val context: Context,
|
||||
@ -186,7 +97,7 @@ public class LanguageModelFacilitator(
|
||||
val lmSuggestions = languageModel!!.getSuggestions(
|
||||
values.composedData,
|
||||
values.ngramContext,
|
||||
proximityInfoHandle,
|
||||
keyboardSwitcher.mainKeyboardView.mKeyDetector,
|
||||
settingsForPrediction,
|
||||
-1,
|
||||
0.0f,
|
||||
@ -209,6 +120,10 @@ public class LanguageModelFacilitator(
|
||||
|
||||
job.cancel()
|
||||
inputLogic.mSuggestionStripViewAccessor.showSuggestionStrip(suggestedWords)
|
||||
|
||||
if(values.composedData.mIsBatchMode) {
|
||||
inputLogic.showBatchSuggestions(suggestedWords, values.inputStyle == SuggestedWords.INPUT_STYLE_TAIL_BATCH);
|
||||
}
|
||||
sequenceIdFinishedFlow.emit(values.sequenceId)
|
||||
} finally {
|
||||
computationSemaphore.release()
|
||||
|
@ -436,7 +436,7 @@ namespace latinime {
|
||||
static void xlm_LanguageModel_getSuggestions(JNIEnv *env, jclass clazz,
|
||||
// inputs
|
||||
jlong dict,
|
||||
jlong proximityInfo,
|
||||
jlong _unused,
|
||||
jstring context,
|
||||
jstring partialWord,
|
||||
jfloatArray inComposeX,
|
||||
|
Loading…
Reference in New Issue
Block a user