Compare commits

..

6 Commits

Author SHA1 Message Date
1de7872881 Improve selection mode when selection ends cross
Emptying the selection with the spacebar slider puts the selection mode
in an unexpected state. This checks that this doesn't happen.

The selection ends can now cross each other, reversing the selection
order. Special care is made to make sure that the sliders go in the
expected direction.

Pointers is changed to send a `onPointerDown` for the first slider event
instead of a `onPointerHold`. This event is detected in
`KeyEventHandler` and the selection ends are reordered if needed when
sliding again.
2025-06-16 00:17:14 +02:00
f9b844e988 Increase the clipboard history size to 6
Some checks failed
Make Apk CI / Build-Apk (push) Has been cancelled
Check translations / check-translations (push) Has been cancelled
Check layouts / check_layout.output (push) Has been cancelled
Check layouts / Generated files (push) Has been cancelled
2025-06-14 11:12:21 +02:00
6f0244a29e Add funding.json
Some checks failed
Make Apk CI / Build-Apk (push) Has been cancelled
Check translations / check-translations (push) Has been cancelled
Check layouts / check_layout.output (push) Has been cancelled
Check layouts / Generated files (push) Has been cancelled
2025-06-04 02:08:31 +02:00
2a50a4a129 Fix high keyboard height making it overflows
Some checks failed
Make Apk CI / Build-Apk (push) Has been cancelled
Check translations / check-translations (push) Has been cancelled
Check layouts / check_layout.output (push) Has been cancelled
Check layouts / Generated files (push) Has been cancelled
The calculation for the size of each rows now avoid making the keyboard
bigger than the screen if the Keyboard Height option is unusually high.

The height calculation with the default settings is changed slightly, it
is now assuming that a layout is 3.95 rows high instead of 4. This is
because the bottom row is usually 0.95% the size of other rows.
2025-05-30 20:07:26 +02:00
9ab099175d Fix label size when the keyboard is unusually high
The label size is computed relatively to the width of the keyboard when
the height is larger than 3/2 of the width. This ensures that the labels
have a reasonable size when the keyboard height increases.
2025-05-30 20:07:26 +02:00
0445c310ad Settings: Remove the upper limit in keyboard height
This can be used on phones that have several screens.
2025-05-30 20:07:26 +02:00
4 changed files with 120 additions and 12 deletions

91
funding.json Normal file
View File

@ -0,0 +1,91 @@
{
"version": "v1.0.0",
"entity": {
"type": "individual",
"role": "owner",
"name": "Julow",
"email": "jules@j3s.fr",
"description": "Open source developer and maintainer of Unexpected Keyboard.",
"webpageUrl": {
"url": "https://github.com/Julow"
}
},
"projects": [
{
"guid": "unexpected-keyboard",
"name": "Unexpected Keyboard",
"description": "Lightweight and privacy-conscious virtual keyboard for Android.",
"webpageUrl": {
"url": "https://github.com/Julow/Unexpected-Keyboard/"
},
"repositoryUrl": {
"url": "https://github.com/Julow/Unexpected-Keyboard/"
},
"licenses": [
"spdx:GPL-3.0",
"spdx:CC0-1.0"
],
"tags": [
"android",
"mobile",
"privacy",
"productivity",
"programming",
"user-experience"
]
}
],
"funding": {
"channels": [
{
"guid": "liberapay",
"type": "other",
"address": "https://liberapay.com/Julow/",
"description": "Recurring donations for funding Unexpected Keyboard."
},
{
"guid": "github-sponsors",
"type": "other",
"address": "https://github.com/sponsors/Julow",
"description": "Recurring donations for funding Unexpected Keyboard."
},
{
"guid": "paypal",
"type": "other",
"address": "https://paypal.me/JulesAguillon",
"description": "One-time donations for funding Unexpected-keyboard."
}
],
"plans": [
{
"guid": "fund-maintenance",
"status": "active",
"name": "Fund developer time",
"description": "Help the maintainers spend time on Unexpected Keyboard",
"amount": 0,
"currency": "EUR",
"frequency": "monthly",
"channels": [
"liberapay",
"github-sponsors",
"paypal"
]
},
{
"guid": "one-time-contribution",
"status": "active",
"name": "Fund developer time",
"description": "Help the maintainers spend time on Unexpected Keyboard",
"amount": 0,
"currency": "EUR",
"frequency": "one-time",
"channels": [
"liberapay",
"github-sponsors",
"paypal"
]
}
],
"history": []
}
}

