Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
d3671be
Merge branch 'zh' into 'master'
abb128 Apr 6, 2026
1aa79ec
Implement clearUserHistoryDictionaries for ChineseIME
abb128 Apr 6, 2026
9ee4e34
Add KeyboardTextsTable.java to gitignore
abb128 Apr 6, 2026
f59e18c
Implement EmailDictionary
abb128 Apr 6, 2026
cffd7b8
Add setting to skip clipboard delete confirmation
abb128 Apr 6, 2026
2a3eadd
Add KoashurCombiner
FarhanSyedain Apr 6, 2026
c5face1
Enable predictive back gesture
tedgravlin Apr 6, 2026
8557748
Dismiss quick clip when corresponding clipboard entry is removed
abb128 Apr 6, 2026
7d88dd4
Haptic feedback on morekeys popup
abb128 Apr 6, 2026
9413b8e
Add ɜ to Italian texts
Sbax Apr 6, 2026
153ce08
Save images to clipboard history
abb128 Apr 7, 2026
423b3b9
Update German dictionary
abb128 Apr 13, 2026
39bef9c
Rename .java to .kt
abb128 Apr 13, 2026
ff9445b
Rewrite SpacingAndPunctuations to use assets
abb128 Apr 13, 2026
2e6701e
Update rime to v1.16.1
abb128 Apr 14, 2026
aeee21c
Implement settings export for RIME
abb128 Apr 14, 2026
a992662
Fix issue configuring RIME
abb128 Apr 14, 2026
f8344a3
Display a loading indicator when RIME is deploying
abb128 Apr 14, 2026
e20fd24
Update navigation, improve personal dictionary errors
abb128 Apr 16, 2026
5059d94
Fix Japanese language switch key issue
abb128 Apr 16, 2026
b823ea9
Update navigation-compose and compose-bom to resolve a recomposition …
abb128 Apr 16, 2026
aeab92d
Force recreate UserBinaryDictionaries on service restart
abb128 Apr 16, 2026
caf39b6
Fix incorrect order of applying automatic space
abb128 Apr 16, 2026
75137a7
Check the save images setting before saving images to clipboard
abb128 Apr 23, 2026
4b03f01
Enable autocaps on text edit popup
abb128 Apr 23, 2026
d9e2c9e
Improve typing i.e. and e.g.
abb128 Apr 23, 2026
a319781
Backport fixes from swipe branch
abb128 Apr 23, 2026
8934449
Update contributors
abb128 Apr 23, 2026
2daced5
Update IPA layout names, disable the never-finished "ipa" layout
abb128 Apr 23, 2026
c9b3fa0
Add Vietnamese Telex and VNI combiners
tenextractor Apr 23, 2026
d12f756
Do not autocorrect words touching a number
abb128 Apr 23, 2026
6b84410
Automatically scale language on spacebar to fit
abb128 Apr 23, 2026
eb38768
Add haptics when dragging cursor with spacebar
abb128 Apr 23, 2026
ad201f9
Do not keep producing delete feedback when there's nothing left to de…
abb128 Apr 23, 2026
2d86405
Don't crash on overly wide morekeys keyboard
abb128 Apr 23, 2026
773b3b2
Update contributors.kt
abb128 Apr 23, 2026
729e856
Don't crash app when unknown keycode is entered (broken custom layouts)
abb128 Apr 23, 2026
45daa0d
Replace use of removeLast with removeAt
abb128 Apr 23, 2026
c3e640f
Add option to toggle whether keyboard is shown when USB keyboard is p…
abb128 Apr 23, 2026
83671b3
Replace uses of context.getString with resources.getString in composa…
abb128 Apr 23, 2026
afb8e6f
Remove obsolete resources and suppress error
abb128 Apr 23, 2026
b51f4c4
Fix a crash when importing backup
abb128 Apr 23, 2026
541bdb8
Implement screenshots in clipboard history
abb128 Apr 28, 2026
de88509
Fix Hinglish keyboard texts
abb128 Apr 30, 2026
ddbfd8a
Update hi__Latn texts
abb128 Apr 30, 2026
a4b31a7
Use AtomicFile for managing clipboard file
abb128 May 2, 2026
df9dfd0
Unregister ScreenshotHelper on destroy
abb128 May 2, 2026
b099856
Fix a misinteraction between .bak file and AtomicFile
abb128 May 2, 2026
d25e64b
Fix inset check
abb128 May 2, 2026
c06870a
Fix search menu constantly recreating text field
abb128 May 5, 2026
9195258
Send KEYCODE_ENTER for enter in non-text fields
abb128 May 6, 2026
939df16
Fix inverted boolean expression
abb128 May 6, 2026
ff8006d
Update screenshots matching rule
abb128 May 8, 2026
cd46ec7
Add haptic feedback for quick clip pasting
abb128 May 19, 2026
0a82c76
Fix voice input close deadlock
abb128 May 19, 2026
6098c74
Add an option to disable voice input animations
abb128 May 19, 2026
b5e2813
Disable composing in non-text fields (#1978)
abb128 May 19, 2026
82a6e87
Mark Haven as a code field (#1978)
abb128 May 19, 2026
5c79114
Add Clipboard History Search Feature (#1935)
lakshaysethi May 19, 2026
17732e3
Enable autofocus for ActionTextEditor
abb128 May 19, 2026
4c2d5d3
Add rowSpan support to keyboard layout engine
jpds Mar 6, 2026
a55c111
Hide split keyboard option for layouts that don't support it
jpds Mar 15, 2026
ed5507e
Add mirrorInOneHanded support for keyboard layouts
jpds Mar 15, 2026
9a5a846
Make a bunch of template keys inheritable for custom layouts
abb128 May 19, 2026
43982a6
Fix clearflow layout breaking when action key is disabled
abb128 May 19, 2026
9714379
Document new changes to layout system
abb128 May 19, 2026
b01ef33
Credits: Add ClearFlow section
jpds Mar 7, 2026
5f476a6
Fix clipboard manager regression
abb128 May 19, 2026
071ee56
Add Unicode 17 emoji and introduce emoji compatibility
abb128 May 20, 2026
533b902
Ignore blank pastText in complex case for composing wrapper to resolv…
abb128 May 20, 2026
aff874b
Use emoji compatibility font in SuggestionItem only for emoji suggest…
abb128 May 20, 2026
9bcd23a
Fix an emoji regression
abb128 May 20, 2026
6d57473
Add confirmation for deleting layouts
abb128 May 21, 2026
c85baa9
Improve emoji handling by using system font more often, update design
abb128 May 21, 2026
5d1dec5
Rewrite repeatablyClickableAction
abb128 May 21, 2026
4846d5d
Disable Transformer on non QWERTY
abb128 May 21, 2026
c73d6b5
Swipe library integration
abb128 May 27, 2026
dcf3bb6
Merge branch 'swipe' into 'master'
abb128 May 27, 2026
614d49f
Rework spacebar/delete settings
abb128 May 27, 2026
400efc6
Make scoring update in realtime
abb128 May 27, 2026
b2aa0ef
Refactor ChineseIME. Now it acts as a backend. Migrate yaml configura…
AstroNot233 May 28, 2026
088b05a
feat: build a framework for generating *.yaml. Some problems need som…
AstroNot233 May 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ crashreporting.properties
keystore.properties
key.jks
java/res/raw/mozc.data
.cxx
.cxx
java/src/org/futo/inputmethod/keyboard/internal/KeyboardTextsTable.java
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
[submodule "java/res-large"]
path = java/res-large
url = https://gitlab.futo.org/keyboard/keyboard-large-resources.git
[submodule "java/assets/futo-swipe"]
path = java/assets/futo-swipe
url = https://huggingface.co/futo-org/futo-swipe
14 changes: 11 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,12 @@ android {
mlModelBinding true
buildConfig true
}

packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
}

dependencies {
Expand All @@ -375,15 +381,15 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.8.4'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4'
implementation 'androidx.activity:activity-compose:1.9.1'
implementation platform('androidx.compose:compose-bom:2024.06.00')
implementation platform('androidx.compose:compose-bom:2025.06.00')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-compose:2.7.7'
implementation 'androidx.navigation:navigation-compose:2.10.0-alpha02'

implementation 'com.google.code.findbugs:jsr305:3.0.2'

Expand All @@ -396,7 +402,7 @@ dependencies {
unstableImplementation 'ch.acra:acra-mail:5.11.1'
unstableImplementation 'ch.acra:acra-dialog:5.11.1'

implementation 'sh.calvin.reorderable:reorderable:2.2.0'
implementation 'sh.calvin.reorderable:reorderable:3.0.0'

//implementation 'com.squareup.okhttp3:okhttp:4.11.0'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1'
Expand All @@ -420,6 +426,8 @@ dependencies {
implementation files('libs/rime-release.aar')
// End for RIME

implementation files('libs/futo-swipe-release.aar')

debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'

Expand Down
82 changes: 82 additions & 0 deletions common/src/org/futo/inputmethod/latin/common/InputPointers.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,41 @@

package org.futo.inputmethod.latin.common;

import android.util.Log;

import org.futo.inputmethod.annotations.UsedForTesting;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.annotation.Nonnull;

import kotlin.Pair;

// TODO: This class is not thread-safe.
public final class InputPointers {
public static final class GestureSegment {
public final int pointerId;
public final ResizableIntArray x = new ResizableIntArray(0);
public final ResizableIntArray y = new ResizableIntArray(0);
public final ResizableIntArray t = new ResizableIntArray(0);

public GestureSegment(int pointerId) {
this.pointerId = pointerId;
}
}
private static final boolean DEBUG_TIME = false;
private static final boolean DEBUG_TRACE = false;
private static final String TAG = "InputPointers";

private final int mDefaultCapacity;
private final ResizableIntArray mXCoordinates;
private final ResizableIntArray mYCoordinates;
private final ResizableIntArray mPointerIds;
private final ResizableIntArray mTimes;
private final ArrayList<GestureSegment> mGestureSegments = new ArrayList<>();
private final HashMap<Integer, GestureSegment> mActivePointerSegments = new HashMap<>();

public InputPointers(final int defaultCapacity) {
mDefaultCapacity = defaultCapacity;
Expand Down Expand Up @@ -73,17 +95,27 @@ public void addPointer(final int x, final int y, final int pointerId, final int
}

public void set(@Nonnull final InputPointers ip) {
if(DEBUG_TRACE) Log.d(TAG, "set " + System.identityHashCode(this) + " ip=" + System.identityHashCode(ip));
mXCoordinates.set(ip.mXCoordinates);
mYCoordinates.set(ip.mYCoordinates);
mPointerIds.set(ip.mPointerIds);
mTimes.set(ip.mTimes);

mGestureSegments.clear();
mGestureSegments.addAll(ip.getGestureSegments());
mActivePointerSegments.clear();
}

public void copy(@Nonnull final InputPointers ip) {
if(DEBUG_TRACE) Log.d(TAG, "copy " + System.identityHashCode(this) + " ip=" + System.identityHashCode(ip));
mXCoordinates.copy(ip.mXCoordinates);
mYCoordinates.copy(ip.mYCoordinates);
mPointerIds.copy(ip.mPointerIds);
mTimes.copy(ip.mTimes);

mGestureSegments.clear();
mGestureSegments.addAll(ip.getGestureSegments());
mActivePointerSegments.clear();
}

/**
Expand All @@ -99,13 +131,40 @@ public void copy(@Nonnull final InputPointers ip) {
public void append(final int pointerId, @Nonnull final ResizableIntArray times,
@Nonnull final ResizableIntArray xCoordinates,
@Nonnull final ResizableIntArray yCoordinates, final int startPos, final int length) {
if(DEBUG_TRACE) {
Log.d(TAG, "add " + pointerId + " " + length + " " + System.identityHashCode(this) + " " + Thread.currentThread().getId());
Log.d(TAG, " x:" + xCoordinates.get(startPos) + "->" + xCoordinates.get(startPos + length - 1));
Log.d(TAG, " y:" + yCoordinates.get(startPos) + "->" + yCoordinates.get(startPos + length - 1));
Log.d(TAG, " t:" + times.get(startPos) + "->" + times.get(startPos + length - 1));
}
if (length == 0) {
return;
}
mXCoordinates.append(xCoordinates, startPos, length);
mYCoordinates.append(yCoordinates, startPos, length);
mPointerIds.fill(pointerId, mPointerIds.getLength(), length);
mTimes.append(times, startPos, length);

GestureSegment seg = mActivePointerSegments.get(pointerId);
if(seg == null) {
Log.e(TAG, "Attempting to append points to pointerId " + pointerId + " which was not pressed down!!");
return;
}
seg.x.append(xCoordinates, startPos, length);
seg.y.append(yCoordinates, startPos, length);
seg.t.append(times, startPos, length);
}

public void onPointerDown(final int pointerId) {
if(DEBUG_TRACE) Log.d(TAG, "down " + pointerId + " " + System.identityHashCode(this));
GestureSegment seg = new GestureSegment(pointerId);
mActivePointerSegments.put(pointerId, seg);
mGestureSegments.add(seg);

}
public void onPointerUp(final int pointerId) {
if(DEBUG_TRACE) Log.d(TAG, "up " + pointerId + " " + System.identityHashCode(this));
//mActivePointerSegments.remove(pointerId);
}

/**
Expand All @@ -118,16 +177,36 @@ public void shift(final int elementCount) {
mYCoordinates.shift(elementCount);
mPointerIds.shift(elementCount);
mTimes.shift(elementCount);

for(int i=0; i<elementCount; i++) mGestureSegments.remove(0);
}

public void reset() {
if(DEBUG_TRACE) Log.d(TAG, "reset full" + Thread.currentThread().getId());
resetNonBatch();
resetForBatch();
}

public void resetNonBatch() {
if(DEBUG_TRACE) Log.d(TAG, "(reset nonbatch)" + Thread.currentThread().getId());
final int defaultCapacity = mDefaultCapacity;
mXCoordinates.reset(defaultCapacity);
mYCoordinates.reset(defaultCapacity);
mPointerIds.reset(defaultCapacity);
mTimes.reset(defaultCapacity);
}

public void resetForBatch() {
if(DEBUG_TRACE) Log.d(TAG, "(reset forbatch)" + Thread.currentThread().getId());
mGestureSegments.clear();
mActivePointerSegments.clear();
}

public void upAllPointers() {
if(DEBUG_TRACE) Log.d(TAG, "up all pointers " + Thread.currentThread().getId());
mActivePointerSegments.clear();
}

public int getPointerSize() {
return mXCoordinates.getLength();
}
Expand All @@ -147,6 +226,9 @@ public int[] getPointerIds() {
return mPointerIds.getPrimitiveArray();
}

@Nonnull
public List<GestureSegment> getGestureSegments() { return mGestureSegments; }

/**
* Gets the time each point was registered, in milliseconds, relative to the first event in the
* sequence.
Expand Down
16 changes: 15 additions & 1 deletion java/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>

<!-- For clipboard manager reading screenshots -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"
android:minSdkVersion="33" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" tools:node="remove"/>

<application android:label="@string/english_ime_name"
Expand All @@ -35,6 +41,7 @@
android:supportsRtl="true"
android:allowBackup="true"
android:largeHeap="true"
android:enableOnBackInvokedCallback="true"
android:name=".CrashLoggingApplication">

<!-- Services -->
Expand Down Expand Up @@ -182,13 +189,20 @@
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
tools:node="remove"
tools:ignore="MissingClass" />

<provider
android:authorities="${applicationId}.font"
android:name=".content.KbContentProvider"
android:exported="false"
android:grantUriPermissions="true" />

<provider
android:authorities="${applicationId}.clipboard"
android:name=".uix.actions.clipboard.ClipboardProvider"
android:exported="false"
android:grantUriPermissions="true" />
</application>

<!-- To query enabled input methods for voice IME detection -->
Expand Down
Binary file added java/assets/fonts/NotoColorEmoji_api24.ttf
Binary file not shown.
1 change: 1 addition & 0 deletions java/assets/futo-swipe
Submodule futo-swipe added at a50462
2 changes: 1 addition & 1 deletion java/res-large
Submodule res-large updated from 17f303 to d87d9d
2 changes: 1 addition & 1 deletion java/res/drawable/ripple_emoji_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
-->

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorAccent">
android:color="@android:color/white">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<corners android:radius="12dp" />
Expand Down
26 changes: 26 additions & 0 deletions java/res/drawable/swipemodel_indicator.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="6.01dp"
android:viewportWidth="14"
android:viewportHeight="6.01">
<path
android:pathData="M1.647,3.725C1.647,3.725 1.647,3.215 3.42,1.648C5.922,-0.565 7.944,5.31 11.093,3.445C12.353,2.698 12.353,1.913 12.353,1.913"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M1.647,4.267C1.647,4.267 1.647,3.756 3.42,2.189C5.922,-0.024 7.944,5.852 11.093,3.986C12.353,3.24 12.353,2.454 12.353,2.454"
android:strokeAlpha="0.66"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M1.647,4.777C1.647,4.777 1.647,4.267 3.42,2.699C5.922,0.487 7.944,6.362 11.093,4.496C12.353,3.75 12.353,2.964 12.353,2.964"
android:strokeAlpha="0.33"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
</vector>
11 changes: 6 additions & 5 deletions java/res/layout/emoji_category.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:paddingTop="24dp"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textSize="22sp"/>
android:paddingTop="16dp"
android:paddingBottom="4dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:textSize="16sp"
android:fontFamily="sans-serif-medium"/>
Binary file modified java/res/raw/gemoji.jsongz
Binary file not shown.
41 changes: 0 additions & 41 deletions java/res/values-h1200dp-port/setup-dimens-large-tablet-port.xml

This file was deleted.

Loading