Merge branch 'main' into settings_new

This commit is contained in:
Helium314 2025-02-06 22:52:05 +01:00
commit ed48d5096a
51 changed files with 1126 additions and 226 deletions

View file

@ -1,18 +1,19 @@
See the contributing readme for more detailed guideline, please understand and accept them: https://github.com/Helium314/HeliBoard/blob/main/CONTRIBUTING.md
tl;dr (you should still read the full list though):
* make sure it's wanted
See the contributing readme for more detailed guidelines: https://github.com/Helium314/HeliBoard/blob/main/CONTRIBUTING.md#guidelines
tl;dr:
necessary;
* a single thing only
* describe it properly
recommended:
* make changes optional
* re-use existing mechanisms / code
* low performance impact
* make it a draft if you still want to work on it
* no translations or dictionaries
A good description and small scope ("single thing") massively help with reviewing. Don't be surprised when your PR gets closes if you clearly / repeatedly violate these parts of the guidelines.
Further
* When the PR contains "fixes" <issue number>, the related issue will be linked and automatically closed if the PR is merged (also works for other words like "fix", "resolve", "resolves", "closes", ...)
* If you add a keyboard layout, make sure you also read https://github.com/Helium314/HeliBoard/blob/main/layouts.md#adding-new-layouts--languages
* Please avoid force-pushing when doing changes unless you have a good reason. This way it's not possible to see which parts have changed since the previous state.
* If you add a keyboard layout, best read https://github.com/Helium314/HeliBoard/blob/main/layouts.md#adding-new-layouts--languages
* Please avoid force-pushing when doing requested changes. This way it's not possible to see which parts have changed since the previous state.
<!-- (please remove the text above before submitting the PR) -->

View file