View File

@ -49,7 +49,7 @@ public final class ClipboardHistoryService
/** The maximum size limits the amount of user data stored in memory but also
gives a sense to the user that the history is not persisted and can be
forgotten as soon as the app stops. */
public static final int MAX_HISTORY_SIZE = 3;
public static final int MAX_HISTORY_SIZE = 6;
/** Time in ms until history entries expire. */
public static final long HISTORY_TTL_MS = 5 * 60 * 1000;

View File

@ -78,6 +78,11 @@ public final class KeyEventHandler
case Compose_pending:
_autocap.stop();
break;
case Slider:
// Don't wait for the next key_up and move the cursor right away. This
// is called after the trigger distance have been travelled.
handle_slider(key.getSlider(), key.getSliderRepeat(), true);
break;
default: break;
}
}
@ -99,7 +104,7 @@ public final class KeyEventHandler
case Modifier: break;
case Editing: handle_editing_key(key.getEditing()); break;
case Compose_pending: _recv.set_compose_pending(true); break;
case Slider: handle_slider(key.getSlider(), key.getSliderRepeat()); break;
case Slider: handle_slider(key.getSlider(), key.getSliderRepeat(), false); break;
case Macro: evaluate_macro(key.getMacro()); break;
}
update_meta_state(old_mods);
@ -261,7 +266,7 @@ public final class KeyEventHandler
}
/** [r] might be negative, in which case the direction is reversed. */
void handle_slider(KeyValue.Slider s, int r)
void handle_slider(KeyValue.Slider s, int r, boolean key_down)
{
switch (s)
{
@ -269,8 +274,8 @@ public final class KeyEventHandler
case Cursor_right: move_cursor(r); break;
case Cursor_up: move_cursor_vertical(-r); break;
case Cursor_down: move_cursor_vertical(r); break;
case Selection_cursor_left: move_cursor_sel(r, true); break;
case Selection_cursor_right: move_cursor_sel(r, false); break;
case Selection_cursor_left: move_cursor_sel(r, true, key_down); break;
case Selection_cursor_right: move_cursor_sel(r, false, key_down); break;
}
}
@ -310,7 +315,7 @@ public final class KeyEventHandler
/** Move one of the two side of a selection. If [sel_left] is true, the left
position is moved, otherwise the right position is moved. */
void move_cursor_sel(int d, boolean sel_left)
void move_cursor_sel(int d, boolean sel_left, boolean key_down)
{
InputConnection conn = _recv.getCurrentInputConnection();
if (conn == null)
@ -320,10 +325,23 @@ public final class KeyEventHandler
{
int sel_start = et.selectionStart;
int sel_end = et.selectionEnd;
if (sel_left == (sel_start <= sel_end))
sel_start += d;
else
sel_end += d;
// Reorder the selection when the slider has just been pressed. The
// selection might have been reversed if one end crossed the other end
// with a previous slider.
if (key_down && sel_start > sel_end)
{
sel_start = et.selectionEnd;
sel_end = et.selectionStart;
}
do
{
if (sel_left)
sel_start += d;
else
sel_end += d;
// Move the cursor twice if moving it once would make the selection
// empty and stop selection mode.
} while (sel_start == sel_end);
if (conn.setSelection(sel_start, sel_end))
return; // Fallback to sending key events if [setSelection] failed
}

View File

@ -306,7 +306,6 @@ public final class Pointers implements Handler.Callback
// Start sliding mode
if (new_value.getKind() == KeyValue.Kind.Slider)
startSliding(ptr, x, y, dx, dy, new_value);
_handler.onPointerDown(new_value, true);
}
}
@ -469,7 +468,7 @@ public final class Pointers implements Handler.Callback
stopLongPress(ptr);
ptr.flags |= FLAG_P_SLIDING;
ptr.sliding = new Sliding(x, y, dirx, diry, kv.getSlider());
_handler.onPointerHold(kv, ptr.modifiers);
_handler.onPointerDown(kv, true);
}
/** Return the [FLAG_P_*] flags that correspond to pressing [kv]. */