Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca4cd38bb2 | ||
|
|
7046c500ff | ||
|
|
0374a82f99 | ||
|
|
217acbd6f1 | ||
|
|
ef27d511be | ||
|
|
f9a4ffa5eb | ||
|
|
5533badd19 | ||
|
|
0f1b4b081d | ||
|
|
3feae09df0 | ||
|
|
34bb28d1fc | ||
|
|
551a294b05 | ||
|
|
671ff1d8b4 | ||
|
|
15caf66370 | ||
|
|
ae0a8e551b | ||
|
|
cb4bedfc2c | ||
|
|
7d63a6885c | ||
|
|
841d797b7c | ||
|
|
0c9ba5326a | ||
|
|
7c5a7dc148 | ||
|
|
37fc714729 | ||
|
|
ec7d65ebc0 | ||
|
|
5670af16d6 | ||
|
|
6b39a846e6 |
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,34 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help fix a bug
|
||||
about: Create a report to help FlorisBoard improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Short description of bug
|
||||
A short but clear and concise description of what the bug is.
|
||||
<!--
|
||||
- Describe the bug in a short but concise way.
|
||||
- If you have a screenshot or screen recording of the bug, link them at
|
||||
the end of this issue.
|
||||
- Please search existing bug reports to avoid creating duplicates.
|
||||
- Thank you for your help in making FlorisBoard better!
|
||||
-->
|
||||
|
||||
#### Steps to reproduce
|
||||
**Environment information**
|
||||
- FlorisBoard Version: <!-- e.g. 0.1.0 -->
|
||||
- Install Source: <!-- Google PlayStore/F-Droid/GitHub/? -->
|
||||
- Device: <!-- e.g. OnePlus 7T -->
|
||||
- Android version, ROM: <!-- e.g. 10, Stock -->
|
||||
|
||||
**Steps to reproduce**
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
#### Expected behavior
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
#### What happened instead?
|
||||
A detailed description of what you expected to happen. If you have screenshots or a screen recording, add it here.
|
||||
|
||||
#### Additional info
|
||||
- Version: [e.g. 0.1.0]
|
||||
- Source: [e.g. Google PlayStore/F-Droid/GitHub/?]
|
||||
- Device: [e.g. OnePlus 7T]
|
||||
- Android version, ROM: [e.g. 10, Stock]
|
||||
|
||||
#### Log
|
||||
<!--
|
||||
```
|
||||
If applicable, paste the captured debug log here.
|
||||
```
|
||||
-->
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: General feedback
|
||||
url: https://github.com/florisboard/florisboard/blob/master/CONTRIBUTING.md
|
||||
about: Give general feedback about this project
|
||||
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,11 +1,19 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea or enhancement for this project
|
||||
name: Feature request / Suggestion
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: proposal
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Describe your idea in a short but concise way. If you have multiple ideas which are not directly connected to each other, file an issue per idea. This makes it easy to implement one feature proposal at a time. If you have any examples, e.g. screenshots or other keyboards which have the proposed feature implemented, link them here.
|
||||
Thank you for your help in making FlorisBoard better!
|
||||
<!--
|
||||
- Describe your idea in a short but concise way.
|
||||
- If you have multiple ideas which are not directly connected to each
|
||||
other, file an issue per idea. This makes it easy to implement one
|
||||
feature proposal at a time.
|
||||
- If you have any examples, e.g. screenshots or other keyboards which
|
||||
have the proposed feature implemented, link them here.
|
||||
- Please search existing proposals to avoid creating duplicates.
|
||||
- Thank you for your help in making FlorisBoard better!
|
||||
-->
|
||||
|
||||
16
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask here if you have a question about FlorisBoard
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
- If you need assistance in using FlorisBoard, ask it here!
|
||||
- If you want to suggest an idea for this project, please use the
|
||||
Feature request template instead.
|
||||
- Please search existing questions to avoid creating duplicates.
|
||||
- Thank you for your help in making FlorisBoard better!
|
||||
-->
|
||||
@@ -5,6 +5,12 @@ First off, thanks for considering contributing to FlorisBoard!
|
||||
There are several ways to contribute to FlorisBoard. This document provides some
|
||||
general guidelines for each type of contribution.
|
||||
|
||||
## Giving general feedback
|
||||
|
||||
Either use the review function within Google Play or email me at
|
||||
[florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev). I
|
||||
love to hear from you!
|
||||
|
||||
## Adding a new feature or making large changes
|
||||
|
||||
If you intend to add a new feature or to make large changes, please discuss this
|
||||
@@ -51,6 +57,7 @@ Notes for tips below:
|
||||
will get accepted.
|
||||
|
||||
### Tips when updating a translation
|
||||
|
||||
- To update a translation, check the `strings.xml` in
|
||||
`app/src/main/res/values` for newly added strings and add them to the
|
||||
translation file in `app/src/main/res/values-<code>`
|
||||
|
||||
71
README.md
71
README.md
@@ -3,8 +3,16 @@
|
||||
An open-source keyboard for Android. Currently in alpha stage.
|
||||
|
||||
#### Public Alpha Test Programme
|
||||
Wanna try it out on your device? You can join the public alpha test
|
||||
programme on Google Play. To become a tester, follow these steps:
|
||||
Wanna try it out on your device? Use one of the following options:
|
||||
|
||||
_A. IzzySoft's repo for F-Droid_:
|
||||
|
||||
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" height="64" alt="IzzySoft repo badge">](https://apt.izzysoft.de/fdroid/index/apk/dev.patrickgold.florisboard)
|
||||
|
||||
_B. Google Play Public Alpha Test_:
|
||||
|
||||
You can join the public alpha test programme on Google Play. To become a
|
||||
tester, follow these steps:
|
||||
1. Join the
|
||||
[FlorisBoard Public Alpha Test](https://groups.google.com/g/florisboard-public-alpha-test)
|
||||
Google Group to be able to access the testing programme.
|
||||
@@ -18,65 +26,64 @@ programme on Google Play. To become a tester, follow these steps:
|
||||
4. Finished! You will receive future versions of FlorisBoard via Google
|
||||
Play.
|
||||
|
||||
_C. Use the APK provided in the release section of this repo_
|
||||
|
||||
##### Giving feedback
|
||||
If you want to give feedback to FlorisBoard, there are 2 ways to do so,
|
||||
as listed below:
|
||||
- *General feedback:* use the private feedback to developer section on
|
||||
the PlayStore listing.
|
||||
- *Bug reports or feature requests:* see the
|
||||
[contribution guidelines](CONTRIBUTING.md)
|
||||
If you want to give feedback to FlorisBoard, there are several ways to
|
||||
do so, as listed in the [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
Thank you for contributing to FlorisBoard!
|
||||
|
||||
##### Note on F-Droid release
|
||||
FlorisBoard is currently only available through Google Play, but it is
|
||||
planned to also release it via F-Droid later on. There is no exact
|
||||
timeline for this, but I aim for the 0.2.0 or 0.3.0 release.
|
||||
FlorisBoard is currently available through Google Play and IzzySoft's
|
||||
repo for F-Droid, but is currently in the inclusion process for the main
|
||||
F-Droid repo. Planned proper F-Droid release is version 0.3.0.
|
||||
|
||||
---
|
||||
|
||||

|
||||
<img src="https://patrickgold.dev/media/previews/florisboard.png"
|
||||
height="256" alt="Preview Image">
|
||||
|
||||
## Feature roadmap
|
||||
|
||||
### Basics
|
||||
* [x] Implementation of the keyboard core (InputMethodService)
|
||||
* [x] Own implementation of deprecated KeyboardView (base only)
|
||||
* [x] Custom implementation of deprecated KeyboardView (base only)
|
||||
* [x] Caps + Caps Lock
|
||||
* [x] Key popups
|
||||
* [x] Extended key popups (e.g. a -> á, à, ä, ...) (needs tweaks for
|
||||
emojis)
|
||||
* [x] Extended key popups (e.g. a -> á, à, ä, ...)
|
||||
* [x] Key press sound/vibration
|
||||
* [x] Portrait orientation support
|
||||
* [x] Landscape orientation support (needs tweaks)
|
||||
* [ ] Tablet screen support
|
||||
* [ ] Tablet screen support (0.4.0)
|
||||
|
||||
### Layouts
|
||||
* [x] Latin character layouts (QWERTY, QWERTZ, AZERTY, Swiss, Spanish,
|
||||
Norwegian, Swedish/Finnish, Icelandic, Danish)
|
||||
Norwegian, Swedish/Finnish, Icelandic, Danish); more coming in
|
||||
future versions
|
||||
* [x] Non-latin character layouts (Persian)
|
||||
* [x] Adapt to situation in app (password, url, text, etc. )
|
||||
* [x] Special character layout(s)
|
||||
* [x] Numeric layout
|
||||
* [x] Numeric layout (advanced)
|
||||
* [x] Phone number layout
|
||||
* [x] Emoji layout (popups buggy atm)
|
||||
* [x] Emoji layout (tweaks: 0.3.0)
|
||||
* [x] Emoticon layout
|
||||
* [ ] Kaomoji layout
|
||||
* [ ] Kaomoji layout (0.3.0)
|
||||
|
||||
### Preferences
|
||||
* [x] Setup wizard
|
||||
* [x] Preferences screen
|
||||
* [x] Customize look and behaviour of keyboard (currently only
|
||||
light/dark theme)
|
||||
* [x] Customize look and behaviour of keyboard
|
||||
* [x] Theme presets (currently only day/night theme)
|
||||
* [x] Theme customization
|
||||
* [ ] Theme import/export (?)
|
||||
* [ ] Theme import/export (0.4.0 or 0.5.0)
|
||||
* [x] Subtype selection (language/layout)
|
||||
* [x] Keyboard behaviour preferences
|
||||
* [ ] Text suggestion / Auto correct preferences
|
||||
* [ ] Gesture preferences
|
||||
* [ ] Text suggestion / Auto correct preferences (0.4.0 or 0.5.0)
|
||||
* [x] Gesture preferences (0.3.0)
|
||||
|
||||
### Composing suggestions
|
||||
### Composing suggestions (0.4.0 or 0.5.0)
|
||||
* [ ] Auto suggest words from precompiled dictionary
|
||||
* [ ] Auto suggest words from user dictionary
|
||||
* [ ] Auto suggest contacts
|
||||
@@ -85,17 +92,23 @@ timeline for this, but I aim for the 0.2.0 or 0.3.0 release.
|
||||
### Other useful features
|
||||
* [x] One-handed mode
|
||||
* [x] Clipboard/cursor tools
|
||||
* [ ] Floating keyboard
|
||||
* [ ] Gesture support
|
||||
* [ ] Glide typing (?)
|
||||
* [x] Integrated number row / symbols in character layouts (0.3.0)
|
||||
* [ ] Floating keyboard (0.4.0)
|
||||
* [x] Gesture support (0.3.0)
|
||||
* [ ] Glide typing (0.4.0)
|
||||
* [x] Full integration in IME service list of Android (xml/method)
|
||||
(integration is internal-only, because Android's default subtype
|
||||
implementation not really allows for dynamic language/layout
|
||||
pairs, only compile-time defined ones)
|
||||
* [ ] Description and settings reference in System Language & Input
|
||||
* [ ] (dev only) Generate well-structured documentation of code
|
||||
* [ ] ...
|
||||
|
||||
Note: (?) = not sure if it will be implemented
|
||||
Note:
|
||||
|
||||
(?) = not sure if it will be implemented
|
||||
|
||||
(0.x.0) = planned version when feature will be implemented.
|
||||
|
||||
## Used libraries, components and icons
|
||||
* [Google Flexbox Layout for Android](https://github.com/google/flexbox-layout)
|
||||
|
||||
@@ -10,8 +10,8 @@ android {
|
||||
applicationId "dev.patrickgold.florisboard"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 12
|
||||
versionName "0.2.0"
|
||||
versionCode 14
|
||||
versionName "0.2.2"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
@@ -13,37 +13,37 @@
|
||||
{ "code": 240, "label": "ð" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"l": [
|
||||
{ "code": 322, "label": "ł" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
"s": [
|
||||
@@ -52,9 +52,9 @@
|
||||
{ "code": 353, "label": "š" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
],
|
||||
@@ -69,6 +69,7 @@
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -83,14 +84,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 225, "label": "á" }
|
||||
@@ -13,46 +13,47 @@
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 299, "label": "ī" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 244, "label": "ô" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 347, "label": "ś" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -67,14 +68,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
@@ -13,44 +13,45 @@
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 299, "label": "ī" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 244, "label": "ô" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 223, "label": "ß" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -65,14 +66,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,43 +1,44 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 261, "label": "ą" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
@@ -45,20 +46,20 @@
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 242, "label": "ò" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 223, "label": "ß" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 64, "label": "@" },
|
||||
@@ -73,14 +74,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
{ "code": 1610, "label": "ي" }
|
||||
],
|
||||
"ا": [
|
||||
{ "code": 1570, "label": "آ" },
|
||||
{ "code": 1649, "label": "ٱ" },
|
||||
{ "code": 1569, "label": "ء" },
|
||||
{ "code": 1570, "label": "آ" },
|
||||
{ "code": 1571, "label": "أ" },
|
||||
{ "code": 1573, "label": "إ" }
|
||||
],
|
||||
@@ -44,8 +44,8 @@
|
||||
{ "code": 1577, "label": "ة" }
|
||||
],
|
||||
"ک": [
|
||||
{ "code": 1603, "label": "ك" },
|
||||
{ "code": 1706, "label": "ڪ"}
|
||||
{ "code": 1706, "label": "ڪ"},
|
||||
{ "code": 1603, "label": "ك" }
|
||||
],
|
||||
"ز": [
|
||||
{ "code": 1688, "label": "ژ" }
|
||||
@@ -54,6 +54,7 @@
|
||||
{ "code": 1572, "label": "ؤ" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 1611, "label": "ً" },
|
||||
{ "code": 1622, "label": "ٖ" },
|
||||
{ "code": 1648, "label": "ٰ" },
|
||||
{ "code": 1619, "label": "ٓ" },
|
||||
@@ -66,15 +67,14 @@
|
||||
{ "code": 1617, "label": "ّ" },
|
||||
{ "code": 1612, "label": "ٌ" },
|
||||
{ "code": 1613, "label": "ٍ" },
|
||||
{ "code": 1611, "label": "ً" },
|
||||
{ "code": 1620, "label": "ٔ" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".ir"},
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" },
|
||||
{ "code": -255, "label": ".ir"}
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 224, "label": "à" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
],
|
||||
"s": [
|
||||
@@ -42,15 +42,15 @@
|
||||
{ "code": 347, "label": "ś" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
],
|
||||
"z": [
|
||||
{ "code": 380, "label": "ż" },
|
||||
{ "code": 382, "label": "ž" },
|
||||
{ "code": 380, "label": "ż" },
|
||||
{ "code": 378, "label": "ź" }
|
||||
],
|
||||
"ä": [
|
||||
@@ -60,6 +60,7 @@
|
||||
{ "code": 248, "label": "ø" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -74,14 +75,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
@@ -44,18 +45,17 @@
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 339, "label": "œ" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 347, "label": "ś" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
],
|
||||
@@ -63,6 +63,7 @@
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -77,14 +78,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 229, "label": "å" }
|
||||
@@ -13,39 +13,39 @@
|
||||
{ "code": 240, "label": "ð" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
"t": [
|
||||
{ "code": 254, "label": "þ" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
],
|
||||
@@ -54,6 +54,7 @@
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -68,14 +69,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,37 +1,38 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 230, "label": "æ" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 237, "label": "í" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
@@ -39,17 +40,17 @@
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 243, "label": "ó" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -64,14 +65,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 224, "label": "à" }
|
||||
@@ -13,28 +13,28 @@
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
],
|
||||
@@ -45,6 +45,7 @@
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -59,14 +60,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 224, "label": "à" }
|
||||
@@ -13,11 +13,11 @@
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
@@ -25,19 +25,19 @@
|
||||
{ "code": 236, "label": "ì" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
],
|
||||
@@ -51,6 +51,7 @@
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -65,14 +66,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,41 +1,42 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 263, "label": "ć" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 263, "label": "ć" },
|
||||
{ "code": 269, "label": "č" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 281, "label": "ę" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 238, "label": "î" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
@@ -43,17 +44,17 @@
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -68,14 +69,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 261, "label": "ą" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
],
|
||||
"d": [
|
||||
@@ -19,36 +19,36 @@
|
||||
{ "code": 271, "label": "ď" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 281, "label": "ę" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 236, "label": "ì" }
|
||||
],
|
||||
"l": [
|
||||
{ "code": 322, "label": "ł" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 328, "label": "ň" },
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 328, "label": "ň" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
],
|
||||
"r": [
|
||||
@@ -65,9 +65,9 @@
|
||||
{ "code": 254, "label": "þ" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 251, "label": "û" }
|
||||
],
|
||||
@@ -76,18 +76,19 @@
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
],
|
||||
"z": [
|
||||
{ "code": 380, "label": "ż" },
|
||||
{ "code": 378, "label": "ź" },
|
||||
{ "code": 380, "label": "ż" },
|
||||
{ "code": 382, "label": "ž" }
|
||||
],
|
||||
"ä": [
|
||||
{ "code": 230, "label": "æ" }
|
||||
],
|
||||
"ö": [
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 339, "label": "œ" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
@@ -102,14 +103,13 @@
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ import android.view.inputmethod.CursorAnchorInfo
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputConnection
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import com.squareup.moshi.Json
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.media.MediaInputManager
|
||||
import dev.patrickgold.florisboard.ime.text.TextInputManager
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.settings.SettingsMainActivity
|
||||
@@ -398,6 +398,19 @@ class FlorisBoard : InputMethodService() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given [SwipeAction]. Ignores any [SwipeAction] but the ones relevant for this
|
||||
* class.
|
||||
*/
|
||||
fun executeSwipeAction(swipeAction: SwipeAction) {
|
||||
when (swipeAction) {
|
||||
SwipeAction.HIDE_KEYBOARD -> requestHideSelf(0)
|
||||
SwipeAction.SWITCH_TO_PREV_SUBTYPE -> switchToPrevSubtype()
|
||||
SwipeAction.SWITCH_TO_NEXT_SUBTYPE -> switchToNextSubtype()
|
||||
else -> textInputManager.executeSwipeAction(swipeAction)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the IME and launches [SettingsMainActivity].
|
||||
*/
|
||||
@@ -417,6 +430,11 @@ class FlorisBoard : InputMethodService() {
|
||||
return subtypeManager.subtypes.size > 1
|
||||
}
|
||||
|
||||
fun switchToPrevSubtype() {
|
||||
activeSubtype = subtypeManager.switchToPrevSubtype() ?: Subtype.DEFAULT
|
||||
onSubtypeChanged(activeSubtype)
|
||||
}
|
||||
|
||||
fun switchToNextSubtype() {
|
||||
activeSubtype = subtypeManager.switchToNextSubtype() ?: Subtype.DEFAULT
|
||||
onSubtypeChanged(activeSubtype)
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.widget.LinearLayout
|
||||
import android.widget.ViewFlipper
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.util.ViewLayoutUtils
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
@@ -83,11 +84,14 @@ class InputView : LinearLayout {
|
||||
"extra_tall" -> 1.15f
|
||||
else -> 1.00f
|
||||
}
|
||||
val height = (resources.getDimension(R.dimen.inputView_baseHeight) * heightFactor).roundToInt()
|
||||
var height = (resources.getDimension(R.dimen.inputView_baseHeight) * heightFactor).roundToInt()
|
||||
desiredInputViewHeight = height
|
||||
desiredSmartbarHeight = (0.16129 * height).roundToInt()
|
||||
desiredTextKeyboardViewHeight = height - desiredSmartbarHeight
|
||||
desiredMediaKeyboardViewHeight = height
|
||||
// Add bottom offset for curved screens here. As the desired heights have already been set,
|
||||
// adding a value to the height now will result in a bottom padding (aka offset).
|
||||
height += ViewLayoutUtils.convertDpToPixel(florisboard.prefs.keyboard.bottomOffset.toFloat(), context).toInt()
|
||||
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ import android.content.SharedPreferences
|
||||
import android.provider.Settings
|
||||
import androidx.preference.PreferenceManager
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.DistanceThreshold
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.VelocityThreshold
|
||||
import dev.patrickgold.florisboard.util.VersionName
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
@@ -37,6 +40,8 @@ class PrefHelper(
|
||||
|
||||
val advanced = Advanced(this)
|
||||
val correction = Correction(this)
|
||||
val gestures = Gestures(this)
|
||||
val glide = Glide(this)
|
||||
val internal = Internal(this)
|
||||
val keyboard = Keyboard(this)
|
||||
val localization = Localization(this)
|
||||
@@ -175,14 +180,81 @@ class PrefHelper(
|
||||
*/
|
||||
class Correction(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val AUTO_CAPITALIZATION = "correction__auto_capitalization"
|
||||
const val DOUBLE_SPACE_PERIOD = "correction__double_space_period"
|
||||
}
|
||||
|
||||
var autoCapitalization: Boolean = false
|
||||
get() = prefHelper.getPref(AUTO_CAPITALIZATION, true)
|
||||
private set
|
||||
|
||||
var doubleSpacePeriod: Boolean = false
|
||||
get() = prefHelper.getPref(DOUBLE_SPACE_PERIOD, true)
|
||||
private set
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for gestures preferences.
|
||||
*/
|
||||
class Gestures(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val SWIPE_UP = "gestures__swipe_up"
|
||||
const val SWIPE_DOWN = "gestures__swipe_down"
|
||||
const val SWIPE_LEFT = "gestures__swipe_left"
|
||||
const val SWIPE_RIGHT = "gestures__swipe_right"
|
||||
const val SPACE_BAR_SWIPE_LEFT = "gestures__space_bar_swipe_left"
|
||||
const val SPACE_BAR_SWIPE_RIGHT = "gestures__space_bar_swipe_right"
|
||||
const val DELETE_KEY_SWIPE_LEFT = "gestures__delete_key_swipe_left"
|
||||
const val SWIPE_VELOCITY_THRESHOLD = "gestures__swipe_velocity_threshold"
|
||||
const val SWIPE_DISTANCE_THRESHOLD = "gestures__swipe_distance_threshold"
|
||||
}
|
||||
|
||||
var swipeUp: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SWIPE_UP, "no_action"))
|
||||
set(v) = prefHelper.setPref(SWIPE_UP, v)
|
||||
var swipeDown: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SWIPE_DOWN, "no_action"))
|
||||
set(v) = prefHelper.setPref(SWIPE_DOWN, v)
|
||||
var swipeLeft: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SWIPE_LEFT, "no_action"))
|
||||
set(v) = prefHelper.setPref(SWIPE_LEFT, v)
|
||||
var swipeRight: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SWIPE_RIGHT, "no_action"))
|
||||
set(v) = prefHelper.setPref(SWIPE_RIGHT, v)
|
||||
var spaceBarSwipeLeft: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SPACE_BAR_SWIPE_LEFT, "no_action"))
|
||||
set(v) = prefHelper.setPref(SPACE_BAR_SWIPE_LEFT, v)
|
||||
var spaceBarSwipeRight: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SPACE_BAR_SWIPE_RIGHT, "no_action"))
|
||||
set(v) = prefHelper.setPref(SPACE_BAR_SWIPE_RIGHT, v)
|
||||
var deleteKeySwipeLeft: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(DELETE_KEY_SWIPE_LEFT, "no_action"))
|
||||
set(v) = prefHelper.setPref(DELETE_KEY_SWIPE_LEFT, v)
|
||||
var swipeVelocityThreshold: VelocityThreshold
|
||||
get() = VelocityThreshold.fromString(prefHelper.getPref(SWIPE_VELOCITY_THRESHOLD, "normal"))
|
||||
set(v) = prefHelper.setPref(SWIPE_VELOCITY_THRESHOLD, v)
|
||||
var swipeDistanceThreshold: DistanceThreshold
|
||||
get() = DistanceThreshold.fromString(prefHelper.getPref(SWIPE_DISTANCE_THRESHOLD, "normal"))
|
||||
set(v) = prefHelper.setPref(SWIPE_DISTANCE_THRESHOLD, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for glide preferences.
|
||||
*/
|
||||
class Glide(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ENABLED = "glide__enabled"
|
||||
const val SHOW_TRAIL = "glide__show_trail"
|
||||
}
|
||||
|
||||
var enabled: Boolean
|
||||
get() = prefHelper.getPref(ENABLED, false)
|
||||
set(v) = prefHelper.setPref(ENABLED, v)
|
||||
var showTrail: Boolean
|
||||
get() = prefHelper.getPref(SHOW_TRAIL, false)
|
||||
set(v) = prefHelper.setPref(SHOW_TRAIL, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for internal preferences. A preference qualifies as an internal pref if the
|
||||
* user has no ability to control this preference's value directly (via a UI pref view).
|
||||
@@ -226,7 +298,10 @@ class PrefHelper(
|
||||
*/
|
||||
class Keyboard(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val BOTTOM_OFFSET = "keyboard__bottom_offset"
|
||||
const val HEIGHT_FACTOR = "keyboard__height_factor"
|
||||
const val HINTED_NUMBER_ROW = "keyboard__hinted_number_row"
|
||||
const val HINTED_SYMBOLS = "keyboard__hinted_symbols"
|
||||
const val LONG_PRESS_DELAY = "keyboard__long_press_delay"
|
||||
const val ONE_HANDED_MODE = "keyboard__one_handed_mode"
|
||||
const val POPUP_ENABLED = "keyboard__popup_enabled"
|
||||
@@ -236,9 +311,18 @@ class PrefHelper(
|
||||
const val VIBRATION_STRENGTH = "keyboard__vibration_strength"
|
||||
}
|
||||
|
||||
var bottomOffset: Int = 0
|
||||
get() = prefHelper.getPref(BOTTOM_OFFSET, 0)
|
||||
private set
|
||||
var heightFactor: String = ""
|
||||
get() = prefHelper.getPref(HEIGHT_FACTOR, "normal")
|
||||
private set
|
||||
var hintedNumberRow: Boolean
|
||||
get() = prefHelper.getPref(HINTED_NUMBER_ROW, true)
|
||||
set(v) = prefHelper.setPref(HINTED_NUMBER_ROW, v)
|
||||
var hintedSymbols: Boolean
|
||||
get() = prefHelper.getPref(HINTED_SYMBOLS, true)
|
||||
set(v) = prefHelper.setPref(HINTED_SYMBOLS, v)
|
||||
var longPressDelay: Int = 0
|
||||
get() = prefHelper.getPref(LONG_PRESS_DELAY, 300)
|
||||
private set
|
||||
@@ -274,10 +358,10 @@ class PrefHelper(
|
||||
}
|
||||
|
||||
var activeSubtypeId: Int
|
||||
get() = prefHelper.getPref(ACTIVE_SUBTYPE_ID, Subtype.DEFAULT.id)
|
||||
get() = prefHelper.getPref(ACTIVE_SUBTYPE_ID, Subtype.DEFAULT.id)
|
||||
set(v) = prefHelper.setPref(ACTIVE_SUBTYPE_ID, v)
|
||||
var subtypes: String
|
||||
get() = prefHelper.getPref(SUBTYPES, "")
|
||||
get() = prefHelper.getPref(SUBTYPES, "")
|
||||
set(v) = prefHelper.setPref(SUBTYPES, v)
|
||||
}
|
||||
|
||||
|
||||
@@ -217,6 +217,34 @@ class SubtypeManager(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the previous subtype in the subtype list if possible.
|
||||
*
|
||||
* @returns The new active subtype or null if the determination process failed.
|
||||
*/
|
||||
fun switchToPrevSubtype(): Subtype? {
|
||||
val subtypeList = subtypes
|
||||
val activeSubtype = getActiveSubtype() ?: return null
|
||||
var triggerNextSubtype = false
|
||||
var newActiveSubtype: Subtype? = null
|
||||
for (subtype in subtypeList.reversed()) {
|
||||
if (triggerNextSubtype) {
|
||||
triggerNextSubtype = false
|
||||
newActiveSubtype = subtype
|
||||
} else if (subtype == activeSubtype) {
|
||||
triggerNextSubtype = true
|
||||
}
|
||||
}
|
||||
if (triggerNextSubtype) {
|
||||
newActiveSubtype = subtypeList.last()
|
||||
}
|
||||
prefs.localization.activeSubtypeId = when (newActiveSubtype) {
|
||||
null -> Subtype.DEFAULT.id
|
||||
else -> newActiveSubtype.id
|
||||
}
|
||||
return newActiveSubtype
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the next subtype in the subtype list if possible.
|
||||
*
|
||||
@@ -236,10 +264,10 @@ class SubtypeManager(
|
||||
}
|
||||
}
|
||||
if (triggerNextSubtype) {
|
||||
newActiveSubtype = subtypeList[0]
|
||||
newActiveSubtype = subtypeList.first()
|
||||
}
|
||||
prefs.localization.activeSubtypeId = when (newActiveSubtype) {
|
||||
null -> -1
|
||||
null -> Subtype.DEFAULT.id
|
||||
else -> newActiveSubtype.id
|
||||
}
|
||||
return newActiveSubtype
|
||||
|
||||
@@ -61,7 +61,7 @@ class MediaInputView : LinearLayout, FlorisBoard.EventListener {
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val height = florisboard?.inputView?.desiredInputViewHeight ?: 0
|
||||
val height = florisboard?.inputView?.desiredMediaKeyboardViewHeight ?: 0
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,12 @@ package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Handler
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.widget.HorizontalScrollView
|
||||
import android.widget.ScrollView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
@@ -91,7 +89,7 @@ class EmojiKeyView(
|
||||
osHandler = Handler()
|
||||
}
|
||||
osHandler?.postDelayed({
|
||||
(parent.parent as HorizontalScrollView)
|
||||
(parent.parent as ScrollView)
|
||||
.requestDisallowInterceptTouchEvent(true)
|
||||
emojiKeyboardView.isScrollBlocked = true
|
||||
emojiKeyboardView.popupManager.show(this)
|
||||
|
||||
@@ -21,14 +21,13 @@ import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.HorizontalScrollView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ViewFlipper
|
||||
import android.widget.*
|
||||
import com.google.android.flexbox.FlexDirection
|
||||
import com.google.android.flexbox.FlexWrap
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
@@ -55,7 +54,7 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
private var layouts: Deferred<EmojiLayoutDataMap>
|
||||
private val mainScope = MainScope()
|
||||
private val tabLayout: TabLayout
|
||||
private val uiLayouts = EnumMap<EmojiCategory, HorizontalScrollView>(EmojiCategory::class.java)
|
||||
private val uiLayouts = EnumMap<EmojiCategory, ScrollView>(EmojiCategory::class.java)
|
||||
|
||||
var isScrollBlocked: Boolean = false
|
||||
var popupManager = KeyPopupManager<EmojiKeyboardView, EmojiKeyView>(this)
|
||||
@@ -66,12 +65,15 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
layouts = mainScope.async(Dispatchers.IO) {
|
||||
parseRawEmojiSpecsFile(context, "ime/media/emoji/emoji-test.txt")
|
||||
}
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
orientation = VERTICAL
|
||||
|
||||
emojiViewFlipper = ViewFlipper(context)
|
||||
emojiViewFlipper.layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
emojiViewFlipper.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, 0).apply {
|
||||
weight = 1.0f
|
||||
}
|
||||
emojiViewFlipper.measureAllChildren = false
|
||||
addView(emojiViewFlipper)
|
||||
|
||||
@@ -117,10 +119,10 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
*/
|
||||
private suspend fun buildLayout() = withContext(Dispatchers.Default) {
|
||||
for (category in EmojiCategory.values()) {
|
||||
val hsv = buildLayoutForCategory(category)
|
||||
uiLayouts[category] = hsv
|
||||
val scrollView = buildLayoutForCategory(category)
|
||||
uiLayouts[category] = scrollView
|
||||
withContext(Dispatchers.Main) {
|
||||
emojiViewFlipper.addView(hsv)
|
||||
emojiViewFlipper.addView(scrollView)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,18 +132,19 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
* context and will not block the main UI thread.
|
||||
*
|
||||
* @param category The category for which a layout should be built.
|
||||
* @return The layout (top-most view is a [HorizontalScrollView]).
|
||||
* @return The layout (top-most view is a [ScrollView]).
|
||||
*/
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private suspend fun buildLayoutForCategory(
|
||||
category: EmojiCategory
|
||||
): HorizontalScrollView = withContext(Dispatchers.Default) {
|
||||
val hsv = HorizontalScrollView(context)
|
||||
hsv.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
||||
): ScrollView = withContext(Dispatchers.Default) {
|
||||
val scrollView = ScrollView(context)
|
||||
scrollView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||
val flexboxLayout = FlexboxLayout(context)
|
||||
flexboxLayout.layoutParams =
|
||||
LayoutParams(LayoutParams.WRAP_CONTENT, emojiKeyHeight * 3)
|
||||
flexboxLayout.flexDirection = FlexDirection.COLUMN
|
||||
LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
flexboxLayout.flexDirection = FlexDirection.ROW
|
||||
flexboxLayout.justifyContent = JustifyContent.SPACE_BETWEEN
|
||||
flexboxLayout.flexWrap = FlexWrap.WRAP
|
||||
for (emojiKeyData in layouts.await()[category].orEmpty()) {
|
||||
val emojiKeyView =
|
||||
@@ -151,11 +154,30 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
)
|
||||
flexboxLayout.addView(emojiKeyView)
|
||||
}
|
||||
hsv.setOnTouchListener { _, _ ->
|
||||
// Add empty placeholder emojis at the end so the grid view. Below is an illustration how
|
||||
// the UI looks with and without an placeholder (e = emoji):
|
||||
// Without placeholder With placeholder
|
||||
// e e e e e e e e e e e e e e
|
||||
// ............. .............
|
||||
// e e e e e e e e e e e e e e
|
||||
// e e e e e e e e
|
||||
//
|
||||
// Based on this SO's answer idea (by La Nube - Luis R. Díaz Muñiz):
|
||||
// https://stackoverflow.com/a/31478004/6801193
|
||||
//
|
||||
// 24 items are chosen here because that's probably the max items that will be shown per
|
||||
// row, even in landscape mode.
|
||||
for (n in 0 until 24) {
|
||||
val gridPlaceholderView = View(context).apply {
|
||||
layoutParams = LayoutParams(emojiKeyWidth, 0)
|
||||
}
|
||||
flexboxLayout.addView(gridPlaceholderView)
|
||||
}
|
||||
scrollView.setOnTouchListener { _, _ ->
|
||||
return@setOnTouchListener isScrollBlocked
|
||||
}
|
||||
hsv.addView(flexboxLayout)
|
||||
return@withContext hsv
|
||||
scrollView.addView(flexboxLayout)
|
||||
return@withContext scrollView
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ import dev.patrickgold.florisboard.util.*
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class KeyPopupExtendedSingleView(
|
||||
context: Context, var isActive: Boolean = false
|
||||
context: Context, val adjustedIndex: Int, var isActive: Boolean = false
|
||||
) : androidx.appcompat.widget.AppCompatTextView(
|
||||
context, null, 0
|
||||
) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.core.content.ContextCompat.getDrawable
|
||||
import androidx.core.view.get
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import dev.patrickgold.florisboard.R
|
||||
@@ -42,7 +43,6 @@ import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
* @property keyboardView Reference to the keyboard view to which this manager class belongs to.
|
||||
*/
|
||||
class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD) {
|
||||
|
||||
private var anchorLeft: Boolean = false
|
||||
private var anchorRight: Boolean = false
|
||||
private var anchorOffset: Int = 0
|
||||
@@ -101,14 +101,14 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
isInitActive: Boolean = false,
|
||||
isWrapBefore: Boolean = false
|
||||
): KeyPopupExtendedSingleView? {
|
||||
val textView = KeyPopupExtendedSingleView(keyView.context, isInitActive)
|
||||
val textView = KeyPopupExtendedSingleView(keyView.context, k, isInitActive)
|
||||
val lp = FlexboxLayout.LayoutParams(keyPopupWidth, keyView.measuredHeight)
|
||||
lp.isWrapBefore = isWrapBefore
|
||||
textView.layoutParams = lp
|
||||
textView.gravity = Gravity.CENTER
|
||||
val textSize = keyboardView.resources.getDimension(R.dimen.key_popup_textSize)
|
||||
if (keyView is KeyView) {
|
||||
when (keyView.data.popup[k].code) {
|
||||
when (keyView.dataPopupWithHint[k].code) {
|
||||
KeyCode.SETTINGS -> {
|
||||
textView.iconDrawable = getDrawable(
|
||||
keyView.context, R.drawable.ic_settings
|
||||
@@ -129,13 +129,13 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
}
|
||||
else -> {
|
||||
textView.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_PX, when (keyView.data.popup[k].code) {
|
||||
TypedValue.COMPLEX_UNIT_PX, when (keyView.dataPopupWithHint[k].code) {
|
||||
KeyCode.URI_COMPONENT_TLD,
|
||||
KeyCode.SWITCH_TO_TEXT_CONTEXT -> textSize * 0.6f
|
||||
else -> textSize
|
||||
}
|
||||
)
|
||||
textView.text = keyView.getComputedLetter(keyView.data.popup[k])
|
||||
textView.text = keyView.getComputedLetter(keyView.dataPopupWithHint[k])
|
||||
}
|
||||
}
|
||||
} else if (keyView is EmojiKeyView) {
|
||||
@@ -211,7 +211,7 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
if (keyView is KeyView) {
|
||||
popupView.findViewById<TextView>(R.id.key_popup_text)?.text = keyView.getComputedLetter()
|
||||
popupView.findViewById<ImageView>(R.id.key_popup_threedots)?.visibility = when {
|
||||
keyView.data.popup.isEmpty() -> View.INVISIBLE
|
||||
keyView.dataPopupWithHint.isEmpty() -> View.INVISIBLE
|
||||
else -> View.VISIBLE
|
||||
}
|
||||
} else if (keyView is EmojiKeyView) {
|
||||
@@ -256,17 +256,12 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
}
|
||||
|
||||
// Anchor left if keyView is in left half of keyboardView, else anchor right
|
||||
if (keyView is KeyView) {
|
||||
anchorLeft = keyView.x < keyboardView.measuredWidth / 2
|
||||
} else if (keyView is EmojiKeyView) {
|
||||
val hsv = (keyView.parent.parent as HorizontalScrollView)
|
||||
anchorLeft = (keyView.x - hsv.scrollX) < keyboardView.measuredWidth / 2
|
||||
}
|
||||
anchorLeft = keyView.x < keyboardView.measuredWidth / 2
|
||||
anchorRight = !anchorLeft
|
||||
|
||||
// Determine key counts for each row
|
||||
val n = when (keyView) {
|
||||
is KeyView -> keyView.data.popup.size
|
||||
is KeyView -> keyView.dataPopupWithHint.size
|
||||
is EmojiKeyView -> keyView.data.popup.size
|
||||
else -> 0
|
||||
}
|
||||
@@ -315,17 +310,29 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
// Build UI
|
||||
popupViewExt.removeAllViews()
|
||||
val indices = when (keyView) {
|
||||
is KeyView -> keyView.data.popup.indices
|
||||
is KeyView -> keyView.dataPopupWithHint.indices
|
||||
is EmojiKeyView -> keyView.data.popup.indices
|
||||
else -> IntRange(0, 0)
|
||||
}
|
||||
var hasShownFirst = false
|
||||
for (k in indices) {
|
||||
val isInitActive =
|
||||
anchorLeft && (k - row1count == anchorOffset) ||
|
||||
anchorRight && (k - row1count == row0count - 1 - anchorOffset)
|
||||
val kk = when (keyView) {
|
||||
is KeyView -> when {
|
||||
isInitActive -> {
|
||||
hasShownFirst = true
|
||||
0
|
||||
}
|
||||
hasShownFirst -> k
|
||||
else -> k + 1
|
||||
}
|
||||
else -> k
|
||||
}
|
||||
popupViewExt.addView(
|
||||
createTextView(
|
||||
keyView, k, isInitActive, (row1count > 0) && (k - row1count == 0)
|
||||
keyView, kk, isInitActive, (row1count > 0) && (k - row1count == 0)
|
||||
)
|
||||
)
|
||||
if (isInitActive) {
|
||||
@@ -441,7 +448,7 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
}
|
||||
|
||||
if (keyView is KeyView) {
|
||||
for (k in keyView.data.popup.indices) {
|
||||
for (k in keyView.dataPopupWithHint.indices) {
|
||||
val view = popupViewExt.getChildAt(k)
|
||||
if (view != null) {
|
||||
val textView = view as KeyPopupExtendedSingleView
|
||||
@@ -471,7 +478,13 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
*/
|
||||
fun getActiveKeyData(keyView: T_KV): KeyData? {
|
||||
return if (keyView is KeyView) {
|
||||
keyView.data.popup.getOrNull(activeExtIndex ?: -1) ?: keyView.data
|
||||
val activeExtIndex = activeExtIndex
|
||||
if (activeExtIndex != null) {
|
||||
val singleView = popupViewExt[activeExtIndex] as KeyPopupExtendedSingleView
|
||||
keyView.dataPopupWithHint.getOrNull(singleView.adjustedIndex) ?: keyView.data
|
||||
} else {
|
||||
keyView.data
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.InputView
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.text.editing.EditingKeyboardView
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyType
|
||||
@@ -119,17 +120,13 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
override fun onCreate() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onCreate()")
|
||||
|
||||
for (mode in KeyboardMode.values()) {
|
||||
if (mode == KeyboardMode.CHARACTERS) {
|
||||
var subtypes = florisboard.subtypeManager.subtypes
|
||||
if (subtypes.isEmpty()) {
|
||||
subtypes = listOf(Subtype.DEFAULT)
|
||||
}
|
||||
for (subtype in subtypes) {
|
||||
layoutManager.preloadComputedLayout(mode, subtype)
|
||||
}
|
||||
} else {
|
||||
layoutManager.preloadComputedLayout(mode, florisboard.activeSubtype)
|
||||
var subtypes = florisboard.subtypeManager.subtypes
|
||||
if (subtypes.isEmpty()) {
|
||||
subtypes = listOf(Subtype.DEFAULT)
|
||||
}
|
||||
for (subtype in subtypes) {
|
||||
for (mode in KeyboardMode.values()) {
|
||||
layoutManager.preloadComputedLayout(mode, subtype)
|
||||
}
|
||||
}
|
||||
smartbarManager = SmartbarManager.getInstance()
|
||||
@@ -303,7 +300,8 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
val inputText =
|
||||
(ic?.getExtractedText(ExtractedTextRequest(), 0)?.text ?: "").toString()
|
||||
selectionEndMax = inputText.length
|
||||
if (isComposingEnabled) {
|
||||
// TODO: separate composing text from delete swipe word detection
|
||||
//if (isComposingEnabled) {
|
||||
if (!isTextSelected) {
|
||||
val newCursorPos = cursorAnchorInfo.selectionStart
|
||||
val prevComposingText = (cursorAnchorInfo.composingText ?: "").toString()
|
||||
@@ -325,7 +323,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
resetComposingText()
|
||||
}
|
||||
smartbarManager.generateCandidatesFromComposing(composingText)
|
||||
}
|
||||
//}
|
||||
if (!isNewSelectionInBoundsOfOld) {
|
||||
isManualSelectionMode = false
|
||||
isManualSelectionModeLeft = false
|
||||
@@ -420,17 +418,33 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
|
||||
/**
|
||||
* Updates the current caps state according to the [cursorCapsMode], while respecting
|
||||
* [capsLock] property.
|
||||
* [capsLock] property and the correction.autoCapitalization preference.
|
||||
*/
|
||||
private fun updateCapsState() {
|
||||
cursorCapsMode = fetchCurrentCursorCapsMode()
|
||||
editorCapsMode = parseCapsModeFromFlags(florisboard.currentInputEditorInfo.inputType)
|
||||
if (!capsLock) {
|
||||
caps = cursorCapsMode != CapsMode.NONE
|
||||
caps = florisboard.prefs.correction.autoCapitalization && cursorCapsMode != CapsMode.NONE
|
||||
keyboardViews[activeKeyboardMode]?.invalidateAllKeys()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given [SwipeAction]. Ignores any [SwipeAction] but the ones relevant for this
|
||||
* class.
|
||||
*/
|
||||
fun executeSwipeAction(swipeAction: SwipeAction) {
|
||||
when (swipeAction) {
|
||||
SwipeAction.DELETE_WORD -> handleDeleteWord()
|
||||
SwipeAction.MOVE_CURSOR_DOWN -> handleArrow(KeyCode.ARROW_DOWN)
|
||||
SwipeAction.MOVE_CURSOR_UP -> handleArrow(KeyCode.ARROW_UP)
|
||||
SwipeAction.MOVE_CURSOR_LEFT -> handleArrow(KeyCode.ARROW_LEFT)
|
||||
SwipeAction.MOVE_CURSOR_RIGHT -> handleArrow(KeyCode.ARROW_RIGHT)
|
||||
SwipeAction.SHIFT -> handleShift()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given [keyCode] as a [KeyEvent.ACTION_DOWN].
|
||||
*
|
||||
@@ -475,6 +489,25 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
ic?.endBatchEdit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.DELETE_WORD] event.
|
||||
*/
|
||||
private fun handleDeleteWord() {
|
||||
val ic = florisboard.currentInputConnection
|
||||
ic?.beginBatchEdit()
|
||||
isManualSelectionMode = false
|
||||
isManualSelectionModeLeft = false
|
||||
isManualSelectionModeRight = false
|
||||
ic?.setComposingText("", 1)
|
||||
ic?.finishComposingText()
|
||||
if (ic?.getTextBeforeCursor(1, 0)?.length ?: 0 > 0) {
|
||||
ic?.deleteSurroundingText(1, 0)
|
||||
}
|
||||
composingText = null
|
||||
composingTextStart = null
|
||||
ic?.endBatchEdit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.ENTER] event.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.gestures
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Enum for declaring the distance thresholds for swipe gestures.
|
||||
*/
|
||||
enum class DistanceThreshold {
|
||||
VERY_SHORT,
|
||||
SHORT,
|
||||
NORMAL,
|
||||
LONG,
|
||||
VERY_LONG;
|
||||
|
||||
companion object {
|
||||
fun fromString(string: String): DistanceThreshold {
|
||||
return valueOf(string.toUpperCase(Locale.ROOT))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super.toString().toLowerCase(Locale.ROOT)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.gestures
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Enum for declaring the possible actions for swipe gestures.
|
||||
*/
|
||||
enum class SwipeAction {
|
||||
NO_ACTION,
|
||||
DELETE_WORD,
|
||||
HIDE_KEYBOARD,
|
||||
MOVE_CURSOR_UP,
|
||||
MOVE_CURSOR_DOWN,
|
||||
MOVE_CURSOR_LEFT,
|
||||
MOVE_CURSOR_RIGHT,
|
||||
SHIFT,
|
||||
SWITCH_TO_PREV_SUBTYPE,
|
||||
SWITCH_TO_NEXT_SUBTYPE;
|
||||
|
||||
companion object {
|
||||
fun fromString(string: String): SwipeAction {
|
||||
return valueOf(string.toUpperCase(Locale.ROOT))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super.toString().toLowerCase(Locale.ROOT)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.gestures
|
||||
|
||||
import android.content.Context
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.MotionEvent
|
||||
import dev.patrickgold.florisboard.R
|
||||
import java.lang.Exception
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
* Wrapper class which holds all enums, interfaces and classes for detecting a swipe gesture.
|
||||
*/
|
||||
abstract class SwipeGesture {
|
||||
/**
|
||||
* Class which detects swipes based on given [MotionEvent]s. Only supports single-finger swipes
|
||||
* and ignores additional pointers provided, if any.
|
||||
*
|
||||
* @property listener The listener to report detected swipes to.
|
||||
*/
|
||||
class Detector(private val context: Context, private val listener: Listener) {
|
||||
private val eventList: MutableList<MotionEvent> = mutableListOf()
|
||||
private var indexFirst: Int = 0
|
||||
private var indexLastMoveRecognized: Int = 0
|
||||
|
||||
var distanceThreshold: DistanceThreshold = DistanceThreshold.NORMAL
|
||||
var velocityThreshold: VelocityThreshold = VelocityThreshold.NORMAL
|
||||
|
||||
fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
try {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN,
|
||||
MotionEvent.ACTION_POINTER_DOWN -> {
|
||||
clearEventList()
|
||||
eventList.add(MotionEvent.obtainNoHistory(event))
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
eventList.add(MotionEvent.obtainNoHistory(event))
|
||||
val lastEvent = eventList[indexLastMoveRecognized]
|
||||
val diffX = event.x - lastEvent.x
|
||||
val diffY = event.y - lastEvent.y
|
||||
val distanceThresholdNV = numericValue(distanceThreshold) / 2.0f
|
||||
return if (abs(diffX) > distanceThresholdNV || abs(diffY) > distanceThresholdNV) {
|
||||
indexLastMoveRecognized = eventList.size - 1
|
||||
val direction = detectDirection(diffX.toDouble(), diffY.toDouble())
|
||||
listener.onSwipe(direction, Type.TOUCH_MOVE)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_POINTER_UP -> {
|
||||
val firstEvent = eventList[indexFirst]
|
||||
val diffX = event.x - firstEvent.x
|
||||
val diffY = event.y - firstEvent.y
|
||||
val distanceThresholdNV = numericValue(distanceThreshold)
|
||||
/*val velocityThresholdNV = numericValue(velocityThreshold)
|
||||
val velocity =
|
||||
((convertPixelsToDp(
|
||||
sqrt(diffX.pow(2) + diffY.pow(2)),
|
||||
context
|
||||
) / event.downTime) * 10.0f.pow(8)).toInt()*/
|
||||
clearEventList()
|
||||
// return if ((abs(diffX) > distanceThresholdNV || abs(diffY) > distanceThresholdNV) && velocity >= velocityThresholdNV) {
|
||||
return if ((abs(diffX) > distanceThresholdNV || abs(diffY) > distanceThresholdNV)) {
|
||||
val direction = detectDirection(diffX.toDouble(), diffY.toDouble())
|
||||
listener.onSwipe(direction, Type.TOUCH_UP)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
clearEventList()
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
return false
|
||||
} catch(e: Exception) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the angle based on the given x any y lengths. The returned angle is in degree
|
||||
* and goes clockwise, beginning with 0° at +x, 90° at +y, 180° at -y and 270° at -y.
|
||||
*
|
||||
* Coordinate system (based on the Android display coordinate system):
|
||||
* -y
|
||||
* -x 00 +x
|
||||
* +y
|
||||
*/
|
||||
private fun angle(diffX: Double, diffY: Double): Double {
|
||||
val tmpAngle = abs(360 * atan(diffY / diffX) / (2 * PI))
|
||||
return if (diffX < 0 && diffY >= 0) {
|
||||
180.0f - tmpAngle
|
||||
} else if (diffX < 0 && diffY < 0) {
|
||||
180.0f + tmpAngle
|
||||
} else if (diffX >= 0 && diffY < 0) {
|
||||
360.0f - tmpAngle
|
||||
} else {
|
||||
tmpAngle
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the direction of a finger swipe by two given events.
|
||||
*/
|
||||
private fun detectDirection(diffX: Double, diffY: Double): Direction {
|
||||
val diffAngle = angle(diffX, diffY) / 360
|
||||
return when {
|
||||
diffAngle >= (1/16.0f) && diffAngle < (3/16.0f) -> Direction.DOWN_RIGHT
|
||||
diffAngle >= (3/16.0f) && diffAngle < (5/16.0f) -> Direction.DOWN
|
||||
diffAngle >= (5/16.0f) && diffAngle < (7/16.0f) -> Direction.DOWN_LEFT
|
||||
diffAngle >= (7/16.0f) && diffAngle < (9/16.0f) -> Direction.LEFT
|
||||
diffAngle >= (9/16.0f) && diffAngle < (11/16.0f) -> Direction.UP_LEFT
|
||||
diffAngle >= (11/16.0f) && diffAngle < (13/16.0f) -> Direction.UP
|
||||
diffAngle >= (13/16.0f) && diffAngle < (15/16.0f) -> Direction.UP_RIGHT
|
||||
else -> Direction.RIGHT
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up and clears the event list.
|
||||
*/
|
||||
private fun clearEventList() {
|
||||
for (event in eventList) {
|
||||
event.recycle()
|
||||
}
|
||||
eventList.clear()
|
||||
indexFirst = 0
|
||||
indexLastMoveRecognized = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a numeric value for a given [DistanceThreshold], based on the values defined in
|
||||
* the resources dimens.xml file.
|
||||
*/
|
||||
private fun numericValue(of: DistanceThreshold): Double {
|
||||
return when (of) {
|
||||
DistanceThreshold.VERY_SHORT -> context.resources.getDimension(R.dimen.gesture_distance_threshold_very_short)
|
||||
DistanceThreshold.SHORT -> context.resources.getDimension(R.dimen.gesture_distance_threshold_short)
|
||||
DistanceThreshold.NORMAL -> context.resources.getDimension(R.dimen.gesture_distance_threshold_normal)
|
||||
DistanceThreshold.LONG -> context.resources.getDimension(R.dimen.gesture_distance_threshold_long)
|
||||
DistanceThreshold.VERY_LONG -> context.resources.getDimension(R.dimen.gesture_distance_threshold_very_long)
|
||||
}.toDouble()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a numeric value for a given [VelocityThreshold], based on the values defined in
|
||||
* the resources dimens.xml file.
|
||||
*/
|
||||
private fun numericValue(of: VelocityThreshold): Double {
|
||||
return when (of) {
|
||||
VelocityThreshold.VERY_SLOW -> context.resources.getInteger(R.integer.gesture_velocity_threshold_very_slow)
|
||||
VelocityThreshold.SLOW -> context.resources.getInteger(R.integer.gesture_velocity_threshold_slow)
|
||||
VelocityThreshold.NORMAL -> context.resources.getInteger(R.integer.gesture_velocity_threshold_normal)
|
||||
VelocityThreshold.FAST -> context.resources.getInteger(R.integer.gesture_velocity_threshold_fast)
|
||||
VelocityThreshold.VERY_FAST -> context.resources.getInteger(R.integer.gesture_velocity_threshold_very_fast)
|
||||
}.toDouble()
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onSwipe(direction: Direction, type: Type): Boolean
|
||||
}
|
||||
|
||||
enum class Direction {
|
||||
UP_LEFT,
|
||||
UP,
|
||||
UP_RIGHT,
|
||||
RIGHT,
|
||||
DOWN_RIGHT,
|
||||
DOWN,
|
||||
DOWN_LEFT,
|
||||
LEFT,
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
TOUCH_UP,
|
||||
TOUCH_MOVE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.gestures
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Enum for declaring the velocity thresholds for swipe gestures.
|
||||
*/
|
||||
enum class VelocityThreshold {
|
||||
VERY_SLOW,
|
||||
SLOW,
|
||||
NORMAL,
|
||||
FAST,
|
||||
VERY_FAST;
|
||||
|
||||
companion object {
|
||||
fun fromString(string: String): VelocityThreshold {
|
||||
return valueOf(string.toUpperCase(Locale.ROOT))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super.toString().toLowerCase(Locale.ROOT)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.key
|
||||
|
||||
object KeyCode {
|
||||
|
||||
@@ -1,8 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.key
|
||||
|
||||
/**
|
||||
* Data class which describes a single key and its variants.
|
||||
*
|
||||
* @property code The UTF-8 encoded code of the character. The code defined here is used as the
|
||||
* data passed to the system.
|
||||
* @property label The string used to display the key in the UI. Is not used for the actual data
|
||||
* passed to the system. Should normally be the exact same as the [code]. Defaults to an empty
|
||||
* string.
|
||||
* @property hintedNumber The hinted number which will be dynamically inserted into the long-press
|
||||
* [popup]. Leave null to disable the hinted popup for this key. The visibility of the hinted number
|
||||
* is controlled by the preferences. Defaults to null.
|
||||
* @property hintedSymbol The hinted symbol which will be dynamically inserted into the long-press
|
||||
* [popup]. Leave null to disable the hinted popup for this key. The visibility of the hinted symbol
|
||||
* is controlled by the preferences. Defaults to null.
|
||||
* @property popup List of keys which will be accessible while long pressing the key. Defaults to
|
||||
* an empty list (no extended popup).
|
||||
* @property type The type of the key. Some actions require both [code] and [type] to match in order
|
||||
* to be successfully executed. Defaults to [KeyType.CHARACTER].
|
||||
* @property variation Controls if the key should only be shown in some contexts (e.g.: url input)
|
||||
* or if the key should always be visible. Defaults to [KeyVariation.ALL].
|
||||
*/
|
||||
data class KeyData(
|
||||
var code: Int,
|
||||
var label: String = "",
|
||||
var hintedNumber: KeyData? = null,
|
||||
var hintedSymbol: KeyData? = null,
|
||||
var popup: MutableList<KeyData> = mutableListOf(),
|
||||
var type: KeyType = KeyType.CHARACTER,
|
||||
var variation: KeyVariation = KeyVariation.ALL
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.key
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.key
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.google.android.flexbox.FlexboxLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeGesture
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor2
|
||||
@@ -51,8 +52,8 @@ import java.util.*
|
||||
class KeyView(
|
||||
private val keyboardView: KeyboardView,
|
||||
val data: KeyData
|
||||
) : View(keyboardView.context) {
|
||||
|
||||
) : View(keyboardView.context), SwipeGesture.Listener {
|
||||
val dataPopupWithHint: MutableList<KeyData>
|
||||
private var isKeyPressed: Boolean = false
|
||||
set(value) {
|
||||
field = value
|
||||
@@ -76,8 +77,19 @@ class KeyView(
|
||||
textSize = resources.getDimension(R.dimen.key_textSize)
|
||||
typeface = Typeface.DEFAULT
|
||||
}
|
||||
private var hintedLabel: String? = null
|
||||
private var hintedLabelPaint: Paint = Paint().apply {
|
||||
alpha = 120
|
||||
color = 0
|
||||
isAntiAlias = true
|
||||
isFakeBoldText = true
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = resources.getDimension(R.dimen.key_textHintSize)
|
||||
typeface = Typeface.DEFAULT
|
||||
}
|
||||
|
||||
var florisboard: FlorisBoard? = null
|
||||
private val swipeGestureDetector = SwipeGesture.Detector(context, this)
|
||||
var touchHitBox: Rect = Rect(-1, -1, -1, -1)
|
||||
|
||||
init {
|
||||
@@ -124,6 +136,23 @@ class KeyView(
|
||||
background = getDrawable(context, R.drawable.shape_rect_rounded)
|
||||
elevation = 4.0f
|
||||
|
||||
var hintKeyData: KeyData? = null
|
||||
val hintedNumber = data.hintedNumber
|
||||
if (prefs.keyboard.hintedNumberRow && hintedNumber != null) {
|
||||
hintKeyData = hintedNumber
|
||||
}
|
||||
val hintedSymbol = data.hintedSymbol
|
||||
if (prefs.keyboard.hintedSymbols && hintedSymbol != null) {
|
||||
hintKeyData = hintedSymbol
|
||||
}
|
||||
dataPopupWithHint = if (hintKeyData == null) {
|
||||
data.popup.toMutableList()
|
||||
} else {
|
||||
val popupList = data.popup.toMutableList()
|
||||
popupList.add(hintKeyData)
|
||||
popupList
|
||||
}
|
||||
|
||||
updateKeyPressedBackground()
|
||||
}
|
||||
|
||||
@@ -173,8 +202,17 @@ class KeyView(
|
||||
*/
|
||||
fun onFlorisTouchEvent(event: MotionEvent?): Boolean {
|
||||
event ?: return false
|
||||
if (swipeGestureDetector.onTouchEvent(event)) {
|
||||
isKeyPressed = false
|
||||
osHandler?.removeCallbacksAndMessages(null)
|
||||
osTimer?.cancel()
|
||||
osTimer = null
|
||||
keyboardView.popupManager.hide()
|
||||
return true
|
||||
}
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
shouldBlockNextKeyCode = false
|
||||
florisboard?.prefs?.keyboard?.let {
|
||||
if (it.popupEnabled){
|
||||
keyboardView.popupManager.show(this)
|
||||
@@ -200,7 +238,7 @@ class KeyView(
|
||||
osHandler = Handler()
|
||||
}
|
||||
osHandler?.postDelayed({
|
||||
if (data.popup.isNotEmpty()) {
|
||||
if (dataPopupWithHint.isNotEmpty()) {
|
||||
keyboardView.popupManager.extend(this)
|
||||
}
|
||||
if (data.code == KeyCode.SPACE) {
|
||||
@@ -253,6 +291,34 @@ class KeyView(
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe event handler. Listens to touch_move left/right swipes and triggers the swipe action
|
||||
* defined in the prefs.
|
||||
*/
|
||||
override fun onSwipe(direction: SwipeGesture.Direction, type: SwipeGesture.Type): Boolean {
|
||||
return when (data.code) {
|
||||
KeyCode.SPACE -> when (type) {
|
||||
SwipeGesture.Type.TOUCH_MOVE -> when (direction) {
|
||||
SwipeGesture.Direction.LEFT -> {
|
||||
florisboard?.executeSwipeAction(prefs.gestures.spaceBarSwipeLeft)
|
||||
osHandler?.removeCallbacksAndMessages(null)
|
||||
shouldBlockNextKeyCode = true
|
||||
true
|
||||
}
|
||||
SwipeGesture.Direction.RIGHT -> {
|
||||
florisboard?.executeSwipeAction(prefs.gestures.spaceBarSwipeRight)
|
||||
osHandler?.removeCallbacksAndMessages(null)
|
||||
shouldBlockNextKeyCode = true
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Solution base from this great StackOverflow answer which explained and helped a lot
|
||||
* for handling onMeasure():
|
||||
@@ -445,6 +511,15 @@ class KeyView(
|
||||
&& data.code != KeyCode.HALF_SPACE && data.code != KeyCode.KESHIDA || data.type == KeyType.NUMERIC
|
||||
) {
|
||||
label = getComputedLetter()
|
||||
val hintedNumber = data.hintedNumber
|
||||
if (prefs.keyboard.hintedNumberRow && hintedNumber != null) {
|
||||
hintedLabel = getComputedLetter(hintedNumber)
|
||||
}
|
||||
val hintedSymbol = data.hintedSymbol
|
||||
if (prefs.keyboard.hintedSymbols && hintedSymbol != null) {
|
||||
hintedLabel = getComputedLetter(hintedSymbol)
|
||||
}
|
||||
|
||||
} else {
|
||||
when (data.code) {
|
||||
KeyCode.DELETE -> {
|
||||
@@ -538,6 +613,9 @@ class KeyView(
|
||||
}
|
||||
}
|
||||
|
||||
val isPortrait =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
||||
// Draw drawable
|
||||
val drawable = drawable
|
||||
if (drawable != null) {
|
||||
@@ -569,14 +647,12 @@ class KeyView(
|
||||
} else {
|
||||
labelPaint.textSize = resources.getDimension(R.dimen.key_textSize)
|
||||
}
|
||||
labelPaint.color = prefs.theme.keyFgColor
|
||||
labelPaint.alpha = if (keyboardView.computedLayout?.mode == KeyboardMode.CHARACTERS &&
|
||||
data.code == KeyCode.SPACE) { 120 } else { 255 }
|
||||
val isPortrait =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
if (prefs.keyboard.oneHandedMode != "off" && isPortrait) {
|
||||
labelPaint.textSize *= 0.9f
|
||||
}
|
||||
labelPaint.color = prefs.theme.keyFgColor
|
||||
labelPaint.alpha = if (keyboardView.computedLayout?.mode == KeyboardMode.CHARACTERS &&
|
||||
data.code == KeyCode.SPACE) { 120 } else { 255 }
|
||||
val centerX = measuredWidth / 2.0f
|
||||
val centerY = measuredHeight / 2.0f + (labelPaint.textSize - labelPaint.descent()) / 2
|
||||
if (label.contains("\n")) {
|
||||
@@ -588,6 +664,20 @@ class KeyView(
|
||||
canvas.drawText(label, centerX, centerY, labelPaint)
|
||||
}
|
||||
}
|
||||
|
||||
// Draw hinted label
|
||||
val hintedLabel = hintedLabel
|
||||
if (hintedLabel != null) {
|
||||
hintedLabelPaint.textSize = resources.getDimension(R.dimen.key_textHintSize)
|
||||
if (prefs.keyboard.oneHandedMode != "off" && isPortrait) {
|
||||
hintedLabelPaint.textSize *= 0.9f
|
||||
}
|
||||
hintedLabelPaint.color = prefs.theme.keyFgColor
|
||||
hintedLabelPaint.alpha = 120
|
||||
val centerX = measuredWidth * 5.0f / 6.0f
|
||||
val centerY = measuredHeight * 1.0f / 6.0f + (hintedLabelPaint.textSize - hintedLabelPaint.descent()) / 2
|
||||
canvas.drawText(hintedLabel, centerX, centerY, hintedLabelPaint)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,8 +29,11 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.popup.KeyPopupManager
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyView
|
||||
import dev.patrickgold.florisboard.ime.text.layout.ComputedLayoutData
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeGesture
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
@@ -39,11 +42,9 @@ import kotlin.math.roundToInt
|
||||
* background of this keyboard is the background of the underlying mainViewFlipper. This prevents
|
||||
* rendering issues when a keyboard is being loaded for the first time.
|
||||
*
|
||||
* TODO: Implement swipe gesture support
|
||||
*
|
||||
* @property florisboard Reference to instance of core class [FlorisBoard].
|
||||
*/
|
||||
class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
class KeyboardView : LinearLayout, FlorisBoard.EventListener, SwipeGesture.Listener {
|
||||
private var activeKeyView: KeyView? = null
|
||||
private var activePointerId: Int? = null
|
||||
private var activeX: Float = 0.0f
|
||||
@@ -57,9 +58,11 @@ class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
var desiredKeyWidth: Int = resources.getDimension(R.dimen.key_width).toInt()
|
||||
var desiredKeyHeight: Int = resources.getDimension(R.dimen.key_height).toInt()
|
||||
var florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private var initialKeyCode: Int = 0
|
||||
var isPreviewMode: Boolean = false
|
||||
var popupManager = KeyPopupManager<KeyboardView, KeyView>(this)
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private val swipeGestureDetector = SwipeGesture.Detector(context, this)
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
@@ -70,6 +73,7 @@ class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
florisboard?.addEventListener(this)
|
||||
onWindowShown()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,6 +108,13 @@ class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
popupManager.dismissAllPopups()
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
swipeGestureDetector.apply {
|
||||
distanceThreshold = prefs.gestures.swipeDistanceThreshold
|
||||
velocityThreshold = prefs.gestures.swipeVelocityThreshold
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch all events which are designated for child views.
|
||||
*/
|
||||
@@ -121,6 +132,12 @@ class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
return false
|
||||
}
|
||||
val eventFloris = MotionEvent.obtainNoHistory(event)
|
||||
if (swipeGestureDetector.onTouchEvent(event)) {
|
||||
sendFlorisTouchEvent(eventFloris, MotionEvent.ACTION_CANCEL)
|
||||
activeKeyView = null
|
||||
activePointerId = null
|
||||
return true
|
||||
}
|
||||
val pointerIndex = event.actionIndex
|
||||
var pointerId = event.getPointerId(pointerIndex)
|
||||
when (event.actionMasked) {
|
||||
@@ -131,6 +148,7 @@ class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
activeX = event.getX(pointerIndex)
|
||||
activeY = event.getY(pointerIndex)
|
||||
searchForActiveKeyView()
|
||||
initialKeyCode = activeKeyView?.data?.code ?: 0
|
||||
sendFlorisTouchEvent(eventFloris, MotionEvent.ACTION_DOWN)
|
||||
} else if (activePointerId != pointerId) {
|
||||
// New pointer arrived. Send ACTION_UP to current active view and move on
|
||||
@@ -139,6 +157,7 @@ class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
activeX = event.getX(pointerIndex)
|
||||
activeY = event.getY(pointerIndex)
|
||||
searchForActiveKeyView()
|
||||
initialKeyCode = activeKeyView?.data?.code ?: 0
|
||||
sendFlorisTouchEvent(eventFloris, MotionEvent.ACTION_DOWN)
|
||||
}
|
||||
}
|
||||
@@ -196,6 +215,45 @@ class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe event handler. Listens to touch_up swipes and executes the swipe action defined for it
|
||||
* in the prefs.
|
||||
*/
|
||||
override fun onSwipe(direction: SwipeGesture.Direction, type: SwipeGesture.Type): Boolean {
|
||||
return when {
|
||||
initialKeyCode == KeyCode.DELETE -> {
|
||||
if (type == SwipeGesture.Type.TOUCH_UP && direction == SwipeGesture.Direction.LEFT) {
|
||||
florisboard?.executeSwipeAction(prefs.gestures.deleteKeySwipeLeft)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
initialKeyCode > KeyCode.SPACE && !popupManager.isShowingExtendedPopup -> when {
|
||||
!prefs.glide.enabled -> when (type) {
|
||||
SwipeGesture.Type.TOUCH_UP -> {
|
||||
val swipeAction = when (direction) {
|
||||
SwipeGesture.Direction.UP -> prefs.gestures.swipeUp
|
||||
SwipeGesture.Direction.DOWN -> prefs.gestures.swipeDown
|
||||
SwipeGesture.Direction.LEFT -> prefs.gestures.swipeLeft
|
||||
SwipeGesture.Direction.RIGHT -> prefs.gestures.swipeRight
|
||||
else -> SwipeAction.NO_ACTION
|
||||
}
|
||||
if (swipeAction != SwipeAction.NO_ACTION) {
|
||||
florisboard?.executeSwipeAction(swipeAction)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for an active key view at [activeX]/[activeY].
|
||||
*/
|
||||
|
||||
@@ -22,10 +22,7 @@ import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyTypeAdapter
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyVariationAdapter
|
||||
import dev.patrickgold.florisboard.ime.text.key.*
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
@@ -37,7 +34,7 @@ private typealias KMS = Pair<KeyboardMode, Subtype>
|
||||
* Class which manages layout loading and caching.
|
||||
*/
|
||||
class LayoutManager(private val context: Context) : CoroutineScope by MainScope() {
|
||||
private val layoutCache: HashMap<KMS, Deferred<ComputedLayoutData>> = hashMapOf()
|
||||
private val computedLayoutCache: HashMap<KMS, Deferred<ComputedLayoutData>> = hashMapOf()
|
||||
|
||||
/**
|
||||
* Loads the layout for the specified type and name.
|
||||
@@ -49,6 +46,7 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
if (type == null || name == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val rawJsonData: String = try {
|
||||
context.assets.open("ime/text/$type/$name.json").bufferedReader().use { it.readText() }
|
||||
} catch (e: Exception) {
|
||||
@@ -110,7 +108,7 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
* @param extension The extension layout type and name.
|
||||
* @returns a [ComputedLayoutData] object, regardless of the specified LTNs or errors.
|
||||
*/
|
||||
private fun mergeLayouts(
|
||||
private suspend fun mergeLayoutsAsync(
|
||||
keyboardMode: KeyboardMode,
|
||||
subtype: Subtype,
|
||||
main: LTN? = null,
|
||||
@@ -194,6 +192,28 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
}
|
||||
}
|
||||
|
||||
// Add hints to keys
|
||||
if (keyboardMode == KeyboardMode.CHARACTERS) {
|
||||
val symbolsComputedArrangement = fetchComputedLayoutAsync(KeyboardMode.SYMBOLS, subtype).await().arrangement
|
||||
for ((r, row) in computedArrangement.withIndex()) {
|
||||
if (r >= 3) {
|
||||
break
|
||||
}
|
||||
if (symbolsComputedArrangement.getOrNull(r) != null) {
|
||||
for ((k, key) in row.withIndex()) {
|
||||
if (key.type == KeyType.CHARACTER) {
|
||||
val symbol = symbolsComputedArrangement[r].getOrNull(k)
|
||||
if (r == 0) {
|
||||
key.hintedNumber = symbol
|
||||
} else {
|
||||
key.hintedSymbol = symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ComputedLayoutData(
|
||||
keyboardMode,
|
||||
"computed",
|
||||
@@ -210,7 +230,7 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
* @param keyboardMode The keyboard mode for which the layout should be computed.
|
||||
* @param subtype The subtype which localizes the computed layout.
|
||||
*/
|
||||
private fun computeLayoutFor(
|
||||
private suspend fun computeLayoutFor(
|
||||
keyboardMode: KeyboardMode,
|
||||
subtype: Subtype
|
||||
): ComputedLayoutData {
|
||||
@@ -223,6 +243,9 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
main = LTN(LayoutType.CHARACTERS, subtype.layout)
|
||||
modifier = LTN(LayoutType.CHARACTERS_MOD, "default")
|
||||
}
|
||||
KeyboardMode.EDITING -> {
|
||||
// Layout for this mode is defined in custom layout xml file.
|
||||
}
|
||||
KeyboardMode.NUMERIC -> {
|
||||
main = LTN(LayoutType.NUMERIC, "default")
|
||||
}
|
||||
@@ -246,7 +269,7 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
}
|
||||
}
|
||||
|
||||
return mergeLayouts(keyboardMode, subtype, main, modifier, extension)
|
||||
return mergeLayoutsAsync(keyboardMode, subtype, main, modifier, extension)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,14 +286,14 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
subtype: Subtype
|
||||
): Deferred<ComputedLayoutData> {
|
||||
val kms = KMS(keyboardMode, subtype)
|
||||
val cachedComputedLayout = layoutCache[kms]
|
||||
val cachedComputedLayout = computedLayoutCache[kms]
|
||||
return if (cachedComputedLayout != null) {
|
||||
cachedComputedLayout
|
||||
} else {
|
||||
val computedLayout = async(Dispatchers.IO) {
|
||||
computeLayoutFor(keyboardMode, subtype)
|
||||
}
|
||||
layoutCache[kms] = computedLayout
|
||||
computedLayoutCache[kms] = computedLayout
|
||||
computedLayout
|
||||
}
|
||||
}
|
||||
@@ -289,8 +312,8 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
subtype: Subtype
|
||||
) {
|
||||
val kms = KMS(keyboardMode, subtype)
|
||||
if (layoutCache[kms] == null) {
|
||||
layoutCache[kms] = async(Dispatchers.IO) {
|
||||
if (computedLayoutCache[kms] == null) {
|
||||
computedLayoutCache[kms] = async(Dispatchers.IO) {
|
||||
computeLayoutFor(keyboardMode, subtype)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ class SmartbarManager private constructor() : FlorisBoard.EventListener {
|
||||
smartbarView?.findViewById<View>(R.id.cc_copy)?.isEnabled = isSelectionActive
|
||||
smartbarView?.findViewById<View>(R.id.cc_paste)?.isEnabled =
|
||||
florisboard.clipboardManager?.hasPrimaryClip() ?: false
|
||||
smartbarView?.invalidate()
|
||||
}
|
||||
|
||||
fun deleteCandidateFromDictionary(candidate: String) {
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package dev.patrickgold.florisboard.util
|
||||
|
||||
import android.content.Context
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
|
||||
|
||||
/**
|
||||
* This file has been taken from the Android LatinIME project. Following modifications were done to
|
||||
* the original source code:
|
||||
@@ -71,4 +74,30 @@ object ViewLayoutUtils {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts dp unit to equivalent pixels, depending on device density.
|
||||
*
|
||||
* Source: https://stackoverflow.com/a/9563438/6801193 (by Muhammad Nabeel Arif)
|
||||
*
|
||||
* @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
|
||||
* @param context Context to get resources and device specific display metrics
|
||||
* @return A float value to represent px equivalent to dp depending on device density
|
||||
*/
|
||||
fun convertDpToPixel(dp: Float, context: Context): Float {
|
||||
return dp * (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts device specific pixels to density independent pixels.
|
||||
*
|
||||
* Source: https://stackoverflow.com/a/9563438/6801193 (by Muhammad Nabeel Arif)
|
||||
*
|
||||
* @param px A value in px (pixels) unit. Which we need to convert into db
|
||||
* @param context Context to get resources and device specific display metrics
|
||||
* @return A float value to represent dp equivalent to px value
|
||||
*/
|
||||
fun convertPixelsToDp(px: Float, context: Context): Float {
|
||||
return px / (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,16 @@
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
style="@style/SettingsCardView">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Note: Preferences tagged with [NYI] are not yet implemented and thus won\'t do anything or do some basic placeholder stuff only. Please do not file a bug report for these."/>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/repo_url_card"
|
||||
style="@style/SettingsCardView.Clickable">
|
||||
|
||||
@@ -49,4 +49,59 @@
|
||||
<item>number_row</item>
|
||||
<item>clipboard_cursor_tools</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__gestures__swipe_action__entries">
|
||||
<item>@string/pref__gestures__swipe_action__no_action</item>
|
||||
<item>@string/pref__gestures__swipe_action__delete_word</item>
|
||||
<item>@string/pref__gestures__swipe_action__hide_keyboard</item>
|
||||
<item>@string/pref__gestures__swipe_action__move_cursor_up</item>
|
||||
<item>@string/pref__gestures__swipe_action__move_cursor_down</item>
|
||||
<item>@string/pref__gestures__swipe_action__move_cursor_left</item>
|
||||
<item>@string/pref__gestures__swipe_action__move_cursor_right</item>
|
||||
<item>@string/pref__gestures__swipe_action__shift</item>
|
||||
<item>@string/pref__gestures__swipe_action__switch_to_prev_subtype</item>
|
||||
<item>@string/pref__gestures__swipe_action__switch_to_next_subtype</item>
|
||||
</string-array>
|
||||
<string-array name="pref__gestures__swipe_action__values">
|
||||
<item>no_action</item>
|
||||
<item>delete_word</item>
|
||||
<item>hide_keyboard</item>
|
||||
<item>move_cursor_up</item>
|
||||
<item>move_cursor_down</item>
|
||||
<item>move_cursor_left</item>
|
||||
<item>move_cursor_right</item>
|
||||
<item>shift</item>
|
||||
<item>switch_to_prev_subtype</item>
|
||||
<item>switch_to_next_subtype</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__gestures__swipe_velocity_threshold__entries">
|
||||
<item>@string/pref__gestures__swipe_velocity_threshold__very_slow</item>
|
||||
<item>@string/pref__gestures__swipe_velocity_threshold__slow</item>
|
||||
<item>@string/pref__gestures__swipe_velocity_threshold__normal</item>
|
||||
<item>@string/pref__gestures__swipe_velocity_threshold__fast</item>
|
||||
<item>@string/pref__gestures__swipe_velocity_threshold__very_fast</item>
|
||||
</string-array>
|
||||
<string-array name="pref__gestures__swipe_velocity_threshold__values">
|
||||
<item>very_slow</item>
|
||||
<item>slow</item>
|
||||
<item>normal</item>
|
||||
<item>fast</item>
|
||||
<item>very_fast</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__gestures__swipe_distance_threshold__entries">
|
||||
<item>@string/pref__gestures__swipe_distance_threshold__very_short</item>
|
||||
<item>@string/pref__gestures__swipe_distance_threshold__short</item>
|
||||
<item>@string/pref__gestures__swipe_distance_threshold__normal</item>
|
||||
<item>@string/pref__gestures__swipe_distance_threshold__long</item>
|
||||
<item>@string/pref__gestures__swipe_distance_threshold__very_long</item>
|
||||
</string-array>
|
||||
<string-array name="pref__gestures__swipe_distance_threshold__values">
|
||||
<item>very_short</item>
|
||||
<item>short</item>
|
||||
<item>normal</item>
|
||||
<item>long</item>
|
||||
<item>very_long</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<dimen name="key_borderRadius">6dp</dimen>
|
||||
|
||||
<dimen name="key_textSize">18sp</dimen>
|
||||
<dimen name="key_textHintSize">10sp</dimen>
|
||||
<dimen name="key_numeric_textSize">12sp</dimen>
|
||||
<dimen name="key_popup_textSize">21sp</dimen>
|
||||
<dimen name="emoji_key_textSize">22sp</dimen>
|
||||
@@ -34,4 +35,16 @@
|
||||
<dimen name="smartbar_height">40dp</dimen>
|
||||
<dimen name="smartbar_button_margin">4dp</dimen>
|
||||
<dimen name="smartbar_button_padding">6dp</dimen>
|
||||
|
||||
<dimen name="gesture_distance_threshold_very_short">24dp</dimen>
|
||||
<dimen name="gesture_distance_threshold_short">28dp</dimen>
|
||||
<dimen name="gesture_distance_threshold_normal">32dp</dimen>
|
||||
<dimen name="gesture_distance_threshold_long">36dp</dimen>
|
||||
<dimen name="gesture_distance_threshold_very_long">40dp</dimen>
|
||||
|
||||
<integer name="gesture_velocity_threshold_very_slow">50</integer>
|
||||
<integer name="gesture_velocity_threshold_slow">60</integer>
|
||||
<integer name="gesture_velocity_threshold_normal">70</integer>
|
||||
<integer name="gesture_velocity_threshold_fast">80</integer>
|
||||
<integer name="gesture_velocity_threshold_very_fast">90</integer>
|
||||
</resources>
|
||||
|
||||
@@ -101,6 +101,11 @@
|
||||
<string name="pref__theme__navBarIsLight_summary">Set to ON for dark or to OFF for light foreground.</string>
|
||||
|
||||
<string name="settings__keyboard__title">Keyboard Preferences</string>
|
||||
<string name="pref__keyboard__group_keys__label">Keys</string>
|
||||
<string name="pref__keyboard__hinted_number_row__label">Number row</string>
|
||||
<string name="pref__keyboard__hinted_number_row__summary">First row of character layout hints number row</string>
|
||||
<string name="pref__keyboard__hinted_symbols__label">Symbols</string>
|
||||
<string name="pref__keyboard__hinted_symbols__summary">Second and third row of character layout hint symbols</string>
|
||||
<string name="pref__keyboard__group_layout__label">Layout</string>
|
||||
<string name="pref__keyboard__one_handed_mode__label">One-handed mode</string>
|
||||
<string name="pref__keyboard__one_handed_mode__off">Off</string>
|
||||
@@ -114,6 +119,7 @@
|
||||
<string name="pref__keyboard__height_factor__mid_tall">Mid-tall</string>
|
||||
<string name="pref__keyboard__height_factor__tall">Tall</string>
|
||||
<string name="pref__keyboard__height_factor__extra_tall">Extra-tall</string>
|
||||
<string name="pref__keyboard__bottom_offset__label">Bottom offset (for curved screens)</string>
|
||||
<string name="pref__keyboard__group_keypress__label">Key press</string>
|
||||
<string name="pref__keyboard__sound_enabled__label">Sound on key press</string>
|
||||
<string name="pref__keyboard__sound_volume__label">Sound volume on key press</string>
|
||||
@@ -133,10 +139,47 @@
|
||||
<string name="pref__suggestion__use_pref_words__label">[NYI] Next-word suggestions</string>
|
||||
<string name="pref__suggestion__use_pref_words__summary">Use previous words for generating suggestions</string>
|
||||
<string name="pref__correction__title">Corrections</string>
|
||||
<string name="pref__correction__auto_capitalization__label">Auto-capitalization</string>
|
||||
<string name="pref__correction__auto_capitalization__summary">Capitalize words based on the current input context</string>
|
||||
<string name="pref__correction__double_space_period__label">Double-space period</string>
|
||||
<string name="pref__correction__double_space_period__summary">Tapping twice on spacebar inserts a period followed by a space</string>
|
||||
|
||||
<string name="settings__gestures__title">Gestures & Glide typing</string>
|
||||
<string name="pref__glide__title">Glide typing</string>
|
||||
<string name="pref__glide__enabled__label">[NYI] Enable glide typing</string>
|
||||
<string name="pref__glide__enabled__summary">Type in a word by sliding your finger through its letters</string>
|
||||
<string name="pref__glide__show_trail__label">[NYI] Show glide trail</string>
|
||||
<string name="pref__glide__show_trail__summary">Will disappear after each word</string>
|
||||
<string name="pref__gestures__title">Gestures</string>
|
||||
<string name="pref__gestures__swipe_action__no_action">No action</string>
|
||||
<string name="pref__gestures__swipe_action__delete_word">Delete word</string>
|
||||
<string name="pref__gestures__swipe_action__hide_keyboard">Hide keyboard</string>
|
||||
<string name="pref__gestures__swipe_action__move_cursor_up">Move cursor up</string>
|
||||
<string name="pref__gestures__swipe_action__move_cursor_down">Move cursor down</string>
|
||||
<string name="pref__gestures__swipe_action__move_cursor_left">Move cursor left</string>
|
||||
<string name="pref__gestures__swipe_action__move_cursor_right">Move cursor right</string>
|
||||
<string name="pref__gestures__swipe_action__shift">Shift</string>
|
||||
<string name="pref__gestures__swipe_action__switch_to_prev_subtype">Switch to previous subtype</string>
|
||||
<string name="pref__gestures__swipe_action__switch_to_next_subtype">Switch to next subtype</string>
|
||||
<string name="pref__gestures__swipe_up__label">Swipe up</string>
|
||||
<string name="pref__gestures__swipe_down__label">Swipe down</string>
|
||||
<string name="pref__gestures__swipe_left__label">Swipe left</string>
|
||||
<string name="pref__gestures__swipe_right__label">Swipe right</string>
|
||||
<string name="pref__gestures__space_bar_swipe_left__label">Space bar swipe left</string>
|
||||
<string name="pref__gestures__space_bar_swipe_right__label">Space bar swipe right</string>
|
||||
<string name="pref__gestures__delete_key_swipe_left__label">Delete key swipe left</string>
|
||||
<string name="pref__gestures__swipe_velocity_threshold__label">Swipe velocity threshold</string>
|
||||
<string name="pref__gestures__swipe_velocity_threshold__very_slow">Very slow</string>
|
||||
<string name="pref__gestures__swipe_velocity_threshold__slow">Slow</string>
|
||||
<string name="pref__gestures__swipe_velocity_threshold__normal">Normal</string>
|
||||
<string name="pref__gestures__swipe_velocity_threshold__fast">Fast</string>
|
||||
<string name="pref__gestures__swipe_velocity_threshold__very_fast">Very fast</string>
|
||||
<string name="pref__gestures__swipe_distance_threshold__label">Swipe distance threshold</string>
|
||||
<string name="pref__gestures__swipe_distance_threshold__very_short">Very short</string>
|
||||
<string name="pref__gestures__swipe_distance_threshold__short">Short</string>
|
||||
<string name="pref__gestures__swipe_distance_threshold__normal">Normal</string>
|
||||
<string name="pref__gestures__swipe_distance_threshold__long">Long</string>
|
||||
<string name="pref__gestures__swipe_distance_threshold__very_long">Very long</string>
|
||||
|
||||
<string name="settings__advanced__title">Advanced</string>
|
||||
<string name="pref__advanced__settings_theme__label">Settings theme</string>
|
||||
|
||||
@@ -2,8 +2,118 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
app:title="Not yet implemented..."
|
||||
app:iconSpaceReserved="false"/>
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="glide"
|
||||
app:title="@string/pref__glide__title">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="false"
|
||||
app:disableDependentsState="true"
|
||||
app:key="glide__enabled"
|
||||
app:title="@string/pref__glide__enabled__label"
|
||||
android:summary="@string/pref__glide__enabled__summary"/>
|
||||
|
||||
<!--<SwitchPreferenceCompat
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="true"
|
||||
app:key="glide__show_trail"
|
||||
app:title="@string/pref__glide__show_trail__label"
|
||||
android:summary="@string/pref__glide__show_trail__summary"/>-->
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="gestures"
|
||||
app:title="@string/pref__gestures__title">
|
||||
|
||||
<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="shift"
|
||||
app:entries="@array/pref__gestures__swipe_action__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_action__values"
|
||||
app:dependency="glide__enabled"
|
||||
app:key="gestures__swipe_up"
|
||||
app:title="@string/pref__gestures__swipe_up__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="hide_keyboard"
|
||||
app:entries="@array/pref__gestures__swipe_action__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_action__values"
|
||||
app:dependency="glide__enabled"
|
||||
app:key="gestures__swipe_down"
|
||||
app:title="@string/pref__gestures__swipe_down__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="switch_to_next_subtype"
|
||||
app:entries="@array/pref__gestures__swipe_action__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_action__values"
|
||||
app:dependency="glide__enabled"
|
||||
app:key="gestures__swipe_left"
|
||||
app:title="@string/pref__gestures__swipe_left__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="switch_to_prev_subtype"
|
||||
app:entries="@array/pref__gestures__swipe_action__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_action__values"
|
||||
app:dependency="glide__enabled"
|
||||
app:key="gestures__swipe_right"
|
||||
app:title="@string/pref__gestures__swipe_right__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="move_cursor_left"
|
||||
app:entries="@array/pref__gestures__swipe_action__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_action__values"
|
||||
app:key="gestures__space_bar_swipe_left"
|
||||
app:title="@string/pref__gestures__space_bar_swipe_left__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="move_cursor_right"
|
||||
app:entries="@array/pref__gestures__swipe_action__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_action__values"
|
||||
app:key="gestures__space_bar_swipe_right"
|
||||
app:title="@string/pref__gestures__space_bar_swipe_right__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="delete_word"
|
||||
app:entries="@array/pref__gestures__swipe_action__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_action__values"
|
||||
app:key="gestures__delete_key_swipe_left"
|
||||
app:title="@string/pref__gestures__delete_key_swipe_left__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<!--<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="normal"
|
||||
app:entries="@array/pref__gestures__swipe_velocity_threshold__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_velocity_threshold__values"
|
||||
app:key="gestures__swipe_velocity_threshold"
|
||||
app:title="@string/pref__gestures__swipe_velocity_threshold__label"
|
||||
app:useSimpleSummaryProvider="true"/>-->
|
||||
|
||||
<ListPreference
|
||||
app:iconSpaceReserved="false"
|
||||
android:defaultValue="normal"
|
||||
app:entries="@array/pref__gestures__swipe_distance_threshold__entries"
|
||||
app:entryValues="@array/pref__gestures__swipe_distance_threshold__values"
|
||||
app:key="gestures__swipe_distance_threshold"
|
||||
app:title="@string/pref__gestures__swipe_distance_threshold__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -2,6 +2,26 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__group_keys__label">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="keyboard__hinted_number_row"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__hinted_number_row__label"
|
||||
app:summary="@string/pref__keyboard__hinted_number_row__summary"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="keyboard__hinted_symbols"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__hinted_symbols__label"
|
||||
app:summary="@string/pref__keyboard__hinted_symbols__summary"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__group_layout__label">
|
||||
@@ -24,6 +44,17 @@
|
||||
app:title="@string/pref__keyboard__height_factor__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<dev.patrickgold.florisboard.settings.components.DialogSeekBarPreference
|
||||
app:allowDividerAbove="false"
|
||||
android:defaultValue="0"
|
||||
app:key="keyboard__bottom_offset"
|
||||
app:min="0"
|
||||
app:max="24"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__bottom_offset__label"
|
||||
app:seekBarIncrement="1"
|
||||
app:unit=" dp"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
app:enabled="false"
|
||||
app:key="suggestion__enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__suggestion__enabled__label"
|
||||
@@ -36,6 +37,13 @@
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__correction__title">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:key="correction__auto_capitalization"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__correction__auto_capitalization__label"
|
||||
app:summary="@string/pref__correction__auto_capitalization__summary"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:key="correction__double_space_period"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.4.0'
|
||||
ext.kotlin_version = '1.4.10'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||
classpath 'com.android.tools.build:gradle:4.0.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
||||
9
fastlane/metadata/android/en-US/changelogs/12.txt
Normal file
9
fastlane/metadata/android/en-US/changelogs/12.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
- Add theming capability
|
||||
- Preset selection
|
||||
- Modify each color individually
|
||||
- Themes are now defined in assets/ime/theme/ as a json file, which allows for a more convenient way to add custom themes (base for future feature)
|
||||
- Restructure settings naming scheme and structure
|
||||
- Add back button functionality in settings
|
||||
- Add clipboard/cursor tools
|
||||
- Improve layout measurement and height behaviour
|
||||
- Fix enter action key bug (#17)
|
||||
4
fastlane/metadata/android/en-US/changelogs/13.txt
Normal file
4
fastlane/metadata/android/en-US/changelogs/13.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
- Add gestures & scrolling space bar
|
||||
- Swipe up / down / left / right
|
||||
- Swipe left from delete key (defaults to deleting current word)
|
||||
- Moving your finger along the space bar will move the cursor
|
||||
14
fastlane/metadata/android/en-US/changelogs/14.txt
Normal file
14
fastlane/metadata/android/en-US/changelogs/14.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
- Add number row / underlying symbol support in character layouts
|
||||
- First row of each character layout has numbers 1-9 and 0 integrated
|
||||
- Second and third row have symbols according to the symbol layout
|
||||
- Number row / Underlying symbols can be enabled/disabled seperately
|
||||
in the preferences
|
||||
- Add bottom offset option to accommodate for curved screens (#20)
|
||||
- Add option to turn off auto-capitalization (#21)
|
||||
- Fix clipboard/cursor UI not updating in Smartbar when text selection
|
||||
has changed
|
||||
- Improve emoji layout
|
||||
- Scroll orientation is now vertical to better scale to different sizes
|
||||
of the keyboard
|
||||
- Temporarily remove swipe velocity threshold as it causes gestures to be
|
||||
ignored in certain circumstances
|
||||
@@ -1,17 +1,12 @@
|
||||
FlorisBoard is an open-source keyboard aimed at providing you with an easy way to type while respecting your privacy.
|
||||
|
||||
Note: This project is currently in alpha stage. If you want to see a feature being implemented or want to report a bug, please visit this project's repository (linked in the end of the description) on GitHub and file an issue. This helps making FlorisBoard even better! Thank you!
|
||||
|
||||
Currently implemented and fully working features:
|
||||
* Latin keyboard layouts
|
||||
* QWERTY, QWERTZ, AZERTY, Spanish, Norwegian, Swedish/Finnish, Danish, Icelandic and Swiss. More coming in future releases
|
||||
* Easy switching between languages/layouts by defining subtypes in the settings
|
||||
* Keyboard layouts for typing in a (phone) number
|
||||
* Special characters input
|
||||
* Emoji/Emoticon keyboard
|
||||
* One-handed/compact mode for easier typing on large devices
|
||||
* Light and dark theme for the input UI (more themes will come in future releases)
|
||||
* Customization of key press sound/vibration
|
||||
|
||||
Source code (Apache 2.0) and project management:
|
||||
https://github.com/florisboard/florisboard
|
||||
<p><i>FlorisBoard</i> is an open-source keyboard aimed at providing you with an easy way to type while respecting your privacy.</p>
|
||||
<p><b>Note:</b> This project is currently in alpha stage. If you want to see a feature being implemented or want to report a bug, please visit this project's repository (linked in the end of the description) on GitHub and file an issue. This helps making FlorisBoard even better! Thank you!</p>
|
||||
<p><br><b>Currently implemented and fully working features:</b></p><ul>
|
||||
<li>Latin keyboard layouts</li>
|
||||
<li>QWERTY, QWERTZ, AZERTY, Spanish, Norwegian, Swedish/Finnish, Danish, Icelandic and Swiss. More coming in future releases</li>
|
||||
<li>Easy switching between languages/layouts by defining subtypes in the settings</li>
|
||||
<li>Keyboard layouts for typing in a (phone) number</li>
|
||||
<li>Special characters input</li>
|
||||
<li>Emoji/Emoticon keyboard</li>
|
||||
<li>One-handed/compact mode for easier typing on large devices</li>
|
||||
<li>Light and dark theme for the input UI (more themes will come in future releases)</li>
|
||||
<li>Customization of key press sound/vibration</li></ul>
|
||||
9
fastlane/metadata/android/it/changelogs/12.txt
Normal file
9
fastlane/metadata/android/it/changelogs/12.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
- Aggiunta di capacità di tematizzazione
|
||||
- Selezione delle preimpostazioni
|
||||
- Modificare ogni colore individualmente
|
||||
- I temi sono ora definiti in asset/imo/tema/ come un file json, che permette un modo più conveniente per aggiungere temi personalizzati (base per la funzione futura)
|
||||
- Schema di denominazione e struttura delle impostazioni della ristrutturazione
|
||||
- Aggiungere la funzionalità del pulsante indietro nelle impostazioni
|
||||
- Aggiunta di strumenti per appunti/cursori
|
||||
- Migliorare la misurazione del layout e il comportamento in altezza
|
||||
- Correggere il bug del tasto azione (#17)
|
||||
4
fastlane/metadata/android/it/changelogs/13.txt
Normal file
4
fastlane/metadata/android/it/changelogs/13.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
- Aggiungi gesti e barra spaziatrice a scorrimento
|
||||
- Scorri su / giù / sinistra / destra
|
||||
- Passare il dito a sinistra dal tasto di cancellazione (default per cancellare la parola corrente)
|
||||
- Muovendo il dito lungo la barra spaziatrice, il cursore si sposta
|
||||
14
fastlane/metadata/android/it/changelogs/14.txt
Normal file
14
fastlane/metadata/android/it/changelogs/14.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
- Aggiungere la riga del numero / il supporto del simbolo sottostante nei layout dei caratteri
|
||||
- La prima riga di ogni disposizione dei caratteri ha i numeri 1-9 e 0 integrati
|
||||
- La seconda e la terza riga hanno i simboli secondo la disposizione dei simboli
|
||||
- Riga dei numeri / I simboli sottostanti possono essere abilitati/disabilitati separatamente
|
||||
nelle preferenze
|
||||
- Aggiungete l'opzione di offset dal basso per accogliere gli schermi curvi (#20)
|
||||
- Aggiungere l'opzione per disattivare l'autocapitalizzazione (#21)
|
||||
- Fix clipboard/cursore UI non aggiornato in Smartbar quando si seleziona il testo
|
||||
è cambiato
|
||||
- Migliorare il layout emoji
|
||||
- L'orientamento di scorrimento è ora verticale per meglio scalare le diverse dimensioni
|
||||
della tastiera
|
||||
- Rimuovere temporaneamente la soglia di velocità di strisciata in quanto fa sì che i gesti siano
|
||||
ignorato in determinate circostanze
|
||||
@@ -1,17 +1,12 @@
|
||||
FlorisBoard è una tastiera open source mirata a fornirti un modo semplice per digitare nel rispetto della tua privacy.
|
||||
|
||||
Nota: questo progetto è attualmente in fase alpha. Se desideri vedere una funzionalità implementata o segnalare un bug, visita il repository di questo progetto (link alla fine della descrizione) su GitHub e segnala un problema. Questo aiuta a rendere FlorisBoard ancora migliore! Grazie!
|
||||
|
||||
Funzionalità attualmente implementate e completamente funzionanti:
|
||||
* Layout della tastiera
|
||||
* QWERTY, QWERTZ, AZERTY, Spagnolo, norvegese, svedese,finlandese, danese, islandese e svizzero. Altre novità in arrivo nelle versioni future
|
||||
* Facile passaggio tra lingue / layout nelle impostazioni
|
||||
* Layout di tastiera tipo tastierino telefonico per digitazione di un numero
|
||||
* Inserimento di caratteri speciali
|
||||
* Tastiera Emoji/Emoticon
|
||||
* Modalità con una sola mano per digitare più facilmente su dispositivi di grandi dimensioni
|
||||
* Tema chiaro e scuro per l'interfaccia utente e per tastiera (altri temi arriveranno nelle versioni future)
|
||||
* Personalizzazione del suono / vibrazione della pressione dei tasti
|
||||
|
||||
Codice sorgente (Apache 2.0) e gestione del progetto:
|
||||
https://github.com/florisboard/florisboard
|
||||
<p><i>FlorisBoard</i> è una tastiera open source mirata a fornirti un modo semplice per digitare nel rispetto della tua privacy.</p>
|
||||
<p><b>Nota:</b> questo progetto è attualmente in fase alpha. Se desideri vedere una funzionalità implementata o segnalare un bug, visita il repository di questo progetto (link alla fine della descrizione) su GitHub e segnala un problema. Questo aiuta a rendere FlorisBoard ancora migliore! Grazie!</p>
|
||||
<p><br><b>Funzionalità attualmente implementate e completamente funzionanti:</b></p><ul>
|
||||
<li>Layout della tastiera</li>
|
||||
<li>QWERTY, QWERTZ, AZERTY, Spagnolo, norvegese, svedese,finlandese, danese, islandese e svizzero. Altre novità in arrivo nelle versioni future</li>
|
||||
<li>Facile passaggio tra lingue / layout nelle impostazioni</li>
|
||||
<li>Layout di tastiera tipo tastierino telefonico per digitazione di un numero</li>
|
||||
<li>Inserimento di caratteri speciali</li>
|
||||
<li>Tastiera Emoji/Emoticon</li>
|
||||
<li>Modalità con una sola mano per digitare più facilmente su dispositivi di grandi dimensioni</li>
|
||||
<li>Tema chiaro e scuro per l'interfaccia utente e per tastiera (altri temi arriveranno nelle versioni future)</li>
|
||||
<li>Personalizzazione del suono / vibrazione della pressione dei tasti</li></ul>
|
||||
Reference in New Issue
Block a user