@ -6,31 +6,64 @@ Once everything is up correctly, you're ready to go!
If you have difficulties implementing some functionality, you're welcome to ask for help. No one will write the code for you, but often other contributors can give you very useful hints.
# About the Code
HeliBoard is based on AOSP keyboard, and in many places still contains mostly the original code. There are some extensions, and some parts have been replaced completely.
When working on this app, you will likely notice its rather large size, and quite different code styles and often ancient comments and _TODO_s, where the latter are typically untouched since AOSP times.
Unfortunately a lot of the old code is hard to read or to fully understand with all of its intended (and unintended) consequences.
Some hints for finding what you're looking for:
* Layouts: stored in `layouts` folder in assets, interpreted by `KeyboardParser` and `TextKeyData`
* Popups: either on layouts, or in `locale_key_texts` (mostly letter variations for specific languages that are not dependent on layout)
* Touch and swipe input handling: `PointerTracker`
* Handling of key inputs: `InputLogic`
* Suggestions: `DictionaryFacilitatorImpl`, `Suggest`, `InputLogic`, and `SuggestionStripView` (in order from creation to display)
* Forwarding entered text / keys to the app / text field: `RichInputConnection`
* Receiving events and information from the app / text field: `LatinIME`
# Guidelines
HeliBoard is a complex application, when contributing, you must take a step back and make sure your contribution:
- **Is actually wanted**. Best check related open issues before you start working on a PR. Issues with the [labels](https://github.com/Helium314/HeliBoard/labels) [_PR_](https://github.com/Helium314/HeliBoard/labels/PR) and [_contributor needed_](https://github.com/Helium314/HeliBoard/issues?q=label%3A%22contributor%20needed%22) (even closed ones) are accepted, but still it would be good if you announced that you are working on it, so we can discuss how changes are best implemented.
If there is no accepted issue related to your intended contribution, it's a good idea to open a new one (and of course getting one of "PR" or "contributor needed" labels) to avoid disappointment of the contribution not being accepted. For small changes or fixing obvious bugs this step is not necessary.
- **Is only about a single thing**. Mixing unrelated or semi-related contributions into a single PR is hard to review and can get messy.
- **Is finished or a draft**. When you keep changing the PR without reviewer's feedback, any attempt to review it is doomed and a waste of time. Better mark it as a draft in this case.
- **Has a proper description**. What your contribution does is usually less obvious to reviewers than for yourself. A good description helps _a lot_ for understanding what is going on, and for separating wanted from unintended changes in behavior. Therefore the changes should be as described, not more and not less.
- **Uses already in-place mechanism and take advantage of them**. In other terms, does not reinvent the wheel or uses shortcuts that could alter the consistency of the existing code. The contribution should only add as little complexity as necessary, the code is overly complicated already 😶.
- **Has a low footprint**. Some parts of the code are executed very frequently, and the keyboard should stay responsive even on older devices.
- **Does not bring any non-free code or proprietary binary blobs**. This also applies to code/binaries with unknown licenses. Make sure you do not introduce any closed-source library from Google.
If your contribution contains code that is not your own, provide a link to the source.
- **Does not increase app size too much**. Just code changes or adding icons is not in issue, but e.g. large dependencies or adding more default dictionaries will not be accepted.
- **Complies with the user privacy principle HeliBoard follows**.
## Recommended
A good description and small scope ("single thing") massively help with reviewing. Don't be surprised when your PR gets closes if you clearly / repeatedly violate these parts of the guidelines.
If possible try to keep your changes contained, i.e. in few places as opposed to sprinkling them over many parts of the code. This helps with both review and maintainability of the app.
If you want to contribute, it's a good idea to make sure your idea is actually wanted in HeliBoard.
Best check related issues before you start working on a PR. If the issue has the [labels](https://github.com/Helium314/HeliBoard/labels) [_PR_](https://github.com/Helium314/HeliBoard/labels/PR) or [_contributor needed_](https://github.com/Helium314/HeliBoard/issues?q=label%3A%22contributor%20needed%22) (even closed ones), contributions are wanted. If you don't find a related issue, it's recommended to open one, but ultimately it's your choice.
Asking before starting a PR may help you for getting pointers to potentially relevant code, and deciding how to implement your desired changes.
Please leave dependency upgrades to the maintainers, unless it's an actual security issue.
HeliBoard is a complex application and used by users with a large variety of opinions on how things should be.
When contributing to the app, please:
* Be careful when modifying core components, as it's easy to trigger unintended consequences
* When introducing a feature or change that might not be wanted by everyone, make it optional
* Keep code simple where possible. Complex code is harder to review and to maintain, so the complexity should also add a clear benefit
* Avoid noticeable performance impact. Some parts of the code are executed very frequently, and the keyboard should stay responsive even on older devices.
* Try making use of in-place mechanisms instead of re-inventing the wheel. Your contribution should only add as much complexity as necessary, the code is overly complicated already 😶.
* Keep your changes to few places, as opposed to sprinkling them over many parts of the code. This helps with keeping down complexity during review, and with maintainability of the app.
* Make a draft PR when you intend to still work on it. Submitting an unfinished PR can be a good idea when you're not sure how to best continue and would like some comments.
# Adding Layouts
Further things to consider (though irrelevant for most PRs):
* APK size:
* Large increases should be discussed first, and will only be added when it's considered worth the increase for a majority of users. It might be possible to avoid size increase by importing optional parts, like it's done for dictionaries.
* Small increases like when adding code or layouts are never an issue
* Do not add proprietary code or binary blobs. If it turns out to be necessary for a feature you want to add, it might be acceptable when the user opts in and imports those parts, like it's done for glide typing.
* Privacy: Only relevant when adding some form of communication with other apps. Internet permission will not be added.
* If your contribution contains code that is not your own, provide a link to the source
* This is especially relevant to be sure the code's license is compatible to HeliBoard's GPL3
## Necessary
Some parts of the guidelines are necessary to fulfill for facilitating code review. It doesn't need to be perfect from the start, but consider it for your future PRs when you're reminded of these guidelines. Note that the larger / more complex your PR is, the more relevant these guidelines are.
Your PR should:
- **Be only about a single thing**. Mixing unrelated or semi-related contributions into a single PR is hard to review and can get messy. As a general rule: if one part doesn't need the other one(s), it should be separate PRs. If one feature builds on top of another one, but the base is usable on its own, do a PR for the base and then a follow-up once it's merged.
- **Have a proper description**. A good description helps _a lot_ for understanding what you intend to achieve with the changes, and for understanding the code. This is relevant for separating wanted from unintended changes in behavior during review.
- **No translations**. Translations should be done using [Weblate](https://translate.codeberg.org/projects/heliboard/). Exception is when you add new resource strings, those can be added right away.
Please leave dependency upgrades to the maintainers, unless you state a good reason why they should be done now.
# Adding / Adjusting Layouts
See [layouts.md](layouts.md#adding-new-layouts--languages) for how to add new layouts to the app. Please stay in line with other layouts regarding the popup keys.
When editing existing layouts, please consider that people should should still get what they're used to. In case of doubt it might be better to add a new layout instead of overhauling existing layouts.
`locale_key_texts` files should only contain letters that are actually part of the language, with exception of the optional `more_popups_<...>.txt` files.
# Update Emojis

View file

@ -0,0 +1,34 @@
ز
ر
ذ
د
خ
ح
ج
ث
ت
ب
ا
ك
ق
ف
غ
ع
ظ
ط
ض
ص
ش
س
ء
ى
ي
ؤ
و
ة
ن
م
ل

View file

@ -1,112 +1,556 @@
[
[
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ং" },
"default": { "label": "ঙ" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ং",
"labelFlags": 1073741824
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "য়" },
"default": { "label": "য" }
"default": {
"label": "ঙ",
"popup": {
"main": {
"label": "ং"
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ঢ" },
"default": { "label": "ড" }
"relevant": [
{
"label": "১"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ফ" },
"default": { "label": "প" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "য়",
"labelFlags": 1073741824
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ঠ" },
"default": { "label": "ট" }
"default": {
"label": "য",
"popup": {
"main": {
"label": "য়"
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ছ" },
"default": { "label": "চ" }
"relevant": [
{
"label": "২"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ঝ" },
"default": { "label": "জ" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ঢ",
"labelFlags": 1073741824
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ঞ" },
"default": { "label": "হ" }
"default": {
"label": "ড",
"popup": {
"main": {
"label": "ঢ"
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ঘ" },
"default": { "label": "গ" }
"relevant": [
{
"label": "৩"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ঢ়" },
"default": { "label": "ড়" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ফ",
"labelFlags": 1073741824
},
"default": {
"label": "প",
"popup": {
"main": {
"label": "ফ"
},
"relevant": [
{
"label": ""
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ঠ",
"labelFlags": 1073741824
},
"default": {
"label": "ট",
"popup": {
"main": {
"label": "ঠ"
},
"relevant": [
{
"label": "৫"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ছ",
"labelFlags": 1073741824
},
"default": {
"label": "চ",
"popup": {
"main": {
"label": "ছ"
},
"relevant": [
{
"label": "৬"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ঝ",
"labelFlags": 1073741824
},
"default": {
"label": "জ",
"popup": {
"main": {
"label": "ঝ"
},
"relevant": [
{
"label": ""
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ঞ",
"labelFlags": 1073741824
},
"default": {
"label": "হ",
"popup": {
"main": {
"label": "ঞ"
},
"relevant": [
{
"label": "৮"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ঘ",
"labelFlags": 1073741824
},
"default": {
"label": "গ",
"popup": {
"main": {
"label": "ঘ"
},
"relevant": [
{
"label": "৯"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ঢ়",
"labelFlags": 1073741824
},
"default": {
"label": "ড়",
"popup": {
"main": {
"label": "ঢ়"
},
"relevant": [
{
"label": ""
}
]
}
}
}
],
[
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ঃ" },
"default": { "label": "ৃ" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ঃ",
"labelFlags": 1073741824
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ূ" },
"default": { "label": "ু" }
"default": {
"label": "ৃ",
"popup": {
"relevant": [
{
"label": "ঋ"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ী" },
"default": { "label": "ি" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ূ",
"popup": {
"relevant": [
{
"label": "ঊ"
}
]
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "অ" },
"default": { "label": "া" }
"default": {
"label": "ু",
"popup": {
"relevant": [
{
"label": "উ"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ঁ" },
"default": { "label": "্" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ী",
"popup": {
"relevant": [
{
"label": "ঈ"
}
]
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ভ" },
"default": { "label": "ব" }
"default": {
"label": "ি",
"popup": {
"relevant": [
{
"label": "ই"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "খ" },
"default": { "label": "ক" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "অ",
"labelFlags": 1073741824
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "থ" },
"default": { "label": "ত" }
"default": {
"label": "া",
"popup": {
"main": {
"label": "আ"
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ধ" },
"default": { "label": "দ" }
"relevant": [
{
"label": "অ"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ঁ",
"labelFlags": 1073741824,
"popup": {
"relevant": [
{
"label": "!autoColumnOrder!6"
},
{
"label": "়"
},
{
"label": "ৄ"
},
{
"label": "ঽ"
},
{
"label": "ৢ"
},
{
"label": "ৱ"
},
{
"label": "ৣ"
},
{
"label": "ৗ"
},
{
"label": "ৠ"
},
{
"label": "৺"
},
{
"label": "ঌ"
},
{
"label": "ৰ"
},
{
"label": "ৡ"
}
]
}
},
"default": {
"label": "্",
"popup": {
"relevant": [
{
"label": "ঁ"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ভ",
"labelFlags": 1073741824
},
"default": {
"label": "ব",
"popup": {
"relevant": [
{
"label": "ভ"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "খ",
"labelFlags": 1073741824
},
"default": {
"label": "ক",
"popup": {
"relevant": [
{
"label": "খ"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "থ",
"labelFlags": 1073741824
},
"default": {
"label": "ত",
"popup": {
"main": {
"label": "থ"
},
"relevant": [
{
"label": "ৎ"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ধ",
"labelFlags": 1073741824
},
"default": {
"label": "দ",
"popup": {
"relevant": [
{
"label": "ধ"
}
]
}
}
}
],
[
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "্য" },
"default": { "label": "্র" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "্য",
"labelFlags": 1073741824
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ৌ" },
"default": { "label": "ো" }
"default": {
"label": "্র",
"popup": {
"relevant": [
{
"label": "্য"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ৈ" },
"default": { "label": "ে" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ৌ",
"popup": {
"relevant": [
{
"label": "ঔ"
}
]
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ল" },
"default": { "label": "র" }
"default": {
"label": "ো",
"popup": {
"relevant": [
{
"label": "ও"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ণ" },
"default": { "label": "ন" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ৈ",
"popup": {
"relevant": [
{
"label": "ঐ"
}
]
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "ষ" },
"default": { "label": "স" }
"default": {
"label": "ে",
"popup": {
"relevant": [
{
"label": "এ"
}
]
}
}
},
{ "$": "shift_state_selector",
"manualOrLocked": { "label": "শ" },
"default": { "label": "ম" }
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ল",
"labelFlags": 1073741824
},
"default": {
"label": "র",
"popup": {
"main": {
"label": "ল"
},
"relevant": [
{
"label": "র‍্য"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ণ",
"labelFlags": 1073741824
},
"default": {
"label": "ন",
"popup": {
"relevant": [
{
"label": "ণ"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "ষ",
"labelFlags": 1073741824
},
"default": {
"label": "স",
"popup": {
"relevant": [
{
"label": "ষ"
}
]
}
}
},
{
"$": "shift_state_selector",
"manualOrLocked": {
"label": "শ",
"labelFlags": 1073741824
},
"default": {
"label": "ম",
"popup": {
"relevant": [
{
"label": "শ"
}
]
}
}
}
]
]

View file

@ -16,7 +16,7 @@
ী ঈ
া আ অ
্ ঁ
় ৺ ঽ ৗ ঌ ৡ ৠ ৱ ৢ ৣ ৄ ৰ
!autoColumnOrder!6 ় ৄ ঽ ৢ ৱ ৣ ৗ ৠ ৺ ঌ ৰ ৡ
ব ভ
ক খ
ত থ ৎ

View file

@ -97,6 +97,7 @@ public interface KeyboardActionListener {
*/
boolean onHorizontalSpaceSwipe(int steps);
boolean onVerticalSpaceSwipe(int steps);
void onEndSpaceSwipe();
boolean toggleNumpad(boolean withSliding, boolean forceReturnToAlpha);
void onMoveDeletePointer(int steps);
@ -148,6 +149,8 @@ public interface KeyboardActionListener {
return false;
}
@Override
public void onEndSpaceSwipe() {}
@Override
public void onMoveDeletePointer(int steps) {}
@Override
public void onUpWithDeletePointerActive() {}

View file

@ -1,6 +1,7 @@
package helium314.keyboard.keyboard
import android.view.KeyEvent
import android.view.inputmethod.InputMethodSubtype
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
import helium314.keyboard.latin.LatinIME
import helium314.keyboard.latin.RichInputMethodManager
@ -19,6 +20,10 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
private val settings = Settings.getInstance()
private var metaState = 0 // is this enough, or are there threading issues with the different PointerTrackers?
// language slide state
private var initialSubtype: InputMethodSubtype? = null
private var subtypeSwitchCount = 0
// todo: maybe keep meta state presses to KeyboardActionListenerImpl, and avoid calls to press/release key
private fun adjustMetaState(code: Int, remove: Boolean) {
val metaCode = when (code) {
@ -84,6 +89,11 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
else -> false
}
override fun onEndSpaceSwipe(){
initialSubtype = null
subtypeSwitchCount = 0
}
override fun toggleNumpad(withSliding: Boolean, forceReturnToAlpha: Boolean): Boolean {
KeyboardSwitcher.getInstance().toggleNumpad(withSliding, latinIME.currentAutoCapsState, latinIME.currentRecapitalizeState, forceReturnToAlpha)
return true
@ -124,7 +134,7 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
}
private fun onLanguageSlide(steps: Int): Boolean {
if (abs(steps) < 4) return false
if (abs(steps) < settings.current.mLanguageSwipeDistance) return false
val subtypes = RichInputMethodManager.getInstance().getMyEnabledInputMethodSubtypeList(false)
if (subtypes.size <= 1) { // only allow if we have more than one subtype
return false
@ -135,7 +145,18 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
wantedIndex %= subtypes.size
if (wantedIndex < 0)
wantedIndex += subtypes.size
KeyboardSwitcher.getInstance().switchToSubtype(subtypes[wantedIndex])
val newSubtype = subtypes[wantedIndex]
// do not switch if we would switch to the initial subtype after cycling all other subtypes
if (initialSubtype == null)
initialSubtype = current
if (initialSubtype == newSubtype) {
if ((subtypeSwitchCount > 0 && steps > 0) || ((subtypeSwitchCount < 0 && steps < 0)))
return true
}
if (steps > 0) subtypeSwitchCount++ else subtypeSwitchCount--
KeyboardSwitcher.getInstance().switchToSubtype(newSubtype)
return true
}

View file

@ -8,6 +8,7 @@ package helium314.keyboard.keyboard;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@ -40,8 +41,10 @@ import helium314.keyboard.latin.RichInputMethodSubtype;
import helium314.keyboard.latin.WordComposer;
import helium314.keyboard.latin.settings.Settings;
import helium314.keyboard.latin.settings.SettingsValues;
import helium314.keyboard.latin.suggestions.SuggestionStripView;
import helium314.keyboard.latin.utils.AdditionalSubtypeUtils;
import helium314.keyboard.latin.utils.CapsModeUtils;
import helium314.keyboard.latin.utils.DeviceProtectedUtils;
import helium314.keyboard.latin.utils.LanguageOnSpacebarUtils;
import helium314.keyboard.latin.utils.Log;
import helium314.keyboard.latin.utils.RecapitalizeStatus;
@ -59,7 +62,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private View mEmojiTabStripView;
private LinearLayout mClipboardStripView;
private HorizontalScrollView mClipboardStripScrollView;
private View mSuggestionStripView;
private SuggestionStripView mSuggestionStripView;
private ClipboardHistoryView mClipboardHistoryView;
private TextView mFakeToastView;
private LatinIME mLatinIME;
@ -325,7 +328,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
if (DEBUG_ACTION) {
Log.d(TAG, "setEmojiKeyboard");
}
final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
mMainKeyboardFrame.setVisibility(View.VISIBLE);
// The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
// @see #getVisibleKeyboardView() and
@ -346,7 +348,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
if (DEBUG_ACTION) {
Log.d(TAG, "setClipboardKeyboard");
}
final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
mMainKeyboardFrame.setVisibility(View.VISIBLE);
// The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
// @see #getVisibleKeyboardView() and
@ -634,6 +635,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mKeyboardView.closing();
}
PointerTracker.clearOldViewData();
final SharedPreferences prefs = DeviceProtectedUtils.getSharedPreferences(displayContext);
if (mSuggestionStripView != null)
prefs.unregisterOnSharedPreferenceChangeListener(mSuggestionStripView);
if (mClipboardHistoryView != null)
prefs.unregisterOnSharedPreferenceChangeListener(mClipboardHistoryView);
updateKeyboardThemeAndContextThemeWrapper(displayContext, KeyboardTheme.getKeyboardTheme(displayContext));
mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(R.layout.input_view, null);
@ -656,6 +662,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mClipboardStripScrollView = mCurrentInputView.findViewById(R.id.clipboard_strip_scroll_view);
mSuggestionStripView = mCurrentInputView.findViewById(R.id.suggestion_strip_view);
prefs.registerOnSharedPreferenceChangeListener(mSuggestionStripView);
prefs.registerOnSharedPreferenceChangeListener(mClipboardHistoryView);
PointerTracker.switchTo(mKeyboardView);
return mCurrentInputView;
}

View file

@ -65,6 +65,7 @@ public class KeyboardView extends View {
private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
private final Colors mColors;
private float mKeyScaleForText;
protected float mFontSizeMultiplier;
// The maximum key label width in the proportion to the key width.
private static final float MAX_LABEL_RATIO = 0.90f;
@ -189,6 +190,9 @@ public class KeyboardView extends View {
mKeyDrawParams.updateParams(scaledKeyHeight, keyboard.mKeyVisualAttributes);
invalidateAllKeys();
requestLayout();
mFontSizeMultiplier = mKeyboard.mId.isEmojiKeyboard()
? Settings.getInstance().getCurrent().mFontSizeMultiplierEmoji
: Settings.getInstance().getCurrent().mFontSizeMultiplier;
}
/**
@ -384,7 +388,7 @@ public class KeyboardView extends View {
final String label = key.getLabel();
if (label != null) {
paint.setTypeface(mTypeface == null ? key.selectTypeface(params) : mTypeface);
paint.setTextSize(key.selectTextSize(params));
paint.setTextSize(key.selectTextSize(params) * mFontSizeMultiplier);
final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint);
final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint);
@ -446,10 +450,10 @@ public class KeyboardView extends View {
// Draw hint label.
final String hintLabel = key.getHintLabel();
if (hintLabel != null && mShowsHints) {
paint.setTextSize(key.selectHintTextSize(params));
paint.setTextSize(key.selectHintTextSize(params) * mFontSizeMultiplier); // maybe take sqrt to not have such extreme changes?
paint.setColor(key.selectHintTextColor(params));
// TODO: Should add a way to specify type face for hint letters
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTypeface(mTypeface == null ? Typeface.DEFAULT_BOLD : mTypeface);
blendAlpha(paint, params.mAnimAlpha);
final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint);
final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint);
@ -561,7 +565,7 @@ public class KeyboardView extends View {
} else {
paint.setColor(key.selectTextColor(mKeyDrawParams));
paint.setTypeface(key.selectTypeface(mKeyDrawParams));
paint.setTextSize(key.selectTextSize(mKeyDrawParams));
paint.setTextSize(key.selectTextSize(mKeyDrawParams) * mFontSizeMultiplier);
}
return paint;
}

View file

@ -164,7 +164,8 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
mBackgroundDimAlphaPaint.setColor(Color.BLACK);
mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f);
R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f)
* Settings.getInstance().getCurrent().mFontSizeMultiplier;
final Colors colors = Settings.getInstance().getCurrent().mColors;
mLanguageOnSpacebarTextColor = colors.get(ColorType.SPACE_BAR_TEXT);
mLanguageOnSpacebarTextShadowRadius = mainKeyboardViewAttr.getFloat(

View file

@ -1076,6 +1076,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
if (mInHorizontalSwipe || mInVerticalSwipe) {
mInHorizontalSwipe = false;
mInVerticalSwipe = false;
sListener.onEndSpaceSwipe();
return;
}
}

View file

@ -4,6 +4,7 @@ package helium314.keyboard.keyboard.clipboard
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
@ -34,6 +35,7 @@ import helium314.keyboard.latin.utils.createToolbarKey
import helium314.keyboard.latin.utils.getCodeForToolbarKey
import helium314.keyboard.latin.utils.getCodeForToolbarKeyLongClick
import helium314.keyboard.latin.utils.getEnabledClipboardToolbarKeys
import helium314.keyboard.latin.utils.setToolbarButtonsActivatedStateOnPrefChange
@SuppressLint("CustomViewStyleable")
class ClipboardHistoryView @JvmOverloads constructor(
@ -41,7 +43,8 @@ class ClipboardHistoryView @JvmOverloads constructor(
attrs: AttributeSet?,
defStyle: Int = R.attr.clipboardHistoryViewStyle
) : LinearLayout(context, attrs, defStyle), View.OnClickListener,
ClipboardHistoryManager.OnHistoryChangeListener, OnKeyEventListener, View.OnLongClickListener {
ClipboardHistoryManager.OnHistoryChangeListener, OnKeyEventListener,
View.OnLongClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
private val clipboardLayoutParams = ClipboardLayoutParams(context)
private val pinIconId: Int
@ -65,10 +68,6 @@ class ClipboardHistoryView @JvmOverloads constructor(
keyBackgroundId = keyboardViewAttr.getResourceId(R.styleable.KeyboardView_keyBackground, 0)
keyboardViewAttr.recycle()
val keyboardAttr = context.obtainStyledAttributes(attrs, R.styleable.Keyboard, defStyle, R.style.SuggestionStripView)
// todo (maybe): setting the correct color only works because the activated state is inverted
// even when state is activated, the not activated color is set
// in suggestionStripView the same thing works correctly, wtf?
// need to properly fix it (and maybe undo the inverted isActivated) when adding a toggle key
getEnabledClipboardToolbarKeys(DeviceProtectedUtils.getSharedPreferences(context))
.forEach { toolbarKeys.add(createToolbarKey(context, KeyboardIconsSet.instance, it)) }
keyboardAttr.recycle()
@ -156,7 +155,8 @@ class ClipboardHistoryView @JvmOverloads constructor(
val params = KeyDrawParams()
params.updateParams(clipboardLayoutParams.bottomRowKeyboardHeight, keyVisualAttr)
Settings.getInstance().getCustomTypeface()?.let { params.mTypeface = it }
val settings = Settings.getInstance()
settings.getCustomTypeface()?.let { params.mTypeface = it }
setupClipKey(params)
setupBottomRowKeyboard(editorInfo, keyboardActionListener)
@ -167,8 +167,24 @@ class ClipboardHistoryView @JvmOverloads constructor(
}
clipboardRecyclerView.apply {
adapter = clipboardAdapter
layoutParams.width = ResourceUtils.getKeyboardWidth(context, Settings.getInstance().current)
val keyboardWidth = ResourceUtils.getKeyboardWidth(context, settings.current)
layoutParams.width = keyboardWidth
// set side padding
val keyboardAttr = context.obtainStyledAttributes(
null, R.styleable.Keyboard, R.attr.keyboardStyle, R.style.Keyboard);
val leftPadding = (keyboardAttr.getFraction(R.styleable.Keyboard_keyboardLeftPadding,
keyboardWidth, keyboardWidth, 0f)
* settings.current.mSidePaddingScale).toInt()
val rightPadding = (keyboardAttr.getFraction(R.styleable.Keyboard_keyboardRightPadding,
keyboardWidth, keyboardWidth, 0f)
* settings.current.mSidePaddingScale).toInt()
keyboardAttr.recycle()
setPadding(leftPadding, paddingTop, rightPadding, paddingBottom)
}
// absurd workaround so Android sets the correct color from stateList (depending on "activated")
toolbarKeys.forEach { it.isEnabled = false; it.isEnabled = true }
}
fun stopClipboardHistory() {
@ -233,4 +249,8 @@ class ClipboardHistoryView @JvmOverloads constructor(
clipboardAdapter.notifyItemChanged(to)
if (to < from) clipboardRecyclerView.smoothScrollToPosition(to)
}
override fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String?) {
setToolbarButtonsActivatedStateOnPrefChange(KeyboardSwitcher.getInstance().clipboardStrip, key)
}
}

View file

@ -173,6 +173,8 @@ final class EmojiCategory {
public void clearKeyboardCache() {
mCategoryKeyboardMap.clear();
for (CategoryProperties props: mShownCategories)
props.mPageCount = -1; // reset page count in case size (number of keys per row) changed
}
private void addShownCategoryId(final int categoryId) {

View file

@ -18,6 +18,7 @@ public final class EmojiCategoryPageIndicatorView extends View {
private int mCategoryPageSize = 0;
private int mCurrentCategoryPageId = 0;
private float mOffset = 0.0f;
int mWidth = 0;
public EmojiCategoryPageIndicatorView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
@ -49,12 +50,13 @@ public final class EmojiCategoryPageIndicatorView extends View {
return;
}
final float height = getHeight();
final float width = getWidth();
final float leftPadding = getPaddingLeft();
final float width = mWidth - leftPadding - getPaddingRight();
final float unitWidth = width / mCategoryPageSize;
final float left = Math.min(unitWidth * mCurrentCategoryPageId + mOffset * unitWidth, width - unitWidth);
final float top = 0.0f;
final float right = Math.min(left + unitWidth, width);
final float bottom = height * BOTTOM_MARGIN_RATIO;
canvas.drawRect(left, top, right, bottom, mPaint);
canvas.drawRect(left + leftPadding, top, right + leftPadding, bottom, mPaint);
}
}

View file

@ -38,7 +38,7 @@ import helium314.keyboard.latin.RichInputMethodSubtype;
import helium314.keyboard.latin.common.ColorType;
import helium314.keyboard.latin.common.Colors;
import helium314.keyboard.latin.settings.Settings;
import helium314.keyboard.latin.utils.DeviceProtectedUtils;
import helium314.keyboard.latin.settings.SettingsValues;
import helium314.keyboard.latin.utils.ResourceUtils;
import org.jetbrains.annotations.NotNull;
@ -117,6 +117,7 @@ public final class EmojiPalettesView extends LinearLayout
+ getPaddingLeft() + getPaddingRight();
final int height = ResourceUtils.getKeyboardHeight(res, Settings.getInstance().getCurrent())
+ getPaddingTop() + getPaddingBottom();
mEmojiCategoryPageIndicatorView.mWidth = width;
setMeasuredDimension(width, height);
}
@ -272,6 +273,7 @@ public final class EmojiPalettesView extends LinearLayout
mEmojiRecyclerView.setAdapter(mEmojiPalettesAdapter);
setCurrentCategoryAndPageId(mEmojiCategory.getCurrentCategoryId(), mEmojiCategory.getCurrentCategoryPageId(), true);
}
setupSidePadding();
}
private void setupBottomRowKeyboard(final EditorInfo editorInfo, final KeyboardActionListener keyboardActionListener) {
@ -283,6 +285,31 @@ public final class EmojiPalettesView extends LinearLayout
keyboardView.setKeyboard(keyboard);
}
private void setupSidePadding() {
final SettingsValues sv = Settings.getInstance().getCurrent();
final int keyboardWidth = ResourceUtils.getKeyboardWidth(getContext(), sv);
final TypedArray keyboardAttr = getContext().obtainStyledAttributes(
null, R.styleable.Keyboard, R.attr.keyboardStyle, R.style.Keyboard);
final float leftPadding = keyboardAttr.getFraction(R.styleable.Keyboard_keyboardLeftPadding,
keyboardWidth, keyboardWidth, 0f) * sv.mSidePaddingScale;
final float rightPadding = keyboardAttr.getFraction(R.styleable.Keyboard_keyboardRightPadding,
keyboardWidth, keyboardWidth, 0f) * sv.mSidePaddingScale;
keyboardAttr.recycle();
mEmojiRecyclerView.setPadding(
(int) leftPadding,
mEmojiRecyclerView.getPaddingTop(),
(int) rightPadding,
mEmojiRecyclerView.getPaddingBottom()
);
mEmojiCategoryPageIndicatorView.setPadding(
(int) leftPadding,
mEmojiCategoryPageIndicatorView.getPaddingTop(),
(int) rightPadding,
mEmojiCategoryPageIndicatorView.getPaddingBottom()
);
// setting width does not do anything, so we have some workaround in EmojiCategoryPageIndicatorView
}
public void stopEmojiPalettes() {
if (!initialized) return;
mEmojiPalettesAdapter.releaseCurrentKey(true);

View file

@ -57,8 +57,8 @@ public class KeyPreviewView extends AppCompatTextView {
setCompoundDrawables(null, null, null, null);
setTextColor(drawParams.mPreviewTextColor);
setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams));
// wie hier machen?
setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams)
* Settings.getInstance().getCurrent().mFontSizeMultiplier);
setTypeface(mTypeface == null ? key.selectPreviewTypeface(drawParams) : mTypeface);
// TODO Should take care of temporaryShiftLabel here.
setTextAndScaleX(key.getPreviewLabel());

View file

@ -52,8 +52,9 @@ public final class KeyboardCodesSet {
"key_emoji",
"key_unspecified",
"key_clipboard",
"key_start_onehanded",
"key_stop_onehanded",
"key_toggle_onehanded",
"key_start_onehanded", // keep name to avoid breaking custom layouts
"key_stop_onehanded", // keep name to avoid breaking custom layouts
"key_switch_onehanded"
};
@ -77,8 +78,9 @@ public final class KeyboardCodesSet {
KeyCode.EMOJI,
KeyCode.NOT_SPECIFIED,
KeyCode.CLIPBOARD,
KeyCode.START_ONE_HANDED_MODE,
KeyCode.STOP_ONE_HANDED_MODE,
KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.SWITCH_ONE_HANDED_MODE
};

View file

@ -225,10 +225,12 @@ public class KeyboardParams {
mBottomPadding = (int) (keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardBottomPadding, height, height, 0)
* Settings.getInstance().getCurrent().mBottomPaddingScale);
mLeftPadding = (int) keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardLeftPadding, width, width, 0);
mRightPadding = (int) keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardRightPadding, width, width, 0);
mLeftPadding = (int) (keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardLeftPadding, width, width, 0)
* Settings.getInstance().getCurrent().mSidePaddingScale);
mRightPadding = (int) (keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardRightPadding, width, width, 0)
* Settings.getInstance().getCurrent().mSidePaddingScale);
mBaseWidth = mOccupiedWidth - mLeftPadding - mRightPadding;
final float defaultKeyWidthFactor = context.getResources().getInteger(R.integer.config_screen_metrics) > 2 ? 0.9f : 1f;
@ -238,7 +240,6 @@ public class KeyboardParams {
mDefaultAbsoluteKeyWidth = (int) (mDefaultKeyWidth * mBaseWidth);
mAbsolutePopupKeyWidth = (int) (alphaSymbolKeyWidth * mBaseWidth);
// todo: maybe settings should not be accessed from here?
if (Settings.getInstance().getCurrent().mNarrowKeyGaps) {
mRelativeHorizontalGap = keyboardAttr.getFraction(
R.styleable.Keyboard_horizontalGapNarrow, 1, 1, 0);

View file

@ -808,10 +808,8 @@ public final class KeyboardState {
toggleNumpad(false, autoCapsFlags, recapitalizeMode, false, true);
} else if (code == KeyCode.SYMBOL) {
setSymbolsKeyboard();
} else if (code == KeyCode.START_ONE_HANDED_MODE) {
setOneHandedModeEnabled(true);
} else if (code == KeyCode.STOP_ONE_HANDED_MODE) {
setOneHandedModeEnabled(false);
} else if (code == KeyCode.TOGGLE_ONE_HANDED_MODE) {
setOneHandedModeEnabled(!Settings.getInstance().getCurrent().mOneHandedModeEnabled);
} else if (code == KeyCode.SWITCH_ONE_HANDED_MODE) {
switchOneHandedMode();
}

View file

@ -42,7 +42,8 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
// determine key width for default settings (no number row, no one-handed mode, 100% height and bottom padding scale)
// this is a bit long, but ensures that emoji size stays the same, independent of these settings
val defaultKeyWidth = (ResourceUtils.getDefaultKeyboardWidth(context) - params.mLeftPadding - params.mRightPadding) * params.mDefaultKeyWidth
// we also ignore side padding for key width, and prefer fewer keys per row over narrower keys
val defaultKeyWidth = ResourceUtils.getDefaultKeyboardWidth(context) * params.mDefaultKeyWidth
val keyWidth = defaultKeyWidth * sqrt(Settings.getInstance().current.mKeyboardHeightScale)
val defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(context.resources, false)
val defaultBottomPadding = context.resources.getFraction(R.fraction.config_keyboard_bottom_padding_holo, defaultKeyboardHeight, defaultKeyboardHeight)

View file

@ -17,12 +17,10 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData
import helium314.keyboard.latin.common.isEmoji
import helium314.keyboard.latin.define.DebugFlags
import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX
import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT
import helium314.keyboard.latin.utils.POPUP_KEYS_NUMBER
import helium314.keyboard.latin.utils.ScriptUtils
import helium314.keyboard.latin.utils.ScriptUtils.script
import helium314.keyboard.latin.utils.getCustomLayoutFiles
import helium314.keyboard.latin.utils.replaceFirst
import helium314.keyboard.latin.utils.splitAt
import helium314.keyboard.latin.utils.sumOf
@ -88,9 +86,11 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
addNumberRowOrPopupKeys(baseKeys, numberRow)
if (params.mId.isAlphabetKeyboard)
addSymbolPopupKeys(baseKeys)
if (params.mId.isAlphaOrSymbolKeyboard && params.mId.mNumberRowEnabled)
baseKeys.add(0, numberRow
.mapTo(mutableListOf()) { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) })
if (params.mId.isAlphaOrSymbolKeyboard && params.mId.mNumberRowEnabled) {
val newLabelFlags = defaultLabelFlags or
if (Settings.getInstance().current.mShowNumberRowHints) 0 else Key.LABEL_FLAGS_DISABLE_HINT_LABEL
baseKeys.add(0, numberRow.mapTo(mutableListOf()) { it.copy(newLabelFlags = newLabelFlags) })
}
if (!params.mAllowRedundantPopupKeys)
params.baseKeys = baseKeys.flatMap { it.map { it.toKeyParams(params) } }

View file

@ -125,8 +125,8 @@ object KeyCode {
// heliboard only codes
const val SYMBOL_ALPHA = -10001
const val START_ONE_HANDED_MODE = -10002
const val STOP_ONE_HANDED_MODE = -10003
const val TOGGLE_ONE_HANDED_MODE = -10002
const val TOGGLE_ONE_HANDED_MODE_2 = -10003 // does the same as TOGGLE_ONE_HANDED_MODE (used to be start & stop)
const val SWITCH_ONE_HANDED_MODE = -10004
const val SHIFT_ENTER = -10005
const val ACTION_NEXT = -10006
@ -179,7 +179,7 @@ object KeyCode {
FN, CLIPBOARD_CLEAR_HISTORY, NUMPAD,
// heliboard only
SYMBOL_ALPHA, START_ONE_HANDED_MODE, STOP_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER,
SYMBOL_ALPHA, TOGGLE_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER,
ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, WORD_LEFT, WORD_RIGHT, PAGE_UP,
PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT,
MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK
@ -189,6 +189,7 @@ object KeyCode {
IME_UI_MODE_TEXT -> ALPHA
VIEW_PHONE -> ALPHA // phone keyboard is treated like alphabet, just with different layout
VIEW_PHONE2 -> SYMBOL
TOGGLE_ONE_HANDED_MODE_2 -> TOGGLE_ONE_HANDED_MODE
else -> throw IllegalStateException("key code $this not yet supported")
}

View file

@ -127,7 +127,7 @@ sealed interface KeyData : AbstractKeyData {
if (!params.mId.mLanguageSwitchKeyEnabled && !params.mId.isNumberLayout && RichInputMethodManager.canSwitchLanguage())
keys.add("!icon/language_switch_key|!code/key_language_switch")
if (!params.mId.mOneHandedModeEnabled)
keys.add("!icon/start_onehanded_mode_key|!code/key_start_onehanded")
keys.add("!icon/start_onehanded_mode_key|!code/key_toggle_onehanded")
if (!params.mId.mDeviceLocked)
keys.add("!icon/settings_key|!code/key_settings")
return keys

View file

@ -84,8 +84,7 @@ class KeyboardWrapperView @JvmOverloads constructor(
if (newScale == oldScale) return@setOnTouchListener true
Settings.getInstance().writeOneHandedModeScale(newScale)
oneHandedModeEnabled = false // intentionally putting wrong value, so KeyboardSwitcher.setOneHandedModeEnabled does actually reload
keyboardActionListener?.onCodeInput(KeyCode.START_ONE_HANDED_MODE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false)
KeyboardSwitcher.getInstance().setOneHandedModeEnabled(true)
}
else -> x = 0f
}
@ -119,7 +118,7 @@ class KeyboardWrapperView @JvmOverloads constructor(
override fun onClick(view: View) {
if (view === stopOneHandedModeBtn) {
keyboardActionListener?.onCodeInput(KeyCode.STOP_ONE_HANDED_MODE,
keyboardActionListener?.onCodeInput(KeyCode.TOGGLE_ONE_HANDED_MODE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
false /* isKeyRepeat */)
} else if (view === switchOneHandedModeBtn) {

View file

@ -150,7 +150,11 @@ class DynamicColors(context: Context, override val themeStyle: String, override
private val spaceBarStateList: ColorStateList
private val adjustedBackgroundStateList: ColorStateList
private val stripBackgroundList: ColorStateList
private val toolbarKeyStateList = activatedStateList(keyText, darken(darken(keyText)))
private val toolbarKeyStateList = activatedStateList(
keyText,
if (isBrightColor(keyText)) darken(darken(keyText))
else brighten(brighten(keyText))
)
/** darkened variant of [accent] because the accent color is always light for dynamic colors */
private val adjustedAccent: Int = darken(accent)
@ -195,12 +199,12 @@ class DynamicColors(context: Context, override val themeStyle: String, override
}
adjustedBackgroundStateList =
if (themeStyle == STYLE_HOLO) {
stateList(accent, adjustedBackground)
pressedStateList(accent, adjustedBackground)
} else if (isNight) {
if (hasKeyBorders) stateList(doubleAdjustedAccent, keyBackground)
else stateList(adjustedAccent, adjustedKeyBackground)
if (hasKeyBorders) pressedStateList(doubleAdjustedAccent, keyBackground)
else pressedStateList(adjustedAccent, adjustedKeyBackground)
} else {
stateList(accent, Color.WHITE)
pressedStateList(accent, Color.WHITE)
}
val stripBackground = if (keyboardBackground == null && !hasKeyBorders) {
@ -210,7 +214,7 @@ class DynamicColors(context: Context, override val themeStyle: String, override
}
val pressedStripElementBackground = if (keyboardBackground == null) adjustedBackground
else if (isDarkColor(background)) 0x22ffffff else 0x11000000
stripBackgroundList = stateList(pressedStripElementBackground, stripBackground)
stripBackgroundList = pressedStateList(pressedStripElementBackground, stripBackground)
adjustedBackgroundFilter =
if (themeStyle == STYLE_HOLO) colorFilter(adjustedBackground)
@ -218,47 +222,47 @@ class DynamicColors(context: Context, override val themeStyle: String, override
if (hasKeyBorders) {
backgroundStateList =
if (!isNight) stateList(adjustedFunctionalKey, background)
else stateList(adjustedKeyBackground, background)
if (!isNight) pressedStateList(adjustedFunctionalKey, background)
else pressedStateList(adjustedKeyBackground, background)
keyStateList =
if (!isNight) stateList(adjustedBackground, keyBackground)
else stateList(adjustedKeyBackground, keyBackground)
if (!isNight) pressedStateList(adjustedBackground, keyBackground)
else pressedStateList(adjustedKeyBackground, keyBackground)
functionalKeyStateList =
if (!isNight) stateList(doubleAdjustedFunctionalKey, functionalKey)
else stateList(functionalKey, doubleAdjustedKeyBackground)
if (!isNight) pressedStateList(doubleAdjustedFunctionalKey, functionalKey)
else pressedStateList(functionalKey, doubleAdjustedKeyBackground)
actionKeyStateList =
if (!isNight) stateList(gesture, accent)
else stateList(doubleAdjustedAccent, accent)
if (!isNight) pressedStateList(gesture, accent)
else pressedStateList(doubleAdjustedAccent, accent)
spaceBarStateList =
if (themeStyle == STYLE_HOLO) stateList(spaceBar, spaceBar)
if (themeStyle == STYLE_HOLO) pressedStateList(spaceBar, spaceBar)
else keyStateList
} else {
// need to set color to background if key borders are disabled, or there will be ugly keys
backgroundStateList =
if (!isNight) stateList(adjustedFunctionalKey, background)
else stateList(adjustedKeyBackground, background)
if (!isNight) pressedStateList(adjustedFunctionalKey, background)
else pressedStateList(adjustedKeyBackground, background)
keyStateList =
if (!isNight) stateList(adjustedFunctionalKey, Color.TRANSPARENT)
else stateList(functionalKey, Color.TRANSPARENT)
if (!isNight) pressedStateList(adjustedFunctionalKey, Color.TRANSPARENT)
else pressedStateList(functionalKey, Color.TRANSPARENT)
functionalKeyStateList =
if (themeStyle == STYLE_HOLO) stateList(functionalKey, Color.TRANSPARENT)
if (themeStyle == STYLE_HOLO) pressedStateList(functionalKey, Color.TRANSPARENT)
else keyStateList
actionKeyStateList =
if (themeStyle == STYLE_HOLO) stateList(accent, Color.TRANSPARENT)
else if (!isNight) stateList(gesture, accent)
else stateList(doubleAdjustedAccent, accent)
if (themeStyle == STYLE_HOLO) pressedStateList(accent, Color.TRANSPARENT)
else if (!isNight) pressedStateList(gesture, accent)
else pressedStateList(doubleAdjustedAccent, accent)
spaceBarStateList =
if (!isNight) stateList(gesture, adjustedFunctionalKey)
else stateList(adjustedKeyBackground, spaceBar)
if (!isNight) pressedStateList(gesture, adjustedFunctionalKey)
else pressedStateList(adjustedKeyBackground, spaceBar)
}
keyTextFilter = colorFilter(keyText)
@ -398,7 +402,11 @@ class DefaultColors (
private val spaceBarStateList: ColorStateList
private val adjustedBackgroundStateList: ColorStateList
private val stripBackgroundList: ColorStateList
private val toolbarKeyStateList = activatedStateList(suggestionText, darken(darken(suggestionText)))
private val toolbarKeyStateList = activatedStateList(
suggestionText,
if (isBrightColor(suggestionText)) darken(darken(suggestionText))
else brighten(brighten(suggestionText))
)
private var backgroundSetupDone = false
init {
@ -409,7 +417,7 @@ class DefaultColors (
adjustedBackground = darken(background)
doubleAdjustedBackground = darken(adjustedBackground)
}
adjustedBackgroundStateList = stateList(doubleAdjustedBackground, adjustedBackground)
adjustedBackgroundStateList = pressedStateList(doubleAdjustedBackground, adjustedBackground)
val stripBackground: Int
val pressedStripElementBackground: Int
@ -424,7 +432,7 @@ class DefaultColors (
stripBackground = adjustedBackground
pressedStripElementBackground = doubleAdjustedBackground
}
stripBackgroundList = stateList(pressedStripElementBackground, stripBackground)
stripBackgroundList = pressedStateList(pressedStripElementBackground, stripBackground)
if (themeStyle == STYLE_HOLO && keyboardBackground == null) {
val darkerBackground = adjustLuminosityAndKeepAlpha(background, -0.2f)
@ -437,22 +445,22 @@ class DefaultColors (
adjustedBackgroundFilter = colorFilter(adjustedBackground)
if (hasKeyBorders) {
backgroundStateList = stateList(brightenOrDarken(background, true), background)
keyStateList = if (themeStyle == STYLE_HOLO) stateList(keyBackground, keyBackground)
else stateList(brightenOrDarken(keyBackground, true), keyBackground)
functionalKeyStateList = stateList(brightenOrDarken(functionalKey, true), functionalKey)
backgroundStateList = pressedStateList(brightenOrDarken(background, true), background)
keyStateList = if (themeStyle == STYLE_HOLO) pressedStateList(keyBackground, keyBackground)
else pressedStateList(brightenOrDarken(keyBackground, true), keyBackground)
functionalKeyStateList = pressedStateList(brightenOrDarken(functionalKey, true), functionalKey)
actionKeyStateList = if (themeStyle == STYLE_HOLO) functionalKeyStateList
else stateList(brightenOrDarken(accent, true), accent)
spaceBarStateList = if (themeStyle == STYLE_HOLO) stateList(spaceBar, spaceBar)
else stateList(brightenOrDarken(spaceBar, true), spaceBar)
else pressedStateList(brightenOrDarken(accent, true), accent)
spaceBarStateList = if (themeStyle == STYLE_HOLO) pressedStateList(spaceBar, spaceBar)
else pressedStateList(brightenOrDarken(spaceBar, true), spaceBar)
} else {
// need to set color to background if key borders are disabled, or there will be ugly keys
backgroundStateList = stateList(brightenOrDarken(background, true), background)
keyStateList = stateList(keyBackground, Color.TRANSPARENT)
backgroundStateList = pressedStateList(brightenOrDarken(background, true), background)
keyStateList = pressedStateList(keyBackground, Color.TRANSPARENT)
functionalKeyStateList = keyStateList
actionKeyStateList = if (themeStyle == STYLE_HOLO) functionalKeyStateList
else stateList(brightenOrDarken(accent, true), accent)
spaceBarStateList = stateList(brightenOrDarken(spaceBar, true), spaceBar)
else pressedStateList(brightenOrDarken(accent, true), accent)
spaceBarStateList = pressedStateList(brightenOrDarken(spaceBar, true), spaceBar)
}
keyTextFilter = colorFilter(keyText)
actionKeyIconColorFilter = when {
@ -556,7 +564,7 @@ class AllColors(private val colorMap: EnumMap<ColorType, Int>, override val them
override fun get(color: ColorType): Int = colorMap[color] ?: color.default()
override fun setColor(drawable: Drawable, color: ColorType) {
val colorStateList = stateListMap.getOrPut(color) { stateList(brightenOrDarken(get(color), true), get(color)) }
val colorStateList = stateListMap.getOrPut(color) { pressedStateList(brightenOrDarken(get(color), true), get(color)) }
DrawableCompat.setTintMode(drawable, PorterDuff.Mode.MULTIPLY)
DrawableCompat.setTintList(drawable, colorStateList)
}
@ -621,14 +629,14 @@ private fun colorFilter(color: Int, mode: BlendModeCompat = BlendModeCompat.MODU
return BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, mode)!!
}
private fun stateList(pressed: Int, normal: Int): ColorStateList {
private fun pressedStateList(pressed: Int, normal: Int): ColorStateList {
val states = arrayOf(intArrayOf(android.R.attr.state_pressed), intArrayOf(-android.R.attr.state_pressed))
return ColorStateList(states, intArrayOf(pressed, normal))
}
private fun activatedStateList(normal: Int, activated: Int): ColorStateList {
val states = arrayOf(intArrayOf(-android.R.attr.state_activated), intArrayOf(android.R.attr.state_activated))
return ColorStateList(states, intArrayOf(normal, activated))
private fun activatedStateList(activated: Int, normal: Int): ColorStateList {
val states = arrayOf(intArrayOf(android.R.attr.state_activated), intArrayOf(-android.R.attr.state_activated))
return ColorStateList(states, intArrayOf(activated, normal))
}
enum class ColorType {

View file

@ -224,8 +224,7 @@ public final class Constants {
case CODE_TAB: return "tab";
case CODE_ENTER: return "enter";
case CODE_SPACE: return "space";
case KeyCode.START_ONE_HANDED_MODE: return "startOneHandedMode";
case KeyCode.STOP_ONE_HANDED_MODE: return "stopOneHandedMode";
case KeyCode.TOGGLE_ONE_HANDED_MODE: return "toggleOneHandedMode";
case KeyCode.SWITCH_ONE_HANDED_MODE: return "switchOneHandedMode";
case KeyCode.NUMPAD: return "numpad";
default:

View file

@ -62,21 +62,32 @@ fun getFullEmojiAtEnd(s: CharSequence): String {
while (offset > 0) {
val codepoint = text.codePointBefore(offset)
// stop if codepoint can't be emoji
if (!mightBeEmoji(codepoint)) return ""
if (!mightBeEmoji(codepoint))
return text.substring(offset)
offset -= Character.charCount(codepoint)
// todo: if codepoint in 0x1F3FB..0x1F3FF -> combine with other emojis in front, but only if they actually combine
// why isn't this done with zwj like everything else? skin tones can be emojis by themselves...
if (offset > 0 && text[offset - 1].code == KeyCode.ZWJ) {
// todo: this appends ZWJ in weird cases like text, ZWJ, emoji
// and detects single ZWJ as emoji (at least irrelevant for current use of getFullEmojiAtEnd)
offset -= 1
continue
}
if (codepoint in 0x1F3FB..0x1F3FF) {
// Skin tones are not added with ZWJ, but just appended. This is not nice as they can be emojis on their own,
// but that's how it is done. Assume that an emoji before the skin tone will get merged (usually correct in practice)
val codepointBefore = text.codePointBefore(offset)
if (isEmoji(codepointBefore)) {
offset -= Character.charCount(codepointBefore)
continue
}
}
// check the whole text after offset
val textToCheck = text.substring(offset)
if (isEmoji(textToCheck)) {
return textToCheck
}
}
return ""
return text.substring(offset)
}
/** split the string on the first of consecutive space only, further consecutive spaces are added to the next split */

View file

@ -779,7 +779,7 @@ public final class InputLogic {
// We need to switch to the shortcut IME. This is handled by LatinIME since the
// input logic has no business with IME switching.
case KeyCode.CAPS_LOCK, KeyCode.SYMBOL_ALPHA, KeyCode.ALPHA, KeyCode.SYMBOL, KeyCode.NUMPAD, KeyCode.EMOJI,
KeyCode.START_ONE_HANDED_MODE, KeyCode.STOP_ONE_HANDED_MODE, KeyCode.SWITCH_ONE_HANDED_MODE,
KeyCode.TOGGLE_ONE_HANDED_MODE, KeyCode.SWITCH_ONE_HANDED_MODE,
KeyCode.CTRL, KeyCode.ALT, KeyCode.FN, KeyCode.META:
break;
default:
@ -1212,7 +1212,12 @@ public final class InputLogic {
}
return;
}
if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
// todo: this is currently disabled, as it causes inconsistencies with textInput, depending whether the end
// is part of a word (where we start composing) or not (where we end in code below)
// see https://github.com/Helium314/HeliBoard/issues/1019
// with better emoji detection on backspace (getFullEmojiAtEnd), this functionality might not be necessary
// -> enable again if there are issues, otherwise delete the code, together with mEnteredText
if (false && mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
// Cancel multi-character input: remove the text we just entered.
// This is triggered on backspace after a key that inputs multiple characters,
// like the smiley key or the .com key.

View file

@ -25,6 +25,7 @@ import androidx.preference.PreferenceManager
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import helium314.keyboard.dictionarypack.DictionaryPackConstants
import helium314.keyboard.keyboard.KeyboardActionListener
import helium314.keyboard.latin.utils.ChecksumCalculator
import helium314.keyboard.keyboard.KeyboardLayoutSet
import helium314.keyboard.keyboard.KeyboardSwitcher
@ -125,6 +126,8 @@ class AdvancedSettingsFragment : SubScreenFragment() {
}
setupKeyLongpressTimeoutSettings()
setupEmojiSdkSetting()
setupLanguageSwipeDistanceSettings()
updateLangSwipeDistanceVisibility(sharedPreferences)
findPreference<Preference>("load_gesture_library")?.setOnPreferenceClickListener { onClickLoadLibrary() }
findPreference<Preference>("backup_restore")?.setOnPreferenceClickListener { showBackupRestoreDialog() }
@ -560,10 +563,37 @@ class AdvancedSettingsFragment : SubScreenFragment() {
})
}
private fun setupLanguageSwipeDistanceSettings() {
val prefs = sharedPreferences
findPreference<SeekBarDialogPreference>(Settings.PREF_LANGUAGE_SWIPE_DISTANCE)?.setInterface(object : ValueProxy {
override fun writeValue(value: Int, key: String) = prefs.edit().putInt(key, value).apply()
override fun writeDefaultValue(key: String) = prefs.edit().remove(key).apply()
override fun readValue(key: String) = Settings.readLanguageSwipeDistance(prefs, resources)
override fun readDefaultValue(key: String) = Settings.readDefaultLanguageSwipeDistance(resources)
override fun getValueText(value: Int) = value.toString()
override fun feedbackValue(value: Int) {}
})
}
private fun updateLangSwipeDistanceVisibility(prefs: SharedPreferences) {
val horizontalSpaceSwipe = Settings.readHorizontalSpaceSwipe(prefs)
val verticalSpaceSwipe = Settings.readVerticalSpaceSwipe(prefs)
val visibility = horizontalSpaceSwipe == KeyboardActionListener.SWIPE_SWITCH_LANGUAGE
|| verticalSpaceSwipe == KeyboardActionListener.SWIPE_SWITCH_LANGUAGE
setPreferenceVisible(Settings.PREF_LANGUAGE_SWIPE_DISTANCE, visibility)
}
override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String?) {
when (key) {
Settings.PREF_SHOW_SETUP_WIZARD_ICON -> SystemBroadcastReceiver.toggleAppIcon(requireContext())
Settings.PREF_MORE_POPUP_KEYS -> KeyboardLayoutSet.onSystemLocaleChanged()
Settings.PREF_SPACE_HORIZONTAL_SWIPE -> updateLangSwipeDistanceVisibility(prefs)
Settings.PREF_SPACE_VERTICAL_SWIPE -> updateLangSwipeDistanceVisibility(prefs)
Settings.PREF_EMOJI_MAX_SDK -> KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext())
}
}

View file

@ -105,6 +105,11 @@ class AppearanceSettingsFragment : SubScreenFragment() {
setupScalePrefs(Settings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_BOTTOM_PADDING_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, 0f)
setupScalePrefs(Settings.PREF_FONT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_EMOJI_FONT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_SIDE_PADDING_SCALE, 0f)
setupScalePrefs(Settings.PREF_SIDE_PADDING_SCALE_LANDSCAPE, 0f)
if (splitScalePref != null) {
setupScalePrefs(Settings.PREF_SPLIT_SPACER_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
splitScalePref?.isVisible = splitPref?.isChecked == true

View file

@ -54,6 +54,7 @@ public final class PreferencesSettingsFragment extends SubScreenFragment {
setupHistoryRetentionTimeSettings();
refreshEnablingsOfKeypressSoundAndVibrationAndHistRetentionSettings();
setLocalizedNumberRowVisibility();
setNumberRowHintsVisibility();
findPreference(Settings.PREF_POPUP_KEYS_LABELS_ORDER).setVisible(getSharedPreferences().getBoolean(Settings.PREF_SHOW_HINTS, false));
findPreference(Settings.PREF_POPUP_KEYS_ORDER).setOnPreferenceClickListener((pref) -> {
DialogUtilsKt.reorderDialog(requireContext(), Settings.PREF_POPUP_KEYS_ORDER,
@ -77,12 +78,18 @@ public final class PreferencesSettingsFragment extends SubScreenFragment {
refreshEnablingsOfKeypressSoundAndVibrationAndHistRetentionSettings();
if (key == null) return;
switch (key) {
case Settings.PREF_POPUP_KEYS_ORDER, Settings.PREF_SHOW_POPUP_HINTS, Settings.PREF_SHOW_NUMBER_ROW,
case Settings.PREF_POPUP_KEYS_ORDER, Settings.PREF_SHOW_POPUP_HINTS, Settings.PREF_SHOW_NUMBER_ROW_HINTS,
Settings.PREF_POPUP_KEYS_LABELS_ORDER, Settings.PREF_LANGUAGE_SWITCH_KEY,
Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY , Settings.PREF_REMOVE_REDUNDANT_POPUPS-> mReloadKeyboard = true;
Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, Settings.PREF_REMOVE_REDUNDANT_POPUPS -> mReloadKeyboard = true;
case Settings.PREF_SHOW_NUMBER_ROW -> {
setNumberRowHintsVisibility();
mReloadKeyboard = true;
}
case Settings.PREF_LOCALIZED_NUMBER_ROW -> KeyboardLayoutSet.onSystemLocaleChanged();
case Settings.PREF_SHOW_HINTS
-> findPreference(Settings.PREF_POPUP_KEYS_LABELS_ORDER).setVisible(prefs.getBoolean(Settings.PREF_SHOW_HINTS, false));
case Settings.PREF_SHOW_HINTS -> {
findPreference(Settings.PREF_POPUP_KEYS_LABELS_ORDER).setVisible(prefs.getBoolean(Settings.PREF_SHOW_HINTS, false));
setNumberRowHintsVisibility();
}
}
}
@ -108,6 +115,12 @@ public final class PreferencesSettingsFragment extends SubScreenFragment {
pref.setVisible(false);
}
private void setNumberRowHintsVisibility() {
var prefs = getSharedPreferences();
setPreferenceVisible(Settings.PREF_SHOW_NUMBER_ROW_HINTS, prefs.getBoolean(Settings.PREF_SHOW_HINTS, false)
&& prefs.getBoolean(Settings.PREF_SHOW_NUMBER_ROW, false));
}
private void refreshEnablingsOfKeypressSoundAndVibrationAndHistRetentionSettings() {
final SharedPreferences prefs = getSharedPreferences();
final Resources res = getResources();

View file

@ -112,6 +112,11 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_SPLIT_SPACER_SCALE = "split_spacer_scale";
public static final String PREF_KEYBOARD_HEIGHT_SCALE = "keyboard_height_scale";
public static final String PREF_BOTTOM_PADDING_SCALE = "bottom_padding_scale";
public static final String PREF_BOTTOM_PADDING_SCALE_LANDSCAPE = "bottom_padding_scale_landscape";
public static final String PREF_SIDE_PADDING_SCALE = "side_padding_scale";
public static final String PREF_SIDE_PADDING_SCALE_LANDSCAPE = "side_padding_scale_landscape";
public static final String PREF_FONT_SCALE = "font_scale";
public static final String PREF_EMOJI_FONT_SCALE = "emoji_font_scale";
public static final String PREF_SPACE_HORIZONTAL_SWIPE = "horizontal_space_swipe";
public static final String PREF_SPACE_VERTICAL_SWIPE = "vertical_space_swipe";
public static final String PREF_DELETE_SWIPE = "delete_swipe";
@ -142,6 +147,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_SHOW_NUMBER_ROW = "show_number_row";
public static final String PREF_LOCALIZED_NUMBER_ROW = "localized_number_row";
public static final String PREF_SHOW_NUMBER_ROW_HINTS = "show_number_row_hints";
public static final String PREF_CUSTOM_CURRENCY_KEY = "custom_currency_key";
public static final String PREF_SHOW_HINTS = "show_hints";
@ -151,6 +157,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_MORE_POPUP_KEYS = "more_popup_keys";
public static final String PREF_SPACE_TO_CHANGE_LANG = "prefs_long_press_keyboard_to_change_lang";
public static final String PREF_LANGUAGE_SWIPE_DISTANCE = "language_swipe_distance";
public static final String PREF_ENABLE_CLIPBOARD_HISTORY = "enable_clipboard_history";
public static final String PREF_CLIPBOARD_HISTORY_RETENTION_TIME = "clipboard_history_retention_time";
@ -447,6 +454,18 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
};
}
public static int readLanguageSwipeDistance(final SharedPreferences prefs,
final Resources res) {
final int sensitivity = prefs.getInt(
PREF_LANGUAGE_SWIPE_DISTANCE, UNDEFINED_PREFERENCE_VALUE_INT);
return (sensitivity != UNDEFINED_PREFERENCE_VALUE_INT) ? sensitivity
: readDefaultLanguageSwipeDistance(res);
}
public static int readDefaultLanguageSwipeDistance(final Resources res) {
return 5;
}
public static boolean readDeleteSwipeEnabled(final SharedPreferences prefs) {
return prefs.getBoolean(PREF_DELETE_SWIPE, true);
}
@ -499,6 +518,18 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
(getCurrent().mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT), gravity).apply();
}
public static float readBottomPaddingScale(final SharedPreferences prefs, final boolean landscape) {
if (landscape)
return prefs.getFloat(PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, 0f);
return prefs.getFloat(PREF_BOTTOM_PADDING_SCALE, SettingsValues.DEFAULT_SIZE_SCALE);
}
public static float readSidePaddingScale(final SharedPreferences prefs, final boolean landscape) {
if (landscape)
return prefs.getFloat(PREF_SIDE_PADDING_SCALE_LANDSCAPE, 0f);
return prefs.getFloat(PREF_SIDE_PADDING_SCALE, 0f);
}
public static boolean readHasHardwareKeyboard(final Configuration conf) {
// The standard way of finding out whether we have a hardware keyboard. This code is taken
// from InputMethodService#onEvaluateInputShown, which canonically determines this.

View file

@ -70,6 +70,7 @@ public class SettingsValues {
private final boolean mShowsLanguageSwitchKey;
public final boolean mShowsNumberRow;
public final boolean mLocalizedNumberRow;
public final boolean mShowNumberRowHints;
public final boolean mShowsHints;
public final boolean mShowsPopupHints;
public final boolean mSpaceForLangChange;
@ -80,6 +81,7 @@ public class SettingsValues {
public final boolean mBlockPotentiallyOffensive;
public final int mSpaceSwipeHorizontal;
public final int mSpaceSwipeVertical;
public final int mLanguageSwipeDistance;
public final boolean mDeleteSwipeEnabled;
public final boolean mAutospaceAfterPunctuationEnabled;
public final boolean mClipboardHistoryEnabled;
@ -113,6 +115,7 @@ public class SettingsValues {
public final float mKeyboardHeightScale;
public final boolean mUrlDetectionEnabled;
public final float mBottomPaddingScale;
public final float mSidePaddingScale;
public final boolean mAutoShowToolbar;
public final boolean mAutoHideToolbar;
public final boolean mAlphaAfterEmojiInEmojiView;
@ -120,6 +123,8 @@ public class SettingsValues {
public final boolean mAlphaAfterSymbolAndSpace;
public final boolean mRemoveRedundantPopups;
public final String mSpaceBarText;
public final float mFontSizeMultiplier;
public final float mFontSizeMultiplierEmoji;
// From the input box
@NonNull
@ -171,6 +176,7 @@ public class SettingsValues {
mShowsLanguageSwitchKey = prefs.getBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, false); // only relevant for default functional key layout
mShowsNumberRow = prefs.getBoolean(Settings.PREF_SHOW_NUMBER_ROW, false);
mLocalizedNumberRow = prefs.getBoolean(Settings.PREF_LOCALIZED_NUMBER_ROW, true);
mShowNumberRowHints = prefs.getBoolean(Settings.PREF_SHOW_NUMBER_ROW_HINTS, false);
mShowsHints = prefs.getBoolean(Settings.PREF_SHOW_HINTS, true);
mShowsPopupHints = prefs.getBoolean(Settings.PREF_SHOW_POPUP_HINTS, false);
mSpaceForLangChange = prefs.getBoolean(Settings.PREF_SPACE_TO_CHANGE_LANG, true);
@ -227,6 +233,7 @@ public class SettingsValues {
mDisplayOrientation = res.getConfiguration().orientation;
mSpaceSwipeHorizontal = Settings.readHorizontalSpaceSwipe(prefs);
mSpaceSwipeVertical = Settings.readVerticalSpaceSwipe(prefs);
mLanguageSwipeDistance = Settings.readLanguageSwipeDistance(prefs, res);
mDeleteSwipeEnabled = Settings.readDeleteSwipeEnabled(prefs);
mAutospaceAfterPunctuationEnabled = Settings.readAutospaceAfterPunctuationEnabled(prefs);
mClipboardHistoryEnabled = Settings.readClipboardHistoryEnabled(prefs);
@ -262,7 +269,8 @@ public class SettingsValues {
prefs.getBoolean(Settings.PREF_GESTURE_SPACE_AWARE, false)
);
mSpacingAndPunctuations = new SpacingAndPunctuations(res, mUrlDetectionEnabled);
mBottomPaddingScale = prefs.getFloat(Settings.PREF_BOTTOM_PADDING_SCALE, DEFAULT_SIZE_SCALE);
mBottomPaddingScale = Settings.readBottomPaddingScale(prefs, mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE);
mSidePaddingScale = Settings.readSidePaddingScale(prefs, mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE);
mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, false);
mAutoShowToolbar = prefs.getBoolean(Settings.PREF_AUTO_SHOW_TOOLBAR, false);
mAutoHideToolbar = readSuggestionsEnabled(prefs) && prefs.getBoolean(Settings.PREF_AUTO_HIDE_TOOLBAR, false);
@ -273,6 +281,8 @@ public class SettingsValues {
mRemoveRedundantPopups = prefs.getBoolean(Settings.PREF_REMOVE_REDUNDANT_POPUPS, false);
mSpaceBarText = prefs.getString(Settings.PREF_SPACE_BAR_TEXT, "");
mEmojiMaxSdk = prefs.getInt(Settings.PREF_EMOJI_MAX_SDK, Build.VERSION.SDK_INT);
mFontSizeMultiplier = prefs.getFloat(Settings.PREF_FONT_SCALE, DEFAULT_SIZE_SCALE);
mFontSizeMultiplierEmoji = prefs.getFloat(Settings.PREF_EMOJI_FONT_SCALE, DEFAULT_SIZE_SCALE);
}
public boolean isApplicationSpecifiedCompletionsOn() {

View file

@ -70,7 +70,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public final class SuggestionStripView extends RelativeLayout implements OnClickListener,
OnLongClickListener {
OnLongClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
public interface Listener {
void pickSuggestionManually(SuggestedWordInfo word);
void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat);
@ -231,6 +231,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
colors.setBackground(this, ColorType.STRIP_BACKGROUND);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, @Nullable String key) {
ToolbarUtilsKt.setToolbarButtonsActivatedStateOnPrefChange(mPinnedKeys, key);
ToolbarUtilsKt.setToolbarButtonsActivatedStateOnPrefChange(mToolbar, key);
}
/**
* A connection back to the input method.
*/
@ -647,11 +653,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (code != KeyCode.UNSPECIFIED) {
Log.d(TAG, "click toolbar key "+tag);
mListener.onCodeInput(code, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, false);
if (tag == ToolbarKey.INCOGNITO || tag == ToolbarKey.AUTOCORRECT || tag == ToolbarKey.ONE_HANDED) {
if (tag == ToolbarKey.INCOGNITO)
updateKeys(); // update icon
view.setActivated(!view.isActivated());
}
updateKeys(); // update expand key icon
return;
}
}

View file

@ -6,6 +6,7 @@ import android.content.Context
import android.content.DialogInterface
import android.content.SharedPreferences
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageButton
import android.widget.ImageView
@ -16,6 +17,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat
import androidx.core.view.forEach
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged
@ -26,7 +28,9 @@ import helium314.keyboard.latin.R
import helium314.keyboard.latin.databinding.ReorderDialogItemBinding
import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.ToolbarKey.*
import kotlinx.serialization.encodeToString
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import java.util.EnumMap
import java.util.Locale
@ -38,14 +42,31 @@ fun createToolbarKey(context: Context, iconsSet: KeyboardIconsSet, key: ToolbarK
val contentDescriptionId = context.resources.getIdentifier(key.name.lowercase(), "string", context.packageName)
if (contentDescriptionId != 0)
button.contentDescription = context.getString(contentDescriptionId)
button.isActivated = !when (key) {
INCOGNITO -> Settings.readAlwaysIncognitoMode(DeviceProtectedUtils.getSharedPreferences(context))
setToolbarButtonActivatedState(button)
button.setImageDrawable(iconsSet.getNewDrawable(key.name, context))
return button
}
fun setToolbarButtonsActivatedStateOnPrefChange(buttonsGroup: ViewGroup, key: String?) {
// settings need to be updated when buttons change
if (key != Settings.PREF_AUTO_CORRECTION
&& key != Settings.PREF_ALWAYS_INCOGNITO_MODE
&& key?.startsWith(Settings.PREF_ONE_HANDED_MODE_PREFIX) == false)
return
GlobalScope.launch {
delay(10) // need to wait until SettingsValues are reloaded
buttonsGroup.forEach { if (it is ImageButton) setToolbarButtonActivatedState(it) }
}
}
private fun setToolbarButtonActivatedState(button: ImageButton) {
button.isActivated = when (button.tag) {
INCOGNITO -> Settings.readAlwaysIncognitoMode(DeviceProtectedUtils.getSharedPreferences(button.context))
ONE_HANDED -> Settings.getInstance().current.mOneHandedModeEnabled
AUTOCORRECT -> Settings.getInstance().current.mAutoCorrectionEnabledPerUserSettings
else -> true
}
button.setImageDrawable(iconsSet.getNewDrawable(key.name, context))
return button
}
fun getCodeForToolbarKey(key: ToolbarKey) = Settings.getInstance().getCustomToolbarKeyCode(key) ?: when (key) {
@ -60,7 +81,7 @@ fun getCodeForToolbarKey(key: ToolbarKey) = Settings.getInstance().getCustomTool
COPY -> KeyCode.CLIPBOARD_COPY
CUT -> KeyCode.CLIPBOARD_CUT
PASTE -> KeyCode.CLIPBOARD_PASTE
ONE_HANDED -> if (Settings.getInstance().current.mOneHandedModeEnabled) KeyCode.STOP_ONE_HANDED_MODE else KeyCode.START_ONE_HANDED_MODE
ONE_HANDED -> KeyCode.TOGGLE_ONE_HANDED_MODE
INCOGNITO -> KeyCode.TOGGLE_INCOGNITO_MODE
AUTOCORRECT -> KeyCode.TOGGLE_AUTOCORRECT
CLEAR_CLIPBOARD -> KeyCode.CLIPBOARD_CLEAR_HISTORY

View file

@ -377,6 +377,7 @@
<string name="prefs_long_press_symbol_for_numpad">নম্বর প্যাডের জন্য প্রতীক বোতামে দীর্ঘ চাপ</string>
<string name="var_toolbar_direction">টুলবারের পরিবর্তনশীল দিক</string>
<string name="var_toolbar_direction_summary">ডান থেকে বাম কিবোর্ড নির্বাচন করলে দিক বিপরীত হবে</string>
<string name="prefs_language_swipe_distance">ভাষা পরিবর্তন অভিস্পর্শের দূরত্ব</string>
<string name="subtype_generic_student"><xliff:g id="LANGUAGE_NAME" example="Russian">%s</xliff:g> (শিক্ষার্থী)</string>
<string name="language_switch_key_behavior">ভাষা পরিবর্তন বোতামের আচরণ</string>
<string name="pinned_toolbar_keys">পিন করে রাখা টুলবার বোতাম নির্বাচন</string>

View file

@ -21,7 +21,7 @@
<dimen name="config_popup_keys_keyboard_slide_allowance">53.76dp</dimen>
<fraction name="config_keyboard_top_padding_holo">2.727%p</fraction>
<fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction>
<fraction name="config_keyboard_bottom_padding_holo">2.5%p</fraction>
<fraction name="config_key_vertical_gap_holo">5.368%p</fraction>
<fraction name="config_key_horizontal_gap_holo">1.020%p</fraction>
<fraction name="config_key_vertical_gap_holo_narrow">4.85%p</fraction>

View file

@ -15,7 +15,7 @@
<dimen name="config_popup_keys_keyboard_key_height">81.9dp</dimen>
<fraction name="config_keyboard_top_padding_holo">2.727%p</fraction>
<fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction>
<fraction name="config_keyboard_bottom_padding_holo">2.0%p</fraction>
<fraction name="config_key_vertical_gap_holo">4.5%p</fraction>
<fraction name="config_key_horizontal_gap_holo">0.9%p</fraction>
<fraction name="config_key_vertical_gap_holo_narrow">4.5%p</fraction>

View file

@ -79,8 +79,8 @@
<integer name="config_touch_noise_threshold_time">40</integer>
<!-- Common keyboard configuration. -->
<fraction name="config_keyboard_left_padding">0%p</fraction>
<fraction name="config_keyboard_right_padding">0%p</fraction>
<fraction name="config_keyboard_left_padding">8%p</fraction>
<fraction name="config_keyboard_right_padding">8%p</fraction>
<dimen name="config_keyboard_vertical_correction">0.0dp</dimen>
<!-- Common key top visual configuration. -->

View file

@ -86,6 +86,9 @@
<!-- Description for Arabic (PC) subtype. -->
<string name="subtype_arabic_pc" translatable="false">%s (PC)</string>
<!-- Description for Arabic (Hija'i) subtype. -->
<string name="subtype_arabic_hijai" translatable="false">%s (Hija\'i)</string>
<!-- Description for Hebrew 2 subtype. -->
<string name="subtype_hebrew_1452_2" translatable="false">%s (1452-2)</string>

View file

@ -239,6 +239,8 @@
<string name="localized_number_row">Localize number row</string>
<!-- Description of the settings to localize number row -->
<string name="localized_number_row_summary">Prefer localized over latin numbers</string>
<!-- Title of the setting to enable number row hints -->
<string name="number_row_hints">Show hints on number row</string>
<!-- Title of the setting to show key hints -->
<string name="show_hints">Show key hints</string>
<!-- Description of the settings to show hints -->
@ -311,6 +313,16 @@
<string name="prefs_keyboard_height_scale">Keyboard height scale</string>
<!-- Title of the setting for setting bottom padding height -->
<string name="prefs_bottom_padding_scale">Bottom padding scale</string>
<!-- Title of the setting for setting bottom padding height in landscape mode -->
<string name="prefs_bottom_padding_scale_landscape">Bottom padding scale (landscape)</string>
<!-- Title of the setting for adjusting font size on the keyboard -->
<!-- Title of the setting for setting side padding -->
<string name="prefs_side_padding_scale">Side padding scale</string>
<!-- Title of the setting for setting side padding in landscape mode -->
<string name="prefs_side_padding_scale_landscape">Side padding scale (landscape)</string>
<string name="prefs_font_scale">Keyboard font scale</string>
<!-- Title of the setting for adjusting font size in emoji view -->
<string name="prefs_emoji_font_scale">Emoji view font scale</string>
<!-- Title of the setting for customizing space bar text -->
<string name="prefs_space_bar_text">Custom text on space bar</string>
<!-- Title of the setting for adding / removing custom font file -->
@ -946,6 +958,8 @@ New dictionary:
<string name="var_toolbar_direction">Variable toolbar direction</string>
<!-- Description of the variable toolbar direction setting -->
<string name="var_toolbar_direction_summary">Reverse direction when a right-to-left keyboard subtype is selected</string>
<!-- Title of the settings for adjusting the language swipe gesture distance -->
<string name="prefs_language_swipe_distance">Switch language swipe distance</string>
<!-- Title of the setting to customize toolbar key codes -->
<string name="customize_toolbar_key_codes">Customize toolbar key codes</string>
<!-- Confirmation message when resetting all custom toolbar key codes -->

View file

@ -97,6 +97,8 @@
>
<item name="keyboardTopPadding">0%p</item>
<item name="keyboardBottomPadding">0%p</item>
<item name="keyboardLeftPadding">0%p</item>
<item name="keyboardRightPadding">0%p</item>
<item name="horizontalGap">0%p</item>
<item name="horizontalGapNarrow">0%p</item>
<item name="touchPositionCorrectionData">@null</item>

View file

@ -81,6 +81,8 @@
>
<item name="keyboardTopPadding">0%p</item>
<item name="keyboardBottomPadding">0%p</item>
<item name="keyboardLeftPadding">0%p</item>
<item name="keyboardRightPadding">0%p</item>
<item name="horizontalGap">0%p</item>
<item name="horizontalGapNarrow">0%p</item>
<item name="touchPositionCorrectionData">@null</item>

View file

@ -90,6 +90,8 @@
>
<item name="keyboardTopPadding">0%p</item>
<item name="keyboardBottomPadding">0%p</item>
<item name="keyboardLeftPadding">0%p</item>
<item name="keyboardRightPadding">0%p</item>
<item name="horizontalGap">0%p</item>
<item name="horizontalGapNarrow">0%p</item>
<item name="touchPositionCorrectionData">@null</item>

View file

@ -178,6 +178,15 @@
android:imeSubtypeExtraValue="KeyboardLayoutSet=arabic_pc,NoShiftKey,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher"
android:label="@string/subtype_arabic_hijai"
android:subtypeId="0x590dde42"
android:imeSubtypeLocale="ar"
android:languageTag="ar"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=arabic_hijai,NoShiftKey,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher"
android:label="@string/subtype_generic"
android:subtypeId="0x70b0f974"

View file

@ -43,6 +43,13 @@
android:title="@string/show_vertical_space_swipe"
latin:singleLineTitle="false" />
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="language_swipe_distance"
android:title="@string/prefs_language_swipe_distance"
latin:minValue="2"
latin:maxValue="18"
latin:stepValue="1" />
<SwitchPreference
android:key="delete_swipe"
android:title="@string/delete_swipe"

View file

@ -111,6 +111,27 @@
latin:minValue="0"
latin:maxValue="500" /> <!-- percentage -->
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="bottom_padding_scale_landscape"
android:title="@string/prefs_bottom_padding_scale_landscape"
latin:defaultValue="0"
latin:minValue="0"
latin:maxValue="500" /> <!-- percentage -->
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="side_padding_scale"
android:title="@string/prefs_side_padding_scale"
latin:defaultValue="0"
latin:minValue="0"
latin:maxValue="300" /> <!-- percentage -->
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="side_padding_scale_landscape"
android:title="@string/prefs_side_padding_scale_landscape"
latin:defaultValue="0"
latin:minValue="0"
latin:maxValue="300" /> <!-- percentage -->
<EditTextPreference
android:key="space_bar_text"
android:title="@string/prefs_space_bar_text"
@ -123,6 +144,18 @@
android:defaultValue=""
android:persistent="true" />
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="font_scale"
android:title="@string/prefs_font_scale"
latin:minValue="50"
latin:maxValue="150" /> <!-- percentage -->
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="emoji_font_scale"
android:title="@string/prefs_emoji_font_scale"
latin:minValue="50"
latin:maxValue="150" /> <!-- percentage -->
</PreferenceCategory>
</PreferenceScreen>

View file

@ -85,6 +85,12 @@
android:defaultValue="true"
android:persistent="true" />
<SwitchPreference
android:key="show_number_row_hints"
android:title="@string/number_row_hints"
android:defaultValue="false"
android:persistent="true" />
<SwitchPreference
android:key="show_language_switch_key"
android:title="@string/show_language_switch_key"

View file

@ -613,6 +613,57 @@ class InputLogicTest {
assertEquals("{\"label\": \"c", text)
}
@Test fun `text input and delete`() {
reset()
input("hello")
assertEquals("hello", text)
functionalKeyPress(KeyCode.DELETE)
assertEquals("hell", text)
reset()
input("hello ")
assertEquals("hello ", text)
functionalKeyPress(KeyCode.DELETE)
assertEquals("hello", text)
}
@Test fun `emoji text input and delete`() {
reset()
input("🕵🏼")
functionalKeyPress(KeyCode.DELETE)
assertEquals("", text)
reset()
input("\uD83D\uDD75\uD83C\uDFFC")
input(' ')
assertEquals("🕵🏼 ", text)
functionalKeyPress(KeyCode.DELETE)
functionalKeyPress(KeyCode.DELETE)
assertEquals("", text)
}
@Test fun `revert autocorrect on delete`() {
reset()
chainInput("hullo")
getAutocorrectedWithSpaceAfter("hello", "hullo")
functionalKeyPress(KeyCode.DELETE)
assertEquals("hullo", text)
// todo: now we want some way to disable revert on backspace, either per setting or something else
// need to avoid getting into the mLastComposedWord.canRevertCommit() part of handleBackspaceEvent
}
@Test fun `remove glide typing word on delete`() {
reset()
glideTypingInput("hello")
assertEquals("hello", text)
functionalKeyPress(KeyCode.DELETE)
assertEquals("", text)
// todo: now we want some way to disable delete-all on backspace, either per setting or something else
// need to avoid getting into the mWordComposer.isBatchMode() part of handleBackspaceEvent
}
// ------- helper functions ---------
// should be called before every test, so the same state is guaranteed
@ -646,6 +697,7 @@ class InputLogicTest {
if (currentScript != ScriptUtils.SCRIPT_HANGUL // check fails if hangul combiner merges symbols
&& !(codePoint == Constants.CODE_SPACE && oldBefore.lastOrNull() == ' ') // check fails when 2 spaces are converted into a period
&& !latinIME.mInputLogic.mSuggestedWords.mWillAutoCorrect // autocorrect obviously creates inconsistencies
) {
if (phantomSpaceToInsert.isEmpty())
assertEquals(oldBefore + insert, textBeforeCursor)
@ -751,6 +803,22 @@ class InputLogicTest {
checkConnectionConsistency()
}
// only works when autocorrect is on, separator after word is required
private fun getAutocorrectedWithSpaceAfter(suggestion: String, typedWord: String?) {
val info = SuggestedWordInfo(suggestion, "", 0, 0, null, 0, 0)
val typedInfo = SuggestedWordInfo(typedWord, "", 0, 0, null, 0, 0)
val sw = SuggestedWords(ArrayList(listOf(typedInfo, info)), null, typedInfo, false, true, false, 0, 0)
latinIME.mInputLogic.setSuggestedWords(sw)
input(' ')
checkConnectionConsistency()
}
private fun glideTypingInput(word: String) {
val info = SuggestedWordInfo(word, "", 0, 0, null, 0, 0)
val sw = SuggestedWords(ArrayList(listOf(info)), null, info, true, false, false, 0, 0)
latinIME.mInputLogic.onUpdateTailBatchInputCompleted(settingsValues, sw, KeyboardSwitcher.getInstance())
}
private fun checkConnectionConsistency() {
// RichInputConnection only has composing text up to cursor, but InputConnection has full composing text
val expectedConnectionComposingText = if (composingStart == -1 || composingEnd == -1) ""
@ -969,11 +1037,12 @@ private val ic = object : InputConnection {
it.selectionEnd = selectionEnd
}
}
// only effect is flashing, so whatever...
override fun commitCorrection(p0: CorrectionInfo?): Boolean = true
// implement only when necessary
override fun getCursorCapsMode(p0: Int): Int = TODO("Not yet implemented")
override fun deleteSurroundingTextInCodePoints(p0: Int, p1: Int): Boolean = TODO("Not yet implemented")
override fun commitCompletion(p0: CompletionInfo?): Boolean = TODO("Not yet implemented")
override fun commitCorrection(p0: CorrectionInfo?): Boolean = TODO("Not yet implemented")
override fun performEditorAction(p0: Int): Boolean = TODO("Not yet implemented")
override fun performContextMenuAction(p0: Int): Boolean = TODO("Not yet implemented")
override fun clearMetaKeyStates(p0: Int): Boolean = TODO("Not yet implemented")

View file

@ -42,6 +42,8 @@ class StringUtilsTest {
}
@Test fun detectEmojisAtEnd() {
assertEquals("", getFullEmojiAtEnd("\uD83C\uDF83 "))
assertEquals("", getFullEmojiAtEnd("a"))
assertEquals("\uD83C\uDF83", getFullEmojiAtEnd("\uD83C\uDF83"))
assertEquals("", getFullEmojiAtEnd(""))
assertEquals("", getFullEmojiAtEnd(""))
@ -51,6 +53,15 @@ class StringUtilsTest {
assertEquals("\uD83C\uDFF3\u200D\uD83C\uDF08", getFullEmojiAtEnd("\uD83C\uDFF3\u200D\uD83C\uDF08"))
assertEquals("\uD83C\uDFF3\u200D\uD83C\uDF08", getFullEmojiAtEnd("\uD83C\uDFF4\u200D☠️\uD83C\uDFF3\u200D\uD83C\uDF08"))
assertEquals("\uD83C\uDFF3\u200D⚧️", getFullEmojiAtEnd("hello there🏳"))
assertEquals("\uD83D\uDD75\uD83C\uDFFC", getFullEmojiAtEnd(" 🕵🏼"))
assertEquals("\uD83D\uDD75\uD83C\uDFFC", getFullEmojiAtEnd("🕵🏼"))
assertEquals("\uD83C\uDFFC", getFullEmojiAtEnd(" \uD83C\uDFFC"))
// fails, but unlikely enough that we leave it unfixed
//assertEquals("\uD83C\uDFFC", getFullEmojiAtEnd("\uD83C\uDF84\uD83C\uDFFC"))
// below also fail, because ZWJ handling is not suitable for some unusual cases
//assertEquals("", getFullEmojiAtEnd("\u200D"))
//assertEquals("", getFullEmojiAtEnd("a\u200D"))
//assertEquals("\uD83D\uDE22", getFullEmojiAtEnd(" \u200D\uD83D\uDE22"))
}
// todo: add tests for emoji detection?

View file

@ -1,9 +1,11 @@
A compilation of information about the layout formats usable in this app.
# A compilation of information about the layout formats usable in this app.
There are two distinct formats:
* the _simple_ format is a text file with one key label per line, and two consecutive line breaks indicating a switch to the next row, [example](app/src/main/assets/layouts/qwerty.txt)
* the _json_ format taken from [FlorisBoard](https://github.com/florisboard/florisboard/blob/master/CONTRIBUTING.md#adding-the-layout), but only "normal" keys are supported (i.e. no action keys and similar), [example](app/src/main/assets/layouts/azerty.json)
You can add both directly in the app, see the related [FAQ](https://github.com/Helium314/HeliBoard/wiki/Customization#layouts).
## General notes
Adding too many keys or too long texts will make the keyboard look awkward or broken, and even crash the app under some specific conditions (popup keys are especially prone for this).
There are some sanity checks when adding a layout to avoid such issues, but they do not cover all possible cases.