Compare commits
43 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 | ||
|
|
a25501d63c | ||
|
|
e9a5f2161c | ||
|
|
6f12f22937 | ||
|
|
25054ef679 | ||
|
|
3af17f99fe | ||
|
|
06664ff521 | ||
|
|
fde0749a3b | ||
|
|
c061e15263 | ||
|
|
7256c597c2 | ||
|
|
2f9d32027b | ||
|
|
538912edc2 | ||
|
|
ee5ff81ee8 | ||
|
|
d873dc54c5 | ||
|
|
1e967463de | ||
|
|
5c084a10dc | ||
|
|
f158a9deb3 | ||
|
|
66d328293c | ||
|
|
e33b652bb3 | ||
|
|
65d8c02b95 | ||
|
|
bd090132eb |
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>`
|
||||
|
||||
77
README.md
77
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)
|
||||
* [ ] Theme customization
|
||||
* [ ] Theme import/export (?)
|
||||
* [x] Customize look and behaviour of keyboard
|
||||
* [x] Theme presets (currently only day/night theme)
|
||||
* [x] Theme customization
|
||||
* [ ] 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,25 +92,33 @@ 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:
|
||||
|
||||
## Used libraries and icons
|
||||
(?) = 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)
|
||||
by [google](https://github.com/google)
|
||||
* [Google Material icons](https://github.com/google/material-design-icons) by
|
||||
[google](https://github.com/google)
|
||||
* [Moshi JSON library](https://github.com/square/moshi) by
|
||||
[square](https://github.com/square)
|
||||
* [ColorPicker preference](https://github.com/jaredrummler/ColorPicker) by
|
||||
[Jared Rummler](https://github.com/jaredrummler)
|
||||
|
||||
## License
|
||||
```
|
||||
|
||||
@@ -10,8 +10,8 @@ android {
|
||||
applicationId "dev.patrickgold.florisboard"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 11
|
||||
versionName "0.1.2"
|
||||
versionCode 14
|
||||
versionName "0.2.2"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -31,10 +31,11 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.1'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'androidx.test:core:1.2.0'
|
||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||
@@ -43,7 +44,8 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
implementation 'com.google.android:flexbox:2.0.1'
|
||||
implementation "com.squareup.moshi:moshi-kotlin:1.9.2"
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.2.0'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"
|
||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
||||
}
|
||||
|
||||
@@ -74,6 +74,14 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/SettingsTheme"/>
|
||||
|
||||
<!-- Advanced Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.settings.AdvancedActivity"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/settings__advanced__title"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/SettingsTheme"/>
|
||||
|
||||
<!-- Setup Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.setup.SetupActivity"
|
||||
|
||||
@@ -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" }
|
||||
]
|
||||
}
|
||||
|
||||
60
app/src/main/assets/ime/theme/floris_day.json
Normal file
60
app/src/main/assets/ime/theme/floris_day.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "floris_day",
|
||||
"displayName": "Floris Day",
|
||||
"author": "patrickgold",
|
||||
"isNightTheme": false,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#4CAF50",
|
||||
"colorPrimaryDark": "#388E3C",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/bgColor",
|
||||
"navigationBarLight": "true",
|
||||
"semiTransparentColor": "#20000000",
|
||||
"textColor": "#000000"
|
||||
},
|
||||
"keyboard": {
|
||||
"bgColor": "#E0E0E0"
|
||||
},
|
||||
"key": {
|
||||
"bgColor": "#FFFFFF",
|
||||
"bgColorPressed": "#F5F5F5",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyEnter": {
|
||||
"bgColor": "@window/colorPrimary",
|
||||
"bgColorPressed": "@window/colorPrimaryDark",
|
||||
"fgColor": "#FFFFFF"
|
||||
},
|
||||
"keyPopup": {
|
||||
"bgColor": "#EEEEEE",
|
||||
"bgColorActive": "#BDBDBD",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyShift": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"bgColorPressed": "@key/bgColorPressed",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorCapsLock": "@window/colorAccent"
|
||||
},
|
||||
"media": {
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#757575"
|
||||
},
|
||||
"oneHanded": {
|
||||
"bgColor": "#E8F5E9"
|
||||
},
|
||||
"oneHandedButton": {
|
||||
"fgColor": "#424242"
|
||||
},
|
||||
"smartbar": {
|
||||
"bgColor": "transparent",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#8A8A8A"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"fgColor": "@key/fgColor"
|
||||
}
|
||||
}
|
||||
}
|
||||
60
app/src/main/assets/ime/theme/floris_night.json
Normal file
60
app/src/main/assets/ime/theme/floris_night.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "floris_night",
|
||||
"displayName": "Floris Night",
|
||||
"author": "patrickgold",
|
||||
"isNightTheme": true,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#4CAF50",
|
||||
"colorPrimaryDark": "#388E3C",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/bgColor",
|
||||
"navigationBarLight": "false",
|
||||
"semiTransparentColor": "#20FFFFFF",
|
||||
"textColor": "#FFFFFF"
|
||||
},
|
||||
"keyboard": {
|
||||
"bgColor": "#212121"
|
||||
},
|
||||
"key": {
|
||||
"bgColor": "#424242",
|
||||
"bgColorPressed": "#616161",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyEnter": {
|
||||
"bgColor": "@window/colorPrimary",
|
||||
"bgColorPressed": "@window/colorPrimaryDark",
|
||||
"fgColor": "#FFFFFF"
|
||||
},
|
||||
"keyPopup": {
|
||||
"bgColor": "#757575",
|
||||
"bgColorActive": "#BDBDBD",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyShift": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"bgColorPressed": "@key/bgColorPressed",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorCapsLock": "@window/colorAccent"
|
||||
},
|
||||
"media": {
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#BDBDBD"
|
||||
},
|
||||
"oneHanded": {
|
||||
"bgColor": "#1B5E20"
|
||||
},
|
||||
"oneHandedButton": {
|
||||
"fgColor": "#EEEEEE"
|
||||
},
|
||||
"smartbar": {
|
||||
"bgColor": "transparent",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#73FFFFFF"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"fgColor": "@key/fgColor"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,24 @@ 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.
|
||||
</pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>ColorPicker preference</h3>
|
||||
<span>Copyright 2016 Jared Rummler / Copyright 2015 Daniel Nilsson</span>
|
||||
<pre>
|
||||
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.
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.SuppressLint
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.inputmethodservice.InputMethodService
|
||||
import android.media.AudioManager
|
||||
@@ -33,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
|
||||
@@ -59,8 +60,10 @@ class FlorisBoard : InputMethodService() {
|
||||
private set
|
||||
|
||||
val context: Context
|
||||
get() = inputView?.context ?: this
|
||||
private var inputView: InputView? = null
|
||||
get() = inputWindowView?.context ?: this
|
||||
var inputView: InputView? = null
|
||||
private set
|
||||
private var inputWindowView: InputWindowView? = null
|
||||
private var eventListeners: MutableList<EventListener> = mutableListOf()
|
||||
|
||||
private var audioManager: AudioManager? = null
|
||||
@@ -70,6 +73,7 @@ class FlorisBoard : InputMethodService() {
|
||||
|
||||
lateinit var subtypeManager: SubtypeManager
|
||||
lateinit var activeSubtype: Subtype
|
||||
private var currentThemeIsNight: Boolean = false
|
||||
private var currentThemeResId: Int = 0
|
||||
|
||||
val textInputManager: TextInputManager
|
||||
@@ -112,6 +116,13 @@ class FlorisBoard : InputMethodService() {
|
||||
fun getInstanceOrNull(): FlorisBoard? {
|
||||
return florisboardInstance
|
||||
}
|
||||
|
||||
fun getDayNightBaseThemeId(isNightTheme: Boolean): Int {
|
||||
return when (isNightTheme) {
|
||||
true -> R.style.KeyboardThemeBase_Night
|
||||
else -> R.style.KeyboardThemeBase_Day
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
@@ -138,19 +149,21 @@ class FlorisBoard : InputMethodService() {
|
||||
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
prefs = PrefHelper(this)
|
||||
prefs = PrefHelper.getDefaultInstance(this)
|
||||
prefs.initDefaultPreferences()
|
||||
prefs.sync()
|
||||
subtypeManager = SubtypeManager(this, prefs)
|
||||
activeSubtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
|
||||
|
||||
currentThemeResId = prefs.theme.getSelectedThemeResId()
|
||||
currentThemeIsNight = prefs.internal.themeCurrentIsNight
|
||||
currentThemeResId = getDayNightBaseThemeId(currentThemeIsNight)
|
||||
setTheme(currentThemeResId)
|
||||
updateTheme()
|
||||
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
|
||||
super.onCreate()
|
||||
eventListeners.forEach { it.onCreate() }
|
||||
eventListeners.toList().forEach { it.onCreate() }
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@@ -159,11 +172,11 @@ class FlorisBoard : InputMethodService() {
|
||||
|
||||
baseContext.setTheme(currentThemeResId)
|
||||
|
||||
inputView = layoutInflater.inflate(R.layout.florisboard, null) as InputView
|
||||
inputWindowView = layoutInflater.inflate(R.layout.florisboard, null) as InputWindowView
|
||||
|
||||
eventListeners.forEach { it.onCreateInputView() }
|
||||
eventListeners.toList().forEach { it.onCreateInputView() }
|
||||
|
||||
return inputView
|
||||
return inputWindowView
|
||||
}
|
||||
|
||||
fun registerInputView(inputView: InputView) {
|
||||
@@ -171,10 +184,11 @@ class FlorisBoard : InputMethodService() {
|
||||
|
||||
this.inputView = inputView
|
||||
initializeOneHandedEnvironment()
|
||||
updateTheme()
|
||||
updateSoftInputWindowLayoutParameters()
|
||||
updateOneHandedPanelVisibility()
|
||||
|
||||
eventListeners.forEach { it.onRegisterInputView(inputView) }
|
||||
eventListeners.toList().forEach { it.onRegisterInputView(inputView) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -183,43 +197,44 @@ class FlorisBoard : InputMethodService() {
|
||||
osHandler.removeCallbacksAndMessages(null)
|
||||
florisboardInstance = null
|
||||
|
||||
eventListeners.toList().forEach { it.onDestroy() }
|
||||
eventListeners.clear()
|
||||
super.onDestroy()
|
||||
eventListeners.forEach { it.onDestroy() }
|
||||
}
|
||||
|
||||
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
|
||||
currentInputConnection?.requestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR)
|
||||
|
||||
super.onStartInputView(info, restarting)
|
||||
eventListeners.forEach { it.onStartInputView(info, restarting) }
|
||||
eventListeners.toList().forEach { it.onStartInputView(info, restarting) }
|
||||
}
|
||||
|
||||
override fun onFinishInputView(finishingInput: Boolean) {
|
||||
currentInputConnection?.requestCursorUpdates(0)
|
||||
|
||||
super.onFinishInputView(finishingInput)
|
||||
eventListeners.forEach { it.onFinishInputView(finishingInput) }
|
||||
eventListeners.toList().forEach { it.onFinishInputView(finishingInput) }
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onWindowShown()")
|
||||
|
||||
prefs.sync()
|
||||
updateThemeIfNecessary()
|
||||
updateTheme()
|
||||
updateOneHandedPanelVisibility()
|
||||
activeSubtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
|
||||
onSubtypeChanged(activeSubtype)
|
||||
setActiveInput(R.id.text_input)
|
||||
|
||||
super.onWindowShown()
|
||||
eventListeners.forEach { it.onWindowShown() }
|
||||
eventListeners.toList().forEach { it.onWindowShown() }
|
||||
}
|
||||
|
||||
override fun onWindowHidden() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onWindowHidden()")
|
||||
|
||||
super.onWindowHidden()
|
||||
eventListeners.forEach { it.onWindowHidden() }
|
||||
eventListeners.toList().forEach { it.onWindowHidden() }
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
@@ -232,7 +247,7 @@ class FlorisBoard : InputMethodService() {
|
||||
|
||||
override fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {
|
||||
super.onUpdateCursorAnchorInfo(cursorAnchorInfo)
|
||||
eventListeners.forEach { it.onUpdateCursorAnchorInfo(cursorAnchorInfo) }
|
||||
eventListeners.toList().forEach { it.onUpdateCursorAnchorInfo(cursorAnchorInfo) }
|
||||
}
|
||||
|
||||
override fun onUpdateSelection(
|
||||
@@ -251,7 +266,7 @@ class FlorisBoard : InputMethodService() {
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
eventListeners.forEach {
|
||||
eventListeners.toList().forEach {
|
||||
it.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
@@ -264,40 +279,52 @@ class FlorisBoard : InputMethodService() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the preferences if the selected theme res id has changed and updates the theme only
|
||||
* then by rebuilding the UI and setting the navigation bar theme manually.
|
||||
* Reapplies the supplies colors and settings from prefs to navigation bar.
|
||||
*/
|
||||
private fun updateThemeIfNecessary() {
|
||||
val newThemeResId = prefs.theme.getSelectedThemeResId()
|
||||
if (newThemeResId != currentThemeResId) {
|
||||
currentThemeResId = newThemeResId
|
||||
private fun updateTheme() {
|
||||
val newThemeIsNightMode = prefs.internal.themeCurrentIsNight
|
||||
if (currentThemeIsNight != newThemeIsNightMode) {
|
||||
currentThemeResId = getDayNightBaseThemeId(newThemeIsNightMode)
|
||||
currentThemeIsNight = newThemeIsNightMode
|
||||
setInputView(onCreateInputView())
|
||||
val w = window?.window ?: return
|
||||
w.navigationBarColor = getColorFromAttr(baseContext, android.R.attr.navigationBarColor)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
var flags = w.decorView.systemUiVisibility
|
||||
flags = if (getBooleanFromAttr(baseContext, android.R.attr.windowLightNavigationBar)) {
|
||||
flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
} else {
|
||||
flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
|
||||
}
|
||||
w.decorView.systemUiVisibility = flags
|
||||
}
|
||||
return
|
||||
}
|
||||
val w = window?.window ?: return
|
||||
inputView?.setBackgroundColor(prefs.theme.keyboardBgColor)
|
||||
w.navigationBarColor = prefs.theme.navBarColor
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
var flags = w.decorView.systemUiVisibility
|
||||
flags = if (prefs.theme.navBarIsLight) {
|
||||
flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
} else {
|
||||
flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
|
||||
}
|
||||
w.decorView.systemUiVisibility = flags
|
||||
}
|
||||
inputView?.oneHandedCtrlPanelStart?.setBackgroundColor(prefs.theme.oneHandedBgColor)
|
||||
inputView?.oneHandedCtrlPanelEnd?.setBackgroundColor(prefs.theme.oneHandedBgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_move_start)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_move_end)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_close_start)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_close_end)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
eventListeners.toList().forEach { it.onApplyThemeAttributes() }
|
||||
}
|
||||
|
||||
override fun onComputeInsets(outInsets: Insets?) {
|
||||
super.onComputeInsets(outInsets)
|
||||
val inputView = this.inputView ?: return
|
||||
val inputWindowView = this.inputWindowView ?: return
|
||||
// TODO: Check also if the keyboard is currently suppressed by a hardware keyboard
|
||||
if (!isInputViewShown) {
|
||||
outInsets?.contentTopInsets = inputView.height
|
||||
outInsets?.visibleTopInsets = inputView.height
|
||||
outInsets?.contentTopInsets = inputWindowView.height
|
||||
outInsets?.visibleTopInsets = inputWindowView.height
|
||||
return
|
||||
}
|
||||
val innerInputViewContainer =
|
||||
inputView.findViewById<LinearLayout>(R.id.inner_input_view_container) ?: return
|
||||
val visibleTopY = inputView.height - innerInputViewContainer.measuredHeight
|
||||
val visibleTopY = inputWindowView.height - inputView.measuredHeight
|
||||
outInsets?.contentTopInsets = visibleTopY
|
||||
outInsets?.visibleTopInsets = visibleTopY
|
||||
}
|
||||
@@ -313,8 +340,8 @@ class FlorisBoard : InputMethodService() {
|
||||
private fun updateSoftInputWindowLayoutParameters() {
|
||||
val w = window?.window ?: return
|
||||
ViewLayoutUtils.updateLayoutHeightOf(w, WindowManager.LayoutParams.MATCH_PARENT)
|
||||
val inputView = this.inputView
|
||||
if (inputView != null) {
|
||||
val inputWindowView = this.inputWindowView
|
||||
if (inputWindowView != null) {
|
||||
val layoutHeight = if (isFullscreenMode) {
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
} else {
|
||||
@@ -323,7 +350,7 @@ class FlorisBoard : InputMethodService() {
|
||||
val inputArea = w.findViewById<View>(android.R.id.inputArea)
|
||||
ViewLayoutUtils.updateLayoutHeightOf(inputArea, layoutHeight)
|
||||
ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM)
|
||||
ViewLayoutUtils.updateLayoutHeightOf(inputView, layoutHeight)
|
||||
ViewLayoutUtils.updateLayoutHeightOf(inputWindowView, layoutHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,9 +358,9 @@ class FlorisBoard : InputMethodService() {
|
||||
* Makes a key press vibration if the user has this feature enabled in the preferences.
|
||||
*/
|
||||
fun keyPressVibrate() {
|
||||
if (prefs.looknfeel.vibrationEnabled) {
|
||||
var vibrationStrength = prefs.looknfeel.vibrationStrength
|
||||
if (vibrationStrength == -1 && prefs.looknfeel.vibrationEnabledSystem) {
|
||||
if (prefs.keyboard.vibrationEnabled) {
|
||||
var vibrationStrength = prefs.keyboard.vibrationStrength
|
||||
if (vibrationStrength == -1 && prefs.keyboard.vibrationEnabledSystem) {
|
||||
vibrationStrength = 36
|
||||
}
|
||||
if (vibrationStrength > 0) {
|
||||
@@ -355,15 +382,15 @@ class FlorisBoard : InputMethodService() {
|
||||
* Makes a key press sound if the user has this feature enabled in the preferences.
|
||||
*/
|
||||
fun keyPressSound(keyData: KeyData? = null) {
|
||||
if (prefs.looknfeel.soundEnabled) {
|
||||
val soundVolume = prefs.looknfeel.soundVolume
|
||||
if (prefs.keyboard.soundEnabled) {
|
||||
val soundVolume = prefs.keyboard.soundVolume
|
||||
val effect = when (keyData?.code) {
|
||||
KeyCode.SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR
|
||||
KeyCode.DELETE -> AudioManager.FX_KEYPRESS_DELETE
|
||||
KeyCode.ENTER -> AudioManager.FX_KEYPRESS_RETURN
|
||||
else -> AudioManager.FX_KEYPRESS_STANDARD
|
||||
}
|
||||
if (soundVolume == -1 && prefs.looknfeel.soundEnabledSystem) {
|
||||
if (soundVolume == -1 && prefs.keyboard.soundEnabledSystem) {
|
||||
audioManager!!.playSoundEffect(effect)
|
||||
} else if (soundVolume > 0) {
|
||||
audioManager!!.playSoundEffect(effect, soundVolume / 100f)
|
||||
@@ -371,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].
|
||||
*/
|
||||
@@ -390,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)
|
||||
@@ -427,26 +472,26 @@ class FlorisBoard : InputMethodService() {
|
||||
private fun onOneHandedPanelButtonClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.one_handed_ctrl_move_start -> {
|
||||
prefs.looknfeel.oneHandedMode = "start"
|
||||
prefs.keyboard.oneHandedMode = "start"
|
||||
}
|
||||
R.id.one_handed_ctrl_move_end -> {
|
||||
prefs.looknfeel.oneHandedMode = "end"
|
||||
prefs.keyboard.oneHandedMode = "end"
|
||||
}
|
||||
R.id.one_handed_ctrl_close_start,
|
||||
R.id.one_handed_ctrl_close_end -> {
|
||||
prefs.looknfeel.oneHandedMode = "off"
|
||||
prefs.keyboard.oneHandedMode = "off"
|
||||
}
|
||||
}
|
||||
updateOneHandedPanelVisibility()
|
||||
}
|
||||
|
||||
fun toggleOneHandedMode() {
|
||||
when (prefs.looknfeel.oneHandedMode) {
|
||||
when (prefs.keyboard.oneHandedMode) {
|
||||
"off" -> {
|
||||
prefs.looknfeel.oneHandedMode = "end"
|
||||
prefs.keyboard.oneHandedMode = "end"
|
||||
}
|
||||
else -> {
|
||||
prefs.looknfeel.oneHandedMode = "off"
|
||||
prefs.keyboard.oneHandedMode = "off"
|
||||
}
|
||||
}
|
||||
updateOneHandedPanelVisibility()
|
||||
@@ -457,7 +502,7 @@ class FlorisBoard : InputMethodService() {
|
||||
inputView?.oneHandedCtrlPanelStart?.visibility = View.GONE
|
||||
inputView?.oneHandedCtrlPanelEnd?.visibility = View.GONE
|
||||
} else {
|
||||
when (prefs.looknfeel.oneHandedMode) {
|
||||
when (prefs.keyboard.oneHandedMode) {
|
||||
"off" -> {
|
||||
inputView?.oneHandedCtrlPanelStart?.visibility = View.GONE
|
||||
inputView?.oneHandedCtrlPanelEnd?.visibility = View.GONE
|
||||
@@ -521,6 +566,7 @@ class FlorisBoard : InputMethodService() {
|
||||
candidatesEnd: Int
|
||||
) {}
|
||||
|
||||
fun onApplyThemeAttributes() {}
|
||||
fun onSubtypeChanged(newSubtype: Subtype) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ViewFlipper
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
/**
|
||||
* Custom ViewFlipper class used to prevent an unnecessary exception to be thrown when it is
|
||||
* detached from a window.
|
||||
*
|
||||
* Based on the solution of this SO answer: https://stackoverflow.com/a/8208874/6801193
|
||||
*/
|
||||
class FlorisViewFlipper : ViewFlipper {
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
try {
|
||||
super.onDetachedFromWindow()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
stopFlipping()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,20 +17,31 @@
|
||||
package dev.patrickgold.florisboard.ime.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.widget.FrameLayout
|
||||
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
|
||||
|
||||
/**
|
||||
* Root view of the keyboard. Notifies [FlorisBoard] when it has been attached to a window.
|
||||
*/
|
||||
class InputView : FrameLayout {
|
||||
|
||||
class InputView : LinearLayout {
|
||||
private var florisboard: FlorisBoard = FlorisBoard.getInstance()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
var desiredInputViewHeight: Int = resources.getDimension(R.dimen.inputView_baseHeight).roundToInt()
|
||||
private set
|
||||
var desiredSmartbarHeight: Int = resources.getDimension(R.dimen.smartbar_baseHeight).roundToInt()
|
||||
private set
|
||||
var desiredTextKeyboardViewHeight: Int = resources.getDimension(R.dimen.textKeyboardView_baseHeight).roundToInt()
|
||||
private set
|
||||
var desiredMediaKeyboardViewHeight: Int = resources.getDimension(R.dimen.mediaKeyboardView_baseHeight).roundToInt()
|
||||
private set
|
||||
|
||||
var mainViewFlipper: ViewFlipper? = null
|
||||
private set
|
||||
@@ -54,4 +65,34 @@ class InputView : FrameLayout {
|
||||
|
||||
florisboard.registerInputView(this)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val heightFactor = when (resources.configuration.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> 0.85f
|
||||
else -> if (prefs.keyboard.oneHandedMode != "off") {
|
||||
0.9f
|
||||
} else {
|
||||
1.0f
|
||||
}
|
||||
} * when (prefs.keyboard.heightFactor) {
|
||||
"extra_short" -> 0.85f
|
||||
"short" -> 0.90f
|
||||
"mid_short" -> 0.95f
|
||||
"normal" -> 1.00f
|
||||
"mid_tall" -> 1.05f
|
||||
"tall" -> 1.10f
|
||||
"extra_tall" -> 1.15f
|
||||
else -> 1.00f
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ViewFlipper
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
/**
|
||||
* Root view of the keyboard.
|
||||
*/
|
||||
class InputWindowView : FrameLayout {
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
}
|
||||
@@ -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,11 +40,12 @@ 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 looknfeel = Looknfeel(this)
|
||||
val localization = Localization(this)
|
||||
val suggestion = Suggestion(this)
|
||||
val popup = PopUp(this)
|
||||
val theme = Theme(this)
|
||||
|
||||
/**
|
||||
@@ -111,15 +115,28 @@ class PrefHelper(
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var defaultInstance: PrefHelper? = null
|
||||
|
||||
@Synchronized
|
||||
fun getDefaultInstance(context: Context): PrefHelper {
|
||||
if (defaultInstance == null) {
|
||||
defaultInstance = PrefHelper(context)
|
||||
}
|
||||
return defaultInstance!!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the [PreferenceManager] to set the defined preferences to their default values, if
|
||||
* they have not been initialized yet.
|
||||
*/
|
||||
fun initDefaultPreferences() {
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_advanced, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_gestures, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_keyboard, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_looknfeel, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_theme, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_typing, true)
|
||||
//setPref(Keyboard.SUBTYPES, "")
|
||||
//setPref(Internal.IS_IME_SET_UP, false)
|
||||
}
|
||||
@@ -129,10 +146,10 @@ class PrefHelper(
|
||||
*/
|
||||
fun sync() {
|
||||
val contentResolver = context.contentResolver
|
||||
looknfeel.soundEnabledSystem = Settings.System.getInt(
|
||||
keyboard.soundEnabledSystem = Settings.System.getInt(
|
||||
contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 0
|
||||
) != 0
|
||||
looknfeel.vibrationEnabledSystem = Settings.System.getInt(
|
||||
keyboard.vibrationEnabledSystem = Settings.System.getInt(
|
||||
contentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, 0
|
||||
) != 0
|
||||
|
||||
@@ -163,37 +180,117 @@ 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 internal preferences.
|
||||
* 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).
|
||||
*/
|
||||
class Internal(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val IS_IME_SET_UP = "internal__is_ime_set_up"
|
||||
const val VERSION_ON_INSTALL = "internal__version_on_install"
|
||||
const val VERSION_LAST_USE = "internal__version_last_use"
|
||||
const val VERSION_LAST_CHANGELOG = "internal__version_last_changelog"
|
||||
const val IS_IME_SET_UP = "internal__is_ime_set_up"
|
||||
const val THEME_CURRENT_BASED_ON = "internal__theme_current_based_on"
|
||||
const val THEME_CURRENT_IS_MODIFIED = "internal__theme_current_is_modified"
|
||||
const val THEME_CURRENT_IS_NIGHT = "internal__theme_current_is_night"
|
||||
const val VERSION_ON_INSTALL = "internal__version_on_install"
|
||||
const val VERSION_LAST_USE = "internal__version_last_use"
|
||||
const val VERSION_LAST_CHANGELOG = "internal__version_last_changelog"
|
||||
}
|
||||
|
||||
var isImeSetUp: Boolean
|
||||
get() = prefHelper.getPref(IS_IME_SET_UP, false)
|
||||
set(value) = prefHelper.setPref(IS_IME_SET_UP, value)
|
||||
get() = prefHelper.getPref(IS_IME_SET_UP, false)
|
||||
set(v) = prefHelper.setPref(IS_IME_SET_UP, v)
|
||||
var themeCurrentBasedOn: String
|
||||
get() = prefHelper.getPref(THEME_CURRENT_BASED_ON, "undefined")
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_BASED_ON, v)
|
||||
var themeCurrentIsModified: Boolean
|
||||
get() = prefHelper.getPref(THEME_CURRENT_IS_MODIFIED, false)
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_IS_MODIFIED, v)
|
||||
var themeCurrentIsNight: Boolean
|
||||
get() = prefHelper.getPref(THEME_CURRENT_IS_NIGHT, false)
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_IS_NIGHT, v)
|
||||
var versionOnInstall: String
|
||||
get() = prefHelper.getPref(VERSION_ON_INSTALL, VersionName.DEFAULT_RAW)
|
||||
set(value) = prefHelper.setPref(VERSION_ON_INSTALL, value)
|
||||
get() = prefHelper.getPref(VERSION_ON_INSTALL, VersionName.DEFAULT_RAW)
|
||||
set(v) = prefHelper.setPref(VERSION_ON_INSTALL, v)
|
||||
var versionLastUse: String
|
||||
get() = prefHelper.getPref(VERSION_LAST_USE, VersionName.DEFAULT_RAW)
|
||||
set(value) = prefHelper.setPref(VERSION_LAST_USE, value)
|
||||
get() = prefHelper.getPref(VERSION_LAST_USE, VersionName.DEFAULT_RAW)
|
||||
set(v) = prefHelper.setPref(VERSION_LAST_USE, v)
|
||||
var versionLastChangelog: String
|
||||
get() = prefHelper.getPref(VERSION_LAST_CHANGELOG, VersionName.DEFAULT_RAW)
|
||||
set(value) = prefHelper.setPref(VERSION_LAST_CHANGELOG, value)
|
||||
get() = prefHelper.getPref(VERSION_LAST_CHANGELOG, VersionName.DEFAULT_RAW)
|
||||
set(v) = prefHelper.setPref(VERSION_LAST_CHANGELOG, v)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,41 +298,40 @@ class PrefHelper(
|
||||
*/
|
||||
class Keyboard(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ACTIVE_SUBTYPE_ID = "keyboard__active_subtype_id"
|
||||
const val SUBTYPES = "keyboard__subtypes"
|
||||
}
|
||||
|
||||
var activeSubtypeId: Int
|
||||
get() = prefHelper.getPref(ACTIVE_SUBTYPE_ID, -1)
|
||||
set(v) = prefHelper.setPref(ACTIVE_SUBTYPE_ID, v)
|
||||
var subtypes: String
|
||||
get() = prefHelper.getPref(SUBTYPES, "")
|
||||
set(v) = prefHelper.setPref(SUBTYPES, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for looknfeel preferences.
|
||||
*/
|
||||
class Looknfeel(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val HEIGHT_FACTOR = "looknfeel__height_factor"
|
||||
const val LONG_PRESS_DELAY = "looknfeel__long_press_delay"
|
||||
const val ONE_HANDED_MODE = "looknfeel__one_handed_mode"
|
||||
const val SOUND_ENABLED = "looknfeel__sound_enabled"
|
||||
const val SOUND_VOLUME = "looknfeel__sound_volume"
|
||||
const val VIBRATION_ENABLED = "looknfeel__vibration_enabled"
|
||||
const val VIBRATION_STRENGTH = "looknfeel__vibration_strength"
|
||||
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"
|
||||
const val SOUND_ENABLED = "keyboard__sound_enabled"
|
||||
const val SOUND_VOLUME = "keyboard__sound_volume"
|
||||
const val VIBRATION_ENABLED = "keyboard__vibration_enabled"
|
||||
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
|
||||
var oneHandedMode: String
|
||||
get() = prefHelper.getPref(ONE_HANDED_MODE, "off")
|
||||
set(value) = prefHelper.setPref(ONE_HANDED_MODE, value)
|
||||
var popupEnabled: Boolean = false
|
||||
get() = prefHelper.getPref(POPUP_ENABLED, true)
|
||||
private set
|
||||
var soundEnabled: Boolean = false
|
||||
get() = prefHelper.getPref(SOUND_ENABLED, true)
|
||||
private set
|
||||
@@ -252,6 +348,23 @@ class PrefHelper(
|
||||
private set
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for localization preferences.
|
||||
*/
|
||||
class Localization(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ACTIVE_SUBTYPE_ID = "localization__active_subtype_id"
|
||||
const val SUBTYPES = "localization__subtypes"
|
||||
}
|
||||
|
||||
var activeSubtypeId: Int
|
||||
get() = prefHelper.getPref(ACTIVE_SUBTYPE_ID, Subtype.DEFAULT.id)
|
||||
set(v) = prefHelper.setPref(ACTIVE_SUBTYPE_ID, v)
|
||||
var subtypes: String
|
||||
get() = prefHelper.getPref(SUBTYPES, "")
|
||||
set(v) = prefHelper.setPref(SUBTYPES, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for suggestion preferences.
|
||||
*/
|
||||
@@ -273,36 +386,124 @@ class PrefHelper(
|
||||
private set
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for popup preferences.
|
||||
*/
|
||||
class PopUp(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ENABLED = "popup__enabled"
|
||||
}
|
||||
|
||||
var enabled: Boolean = false
|
||||
get() = prefHelper.getPref(ENABLED, true)
|
||||
private set
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for theme preferences.
|
||||
*/
|
||||
class Theme(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val NAME = "theme__name"
|
||||
const val COLOR_PRIMARY = "theme__colorPrimary"
|
||||
const val COLOR_PRIMARY_DARK = "theme__colorPrimaryDark"
|
||||
const val COLOR_ACCENT = "theme__colorAccent"
|
||||
const val NAV_BAR_COLOR = "theme__navBarColor"
|
||||
const val NAV_BAR_IS_LIGHT = "theme__navBarIsLight"
|
||||
const val KEYBOARD_BG_COLOR = "theme__keyboard_bgColor"
|
||||
const val KEY_BG_COLOR = "theme__key_bgColor"
|
||||
const val KEY_BG_COLOR_PRESSED = "theme__key_bgColorPressed"
|
||||
const val KEY_FG_COLOR = "theme__key_fgColor"
|
||||
const val KEY_ENTER_BG_COLOR = "theme__keyEnter_bgColor"
|
||||
const val KEY_ENTER_BG_COLOR_PRESSED = "theme__keyEnter_bgColorPressed"
|
||||
const val KEY_ENTER_FG_COLOR = "theme__keyEnter_fgColor"
|
||||
const val KEY_SHIFT_BG_COLOR = "theme__keyShift_bgColor"
|
||||
const val KEY_SHIFT_BG_COLOR_PRESSED = "theme__keyShift_bgColorPressed"
|
||||
const val KEY_SHIFT_FG_COLOR = "theme__keyShift_fgColor"
|
||||
const val KEY_SHIFT_FG_COLOR_CAPSLOCK = "theme__keyShift_fgColorCapsLock"
|
||||
const val KEY_POPUP_BG_COLOR = "theme__keyPopup_bgColor"
|
||||
const val KEY_POPUP_BG_COLOR_ACTIVE = "theme__keyPopup_bgColorActive"
|
||||
const val KEY_POPUP_FG_COLOR = "theme__keyPopup_fgColor"
|
||||
const val MEDIA_FG_COLOR = "theme__media_fgColor"
|
||||
const val MEDIA_FG_COLOR_ALT = "theme__media_fgColorAlt"
|
||||
const val ONE_HANDED_BG_COLOR = "theme__oneHanded_bgColor"
|
||||
const val ONE_HANDED_BUTTON_FG_COLOR = "theme__oneHandedButton_fgColor"
|
||||
const val SMARTBAR_BG_COLOR = "theme__smartbar_bgColor"
|
||||
const val SMARTBAR_FG_COLOR = "theme__smartbar_fgColor"
|
||||
const val SMARTBAR_FG_COLOR_ALT = "theme__smartbar_fgColorAlt"
|
||||
const val SMARTBAR_BUTTON_BG_COLOR = "theme__smartbarButton_bgColor"
|
||||
const val SMARTBAR_BUTTON_FG_COLOR = "theme__smartbarButton_fgColor"
|
||||
}
|
||||
|
||||
var name: String = ""
|
||||
get() = prefHelper.getPref(NAME, "floris_light")
|
||||
private set
|
||||
fun getSelectedThemeResId(): Int {
|
||||
return when (name) {
|
||||
"floris_light" -> R.style.KeyboardTheme_FlorisLight
|
||||
"floris_dark" -> R.style.KeyboardTheme_FlorisDark
|
||||
else -> R.style.KeyboardTheme_FlorisLight
|
||||
}
|
||||
}
|
||||
var colorPrimary: Int
|
||||
get() = prefHelper.getPref(COLOR_PRIMARY, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_PRIMARY, v)
|
||||
var colorPrimaryDark: Int
|
||||
get() = prefHelper.getPref(COLOR_PRIMARY_DARK, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_PRIMARY_DARK, v)
|
||||
var colorAccent: Int
|
||||
get() = prefHelper.getPref(COLOR_ACCENT, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_ACCENT, v)
|
||||
var navBarColor: Int
|
||||
get() = prefHelper.getPref(NAV_BAR_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(NAV_BAR_COLOR, v)
|
||||
var navBarIsLight: Boolean
|
||||
get() = prefHelper.getPref(NAV_BAR_IS_LIGHT, false)
|
||||
set(v) = prefHelper.setPref(NAV_BAR_IS_LIGHT, v)
|
||||
var keyboardBgColor: Int
|
||||
get() = prefHelper.getPref(KEYBOARD_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEYBOARD_BG_COLOR, v)
|
||||
var keyBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_BG_COLOR, v)
|
||||
var keyBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_BG_COLOR_PRESSED, v)
|
||||
var keyFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_FG_COLOR, v)
|
||||
var keyEnterBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_BG_COLOR, v)
|
||||
var keyEnterBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_BG_COLOR_PRESSED, v)
|
||||
var keyEnterFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_FG_COLOR, v)
|
||||
var keyShiftBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_BG_COLOR, v)
|
||||
var keyShiftBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_BG_COLOR_PRESSED, v)
|
||||
var keyShiftFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_FG_COLOR, v)
|
||||
var keyShiftFgColorCapsLock: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_FG_COLOR_CAPSLOCK, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_FG_COLOR_CAPSLOCK, v)
|
||||
var keyPopupBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_BG_COLOR, v)
|
||||
var keyPopupBgColorActive: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_BG_COLOR_ACTIVE, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_BG_COLOR_ACTIVE, v)
|
||||
var keyPopupFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_FG_COLOR, v)
|
||||
var mediaFgColor: Int
|
||||
get() = prefHelper.getPref(MEDIA_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(MEDIA_FG_COLOR, v)
|
||||
var mediaFgColorAlt: Int
|
||||
get() = prefHelper.getPref(MEDIA_FG_COLOR_ALT, 0)
|
||||
set(v) = prefHelper.setPref(MEDIA_FG_COLOR_ALT, v)
|
||||
var oneHandedBgColor: Int
|
||||
get() = prefHelper.getPref(ONE_HANDED_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(ONE_HANDED_BG_COLOR, v)
|
||||
var oneHandedButtonFgColor: Int
|
||||
get() = prefHelper.getPref(ONE_HANDED_BUTTON_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(ONE_HANDED_BUTTON_FG_COLOR, v)
|
||||
var smartbarBgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BG_COLOR, v)
|
||||
var smartbarFgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_FG_COLOR, v)
|
||||
var smartbarFgColorAlt: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_FG_COLOR_ALT, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_FG_COLOR_ALT, v)
|
||||
var smartbarButtonBgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BUTTON_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BUTTON_BG_COLOR, v)
|
||||
var smartbarButtonFgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BUTTON_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BUTTON_FG_COLOR, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class SubtypeManager(
|
||||
var imeConfig: FlorisBoard.ImeConfig = FlorisBoard.ImeConfig(context.packageName)
|
||||
var subtypes: List<Subtype>
|
||||
get() {
|
||||
val listRaw = prefs.keyboard.subtypes
|
||||
val listRaw = prefs.localization.subtypes
|
||||
return if (listRaw.isBlank()) {
|
||||
listOf()
|
||||
} else {
|
||||
@@ -58,7 +58,7 @@ class SubtypeManager(
|
||||
}
|
||||
}
|
||||
set(v) {
|
||||
prefs.keyboard.subtypes = v.joinToString(SUBTYPE_LIST_STR_DELIMITER)
|
||||
prefs.localization.subtypes = v.joinToString(SUBTYPE_LIST_STR_DELIMITER)
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -134,16 +134,16 @@ class SubtypeManager(
|
||||
*/
|
||||
fun getActiveSubtype(): Subtype? {
|
||||
for (subtype in subtypes) {
|
||||
if (subtype.id == prefs.keyboard.activeSubtypeId) {
|
||||
if (subtype.id == prefs.localization.activeSubtypeId) {
|
||||
return subtype
|
||||
}
|
||||
}
|
||||
val subtypeList = subtypes
|
||||
return if (subtypeList.isNotEmpty()) {
|
||||
prefs.keyboard.activeSubtypeId = subtypeList[0].id
|
||||
prefs.localization.activeSubtypeId = subtypeList[0].id
|
||||
subtypeList[0]
|
||||
} else {
|
||||
prefs.keyboard.activeSubtypeId = -1
|
||||
prefs.localization.activeSubtypeId = Subtype.DEFAULT.id
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -212,11 +212,39 @@ class SubtypeManager(
|
||||
}
|
||||
}
|
||||
subtypes = subtypeList
|
||||
if (subtypeToRemove.id == prefs.keyboard.activeSubtypeId) {
|
||||
if (subtypeToRemove.id == prefs.localization.activeSubtypeId) {
|
||||
getActiveSubtype()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.keyboard.activeSubtypeId = when (newActiveSubtype) {
|
||||
null -> -1
|
||||
prefs.localization.activeSubtypeId = when (newActiveSubtype) {
|
||||
null -> Subtype.DEFAULT.id
|
||||
else -> newActiveSubtype.id
|
||||
}
|
||||
return newActiveSubtype
|
||||
|
||||
@@ -129,7 +129,6 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onDestroy()")
|
||||
|
||||
cancel()
|
||||
florisboard.removeEventListener(this)
|
||||
instance = null
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.media
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
|
||||
class MediaInputView : LinearLayout, FlorisBoard.EventListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
var tabLayout: TabLayout? = null
|
||||
private set
|
||||
var switchToTextInputButton: Button? = null
|
||||
private set
|
||||
var backspaceButton: ImageButton? = null
|
||||
private set
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
florisboard?.addEventListener(this)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
tabLayout = findViewById(R.id.media_input_tabs)
|
||||
switchToTextInputButton = findViewById(R.id.media_input_switch_to_text_input_button)
|
||||
backspaceButton = findViewById(R.id.media_input_backspace_button)
|
||||
onApplyThemeAttributes()
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
tabLayout?.setTabTextColors(prefs.theme.mediaFgColor, prefs.theme.mediaFgColor)
|
||||
tabLayout?.tabIconTint = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
tabLayout?.setSelectedTabIndicatorColor(prefs.theme.colorPrimary)
|
||||
switchToTextInputButton?.setTextColor(prefs.theme.mediaFgColor)
|
||||
backspaceButton?.imageTintList = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val height = florisboard?.inputView?.desiredMediaKeyboardViewHeight ?: 0
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,13 @@ 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
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.util.getColorFromAttr
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
|
||||
/**
|
||||
* View class for managing the rendering and the events of a single emoji keyboard key.
|
||||
@@ -40,10 +41,12 @@ import dev.patrickgold.florisboard.util.getColorFromAttr
|
||||
*/
|
||||
@SuppressLint("ViewConstructor")
|
||||
class EmojiKeyView(
|
||||
private val florisboard: FlorisBoard,
|
||||
private val emojiKeyboardView: EmojiKeyboardView,
|
||||
val data: EmojiKeyData
|
||||
) : androidx.appcompat.widget.AppCompatTextView(florisboard.context) {
|
||||
) : androidx.appcompat.widget.AppCompatTextView(emojiKeyboardView.context),
|
||||
FlorisBoard.EventListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
private var isCancelled: Boolean = false
|
||||
private var osHandler: Handler? = null
|
||||
@@ -55,14 +58,16 @@ class EmojiKeyView(
|
||||
setPadding(0, 0, 0, 0)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.emoji_key_textSize))
|
||||
|
||||
triangleDrawable = resources.getDrawable(
|
||||
R.drawable.triangle_bottom_right, context.theme
|
||||
)
|
||||
triangleDrawable?.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
getColorFromAttr(context, R.attr.emoji_key_fgColor), BlendModeCompat.SRC_ATOP
|
||||
)
|
||||
triangleDrawable = ContextCompat.getDrawable(context, R.drawable.triangle_bottom_right)
|
||||
|
||||
text = data.getCodePointsAsString()
|
||||
|
||||
florisboard?.addEventListener(this)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
onApplyThemeAttributes()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,18 +84,18 @@ class EmojiKeyView(
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
isCancelled = false
|
||||
val delayMillis = florisboard.prefs.looknfeel.longPressDelay
|
||||
val delayMillis = prefs.keyboard.longPressDelay
|
||||
if (osHandler == null) {
|
||||
osHandler = Handler()
|
||||
}
|
||||
osHandler?.postDelayed({
|
||||
(parent.parent as HorizontalScrollView)
|
||||
(parent.parent as ScrollView)
|
||||
.requestDisallowInterceptTouchEvent(true)
|
||||
emojiKeyboardView.isScrollBlocked = true
|
||||
emojiKeyboardView.popupManager.show(this)
|
||||
emojiKeyboardView.popupManager.extend(this)
|
||||
florisboard.keyPressVibrate()
|
||||
florisboard.keyPressSound()
|
||||
florisboard?.keyPressVibrate()
|
||||
florisboard?.keyPressSound()
|
||||
}, delayMillis.toLong())
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
@@ -117,10 +122,10 @@ class EmojiKeyView(
|
||||
if (event.actionMasked != MotionEvent.ACTION_CANCEL &&
|
||||
retData != null && !isCancelled) {
|
||||
if (!emojiKeyboardView.isScrollBlocked) {
|
||||
florisboard.keyPressVibrate()
|
||||
florisboard.keyPressSound()
|
||||
florisboard?.keyPressVibrate()
|
||||
florisboard?.keyPressSound()
|
||||
}
|
||||
florisboard.mediaInputManager.sendEmojiKeyPress(retData)
|
||||
florisboard?.mediaInputManager?.sendEmojiKeyPress(retData)
|
||||
performClick()
|
||||
}
|
||||
if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
|
||||
@@ -131,18 +136,29 @@ class EmojiKeyView(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
triangleDrawable?.setBounds(
|
||||
(measuredWidth * 0.75f).toInt(),
|
||||
(measuredHeight * 0.75f).toInt(),
|
||||
(measuredWidth * 0.85f).toInt(),
|
||||
(measuredHeight * 0.85f).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
triangleDrawable?.colorFilter =
|
||||
BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
prefs.theme.mediaFgColorAlt, BlendModeCompat.SRC_ATOP
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
canvas ?: return
|
||||
|
||||
if (data.popup.isNotEmpty()) {
|
||||
triangleDrawable?.setBounds(
|
||||
(measuredWidth * 0.75f).toInt(),
|
||||
(measuredHeight * 0.75f).toInt(),
|
||||
(measuredWidth * 0.85f).toInt(),
|
||||
(measuredHeight * 0.85f).toInt()
|
||||
)
|
||||
triangleDrawable?.draw(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,20 @@ package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.popup.KeyPopupManager
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
@@ -42,16 +43,18 @@ import java.util.*
|
||||
*
|
||||
* @property florisboard Reference to instance of core class [FlorisBoard].
|
||||
*/
|
||||
class EmojiKeyboardView : LinearLayout {
|
||||
class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
private var activeCategory: EmojiCategory = EmojiCategory.SMILEYS_EMOTION
|
||||
private var emojiViewFlipper: ViewFlipper
|
||||
private val emojiKeyWidth = resources.getDimension(R.dimen.emoji_key_width).toInt()
|
||||
private val emojiKeyHeight = resources.getDimension(R.dimen.emoji_key_height).toInt()
|
||||
private val florisboard: FlorisBoard = FlorisBoard.getInstance()
|
||||
private var layouts: Deferred<EmojiLayoutDataMap>
|
||||
private val mainScope = MainScope()
|
||||
private val uiLayouts = EnumMap<EmojiCategory, HorizontalScrollView>(EmojiCategory::class.java)
|
||||
private val tabLayout: TabLayout
|
||||
private val uiLayouts = EnumMap<EmojiCategory, ScrollView>(EmojiCategory::class.java)
|
||||
|
||||
var isScrollBlocked: Boolean = false
|
||||
var popupManager = KeyPopupManager<EmojiKeyboardView, EmojiKeyView>(this)
|
||||
@@ -62,18 +65,21 @@ class EmojiKeyboardView : LinearLayout {
|
||||
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)
|
||||
|
||||
val tabs =
|
||||
tabLayout =
|
||||
ViewGroup.inflate(context, R.layout.media_input_emoji_tabs, null) as TabLayout
|
||||
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) {
|
||||
setActiveCategory(when (tab?.position) {
|
||||
0 -> EmojiCategory.SMILEYS_EMOTION
|
||||
@@ -92,7 +98,8 @@ class EmojiKeyboardView : LinearLayout {
|
||||
override fun onTabReselected(tab: TabLayout.Tab?) {}
|
||||
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||
})
|
||||
addView(tabs)
|
||||
addView(tabLayout)
|
||||
florisboard?.addEventListener(this)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
@@ -102,11 +109,7 @@ class EmojiKeyboardView : LinearLayout {
|
||||
buildLayout()
|
||||
setActiveCategory(EmojiCategory.SMILEYS_EMOTION)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
mainScope.cancel()
|
||||
onApplyThemeAttributes()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,10 +119,10 @@ class EmojiKeyboardView : LinearLayout {
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,32 +132,52 @@ class EmojiKeyboardView : LinearLayout {
|
||||
* 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 =
|
||||
EmojiKeyView(florisboard, this@EmojiKeyboardView, emojiKeyData)
|
||||
EmojiKeyView(this@EmojiKeyboardView, emojiKeyData)
|
||||
emojiKeyView.layoutParams = FlexboxLayout.LayoutParams(
|
||||
emojiKeyWidth, emojiKeyHeight
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,4 +215,9 @@ class EmojiKeyboardView : LinearLayout {
|
||||
))
|
||||
isScrollBlocked = true
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
tabLayout.tabIconTint = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
tabLayout.setSelectedTabIndicatorColor(prefs.theme.colorAccent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,22 @@ package dev.patrickgold.florisboard.ime.popup
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.ContextCompat.getDrawable
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
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
|
||||
) {
|
||||
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
var iconDrawable: Drawable? = null
|
||||
|
||||
init {
|
||||
@@ -40,15 +42,16 @@ class KeyPopupExtendedSingleView(
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, when {
|
||||
isActive -> prefs.theme.keyPopupBgColorActive
|
||||
else -> Color.TRANSPARENT
|
||||
})
|
||||
setTextColor(prefs.theme.keyPopupFgColor)
|
||||
|
||||
super.onDraw(canvas)
|
||||
|
||||
canvas ?: return
|
||||
|
||||
setBackgroundTintColor(this, when {
|
||||
isActive -> R.attr.key_popup_extended_bgColorActive
|
||||
else -> R.attr.key_popup_extended_bgColor
|
||||
})
|
||||
|
||||
val drawable = iconDrawable
|
||||
val drawablePadding = (0.2f * measuredHeight).toInt()
|
||||
if (drawable != null) {
|
||||
@@ -65,7 +68,7 @@ class KeyPopupExtendedSingleView(
|
||||
measuredWidth - marginH - drawablePadding,
|
||||
measuredHeight - marginV - drawablePadding)
|
||||
drawable.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
getColorFromAttr(context, R.attr.key_popup_fgColor),
|
||||
prefs.theme.keyPopupFgColor,
|
||||
BlendModeCompat.SRC_ATOP
|
||||
)
|
||||
drawable.draw(canvas)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.popup
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
|
||||
class KeyPopupExtendedView : FlexboxLayout {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, prefs.theme.keyPopupBgColor)
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -34,7 +35,6 @@ import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyView
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
import dev.patrickgold.florisboard.util.setTextTintColor
|
||||
|
||||
/**
|
||||
* Manages the creation and dismissal of key popups as well as the checks if the pointer moved
|
||||
@@ -43,7 +43,6 @@ import dev.patrickgold.florisboard.util.setTextTintColor
|
||||
* @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
|
||||
@@ -102,18 +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
|
||||
setTextTintColor(
|
||||
textView,
|
||||
R.attr.key_popup_fgColor
|
||||
)
|
||||
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
|
||||
@@ -134,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) {
|
||||
@@ -216,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) {
|
||||
@@ -261,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
|
||||
}
|
||||
@@ -320,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) {
|
||||
@@ -446,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
|
||||
@@ -476,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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.popup
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
|
||||
class KeyPopupView : LinearLayout {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private lateinit var text: TextView
|
||||
private lateinit var threedots: ImageView
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
text = findViewById(R.id.key_popup_text)
|
||||
threedots = findViewById(R.id.key_popup_threedots)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, prefs.theme.keyPopupBgColor)
|
||||
text.setTextColor(prefs.theme.keyPopupFgColor)
|
||||
setImageTintColor2(threedots, prefs.theme.keyPopupFgColor)
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,8 @@ import dev.patrickgold.florisboard.R
|
||||
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.editing.EditingKeyboardView
|
||||
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()
|
||||
@@ -137,8 +134,6 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
|
||||
private suspend fun addKeyboardView(mode: KeyboardMode) {
|
||||
val keyboardView = KeyboardView(florisboard.context)
|
||||
keyboardView.florisboard = florisboard
|
||||
keyboardView.prefs = florisboard.prefs
|
||||
keyboardView.computedLayout = layoutManager.fetchComputedLayoutAsync(mode, florisboard.activeSubtype).await()
|
||||
keyboardViews[mode] = keyboardView
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -180,7 +175,6 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
osHandler.removeCallbacksAndMessages(null)
|
||||
layoutManager.onDestroy()
|
||||
smartbarManager.onDestroy()
|
||||
florisboard.removeEventListener(this)
|
||||
instance = null
|
||||
}
|
||||
|
||||
@@ -306,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()
|
||||
@@ -328,7 +323,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
resetComposingText()
|
||||
}
|
||||
smartbarManager.generateCandidatesFromComposing(composingText)
|
||||
}
|
||||
//}
|
||||
if (!isNewSelectionInBoundsOfOld) {
|
||||
isManualSelectionMode = false
|
||||
isManualSelectionModeLeft = false
|
||||
@@ -423,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].
|
||||
*
|
||||
@@ -478,30 +489,48 @@ 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.
|
||||
*/
|
||||
private fun handleEnter() {
|
||||
val ic = florisboard.currentInputConnection
|
||||
ic?.beginBatchEdit()
|
||||
resetComposingText()
|
||||
val action = florisboard.currentInputEditorInfo?.imeOptions ?: 0
|
||||
val actionMasked = action and EditorInfo.IME_MASK_ACTION
|
||||
if (action and EditorInfo.IME_FLAG_NO_ENTER_ACTION > 0) {
|
||||
sendSystemKeyEvent(ic, KeyEvent.KEYCODE_ENTER)
|
||||
} else {
|
||||
when (action and EditorInfo.IME_MASK_ACTION) {
|
||||
when (actionMasked) {
|
||||
EditorInfo.IME_ACTION_DONE,
|
||||
EditorInfo.IME_ACTION_GO,
|
||||
EditorInfo.IME_ACTION_NEXT,
|
||||
EditorInfo.IME_ACTION_PREVIOUS,
|
||||
EditorInfo.IME_ACTION_SEARCH,
|
||||
EditorInfo.IME_ACTION_SEND -> {
|
||||
ic?.performEditorAction(action)
|
||||
ic?.performEditorAction(actionMasked)
|
||||
}
|
||||
else -> sendSystemKeyEvent(ic, KeyEvent.KEYCODE_ENTER)
|
||||
}
|
||||
}
|
||||
ic?.endBatchEdit()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.editing
|
||||
package dev.patrickgold.florisboard.ime.text.editing
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
@@ -29,9 +29,9 @@ import android.widget.Button
|
||||
import androidx.appcompat.widget.AppCompatImageButton
|
||||
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.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.util.getColorFromAttr
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -39,6 +39,7 @@ import java.util.*
|
||||
*/
|
||||
class EditingKeyView : AppCompatImageButton {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private val data: KeyData
|
||||
private var isKeyPressed: Boolean = false
|
||||
private var osTimer: Timer? = null
|
||||
@@ -133,20 +134,20 @@ class EditingKeyView : AppCompatImageButton {
|
||||
|
||||
canvas ?: return
|
||||
|
||||
imageTintList = ColorStateList.valueOf(getColorFromAttr(context, when {
|
||||
isEnabled -> R.attr.key_fgColor
|
||||
else -> android.R.attr.colorButtonNormal
|
||||
}))
|
||||
imageTintList = ColorStateList.valueOf(when {
|
||||
isEnabled -> prefs.theme.smartbarFgColor
|
||||
else -> prefs.theme.smartbarFgColorAlt
|
||||
})
|
||||
|
||||
// Draw label
|
||||
val label = label
|
||||
if (label != null) {
|
||||
labelPaint.color = if (isHighlighted && isEnabled) {
|
||||
getColorFromAttr(context, R.attr.colorPrimary)
|
||||
prefs.theme.colorPrimary
|
||||
} else if (!isEnabled) {
|
||||
getColorFromAttr(context, android.R.attr.colorButtonNormal)
|
||||
prefs.theme.smartbarFgColorAlt
|
||||
} else {
|
||||
getColorFromAttr(context, R.attr.key_fgColor)
|
||||
prefs.theme.smartbarFgColor
|
||||
}
|
||||
val isPortrait =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
@@ -14,21 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.editing
|
||||
package dev.patrickgold.florisboard.ime.text.editing
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.inputmethod.CursorAnchorInfo
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor2
|
||||
|
||||
/**
|
||||
* View class for updating the key views depending on the current selection and clipboard state.
|
||||
*/
|
||||
class EditingKeyboardView : ConstraintLayout, FlorisBoard.EventListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
private var arrowUpKey: EditingKeyView? = null
|
||||
private var arrowDownKey: EditingKeyView? = null
|
||||
@@ -56,12 +60,6 @@ class EditingKeyboardView : ConstraintLayout, FlorisBoard.EventListener {
|
||||
pasteKey = findViewById(R.id.clipboard_paste)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
|
||||
florisboard?.removeEventListener(this)
|
||||
}
|
||||
|
||||
override fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {
|
||||
val isSelectionActive = florisboard?.textInputManager?.isTextSelected ?: false
|
||||
val isSelectionMode = florisboard?.textInputManager?.isManualSelectionMode ?: false
|
||||
@@ -79,4 +77,30 @@ class EditingKeyboardView : ConstraintLayout, FlorisBoard.EventListener {
|
||||
copyKey?.isEnabled = isSelectionActive
|
||||
pasteKey?.isEnabled = florisboard?.clipboardManager?.hasPrimaryClip() ?: false
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
|
||||
val height = when (heightMode) {
|
||||
MeasureSpec.EXACTLY -> {
|
||||
// Must be this size
|
||||
heightSize
|
||||
}
|
||||
MeasureSpec.AT_MOST -> {
|
||||
// Can't be bigger than...
|
||||
(florisboard?.inputView?.desiredTextKeyboardViewHeight ?: 0).coerceAtMost(heightSize)
|
||||
}
|
||||
else -> {
|
||||
// Be whatever you want
|
||||
florisboard?.inputView?.desiredTextKeyboardViewHeight ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
setBackgroundTintColor2(this, prefs.theme.smartbarBgColor)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -33,10 +33,11 @@ import androidx.core.view.children
|
||||
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.getColorFromAttr
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor2
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
@@ -60,6 +61,7 @@ class KeyView(
|
||||
}
|
||||
private var osHandler: Handler? = null
|
||||
private var osTimer: Timer? = null
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private var shouldBlockNextKeyCode: Boolean = false
|
||||
|
||||
private var drawable: Drawable? = null
|
||||
@@ -75,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 {
|
||||
@@ -123,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()
|
||||
}
|
||||
|
||||
@@ -172,10 +202,19 @@ 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 -> {
|
||||
florisboard?.prefs?.popup?.let {
|
||||
if (it.enabled){
|
||||
shouldBlockNextKeyCode = false
|
||||
florisboard?.prefs?.keyboard?.let {
|
||||
if (it.popupEnabled){
|
||||
keyboardView.popupManager.show(this)
|
||||
}
|
||||
}
|
||||
@@ -194,12 +233,12 @@ class KeyView(
|
||||
}
|
||||
}, 500, 50)
|
||||
}
|
||||
val delayMillis = keyboardView.prefs.looknfeel.longPressDelay
|
||||
val delayMillis = prefs.keyboard.longPressDelay
|
||||
if (osHandler == null) {
|
||||
osHandler = Handler()
|
||||
}
|
||||
osHandler?.postDelayed({
|
||||
if (data.popup.isNotEmpty()) {
|
||||
if (dataPopupWithHint.isNotEmpty()) {
|
||||
keyboardView.popupManager.extend(this)
|
||||
}
|
||||
if (data.code == KeyCode.SPACE) {
|
||||
@@ -252,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():
|
||||
@@ -337,20 +404,31 @@ class KeyView(
|
||||
* Updates the background depending on [isKeyPressed] and [data].
|
||||
*/
|
||||
private fun updateKeyPressedBackground() {
|
||||
if (data.code == KeyCode.ENTER) {
|
||||
setBackgroundTintColor(
|
||||
this, when {
|
||||
isKeyPressed -> R.attr.colorPrimaryDark
|
||||
else -> R.attr.colorPrimary
|
||||
}
|
||||
)
|
||||
} else {
|
||||
setBackgroundTintColor(
|
||||
this, when {
|
||||
isKeyPressed -> R.attr.key_bgColorPressed
|
||||
else -> R.attr.key_bgColor
|
||||
}
|
||||
)
|
||||
when (data.code) {
|
||||
KeyCode.ENTER -> {
|
||||
setBackgroundTintColor2(
|
||||
this, when {
|
||||
isKeyPressed -> prefs.theme.keyEnterBgColorPressed
|
||||
else -> prefs.theme.keyEnterBgColor
|
||||
}
|
||||
)
|
||||
}
|
||||
KeyCode.SHIFT -> {
|
||||
setBackgroundTintColor2(
|
||||
this, when {
|
||||
isKeyPressed -> prefs.theme.keyShiftBgColorPressed
|
||||
else -> prefs.theme.keyShiftBgColor
|
||||
}
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
setBackgroundTintColor2(
|
||||
this, when {
|
||||
isKeyPressed -> prefs.theme.keyBgColorPressed
|
||||
else -> prefs.theme.keyBgColor
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,11 +511,20 @@ 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 -> {
|
||||
drawable = getDrawable(context, R.drawable.ic_backspace)
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyFgColor
|
||||
}
|
||||
KeyCode.ENTER -> {
|
||||
val action = florisboard?.currentInputEditorInfo?.imeOptions ?: 0
|
||||
@@ -451,29 +538,29 @@ class KeyView(
|
||||
EditorInfo.IME_ACTION_SEND -> R.drawable.ic_send
|
||||
else -> R.drawable.ic_arrow_right_alt
|
||||
})
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_enter_fgColor)
|
||||
drawableColor = prefs.theme.keyEnterFgColor
|
||||
if (action and EditorInfo.IME_FLAG_NO_ENTER_ACTION > 0) {
|
||||
drawable = getDrawable(context, R.drawable.ic_keyboard_return)
|
||||
}
|
||||
}
|
||||
KeyCode.LANGUAGE_SWITCH -> {
|
||||
drawable = getDrawable(context, R.drawable.ic_language)
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyFgColor
|
||||
}
|
||||
KeyCode.PHONE_PAUSE -> label = resources.getString(R.string.key__phone_pause)
|
||||
KeyCode.PHONE_WAIT -> label = resources.getString(R.string.key__phone_wait)
|
||||
KeyCode.SHIFT -> {
|
||||
drawable = getDrawable(context, when {
|
||||
florisboard?.textInputManager?.caps ?: false && florisboard?.textInputManager?.capsLock ?: false -> {
|
||||
drawableColor = getColorFromAttr(context, R.attr.colorAccent)
|
||||
drawableColor = prefs.theme.keyShiftFgColorCapsLock
|
||||
R.drawable.ic_keyboard_capslock
|
||||
}
|
||||
florisboard?.textInputManager?.caps ?: false && !(florisboard?.textInputManager?.capsLock ?: false) -> {
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyShiftFgColor
|
||||
R.drawable.ic_keyboard_capslock
|
||||
}
|
||||
else -> {
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyShiftFgColor
|
||||
R.drawable.ic_keyboard_arrow_up
|
||||
}
|
||||
})
|
||||
@@ -485,7 +572,7 @@ class KeyView(
|
||||
KeyboardMode.PHONE,
|
||||
KeyboardMode.PHONE2 -> {
|
||||
drawable = getDrawable(context, R.drawable.ic_space_bar)
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyFgColor
|
||||
}
|
||||
KeyboardMode.CHARACTERS -> {
|
||||
label = florisboard?.activeSubtype?.locale?.displayName
|
||||
@@ -495,7 +582,7 @@ class KeyView(
|
||||
}
|
||||
KeyCode.SWITCH_TO_MEDIA_CONTEXT -> {
|
||||
drawable = getDrawable(context, R.drawable.ic_sentiment_satisfied)
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyFgColor
|
||||
}
|
||||
KeyCode.SWITCH_TO_TEXT_CONTEXT,
|
||||
KeyCode.VIEW_CHARACTERS -> {
|
||||
@@ -526,6 +613,9 @@ class KeyView(
|
||||
}
|
||||
}
|
||||
|
||||
val isPortrait =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
||||
// Draw drawable
|
||||
val drawable = drawable
|
||||
if (drawable != null) {
|
||||
@@ -557,14 +647,12 @@ class KeyView(
|
||||
} else {
|
||||
labelPaint.textSize = resources.getDimension(R.dimen.key_textSize)
|
||||
}
|
||||
labelPaint.color = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
labelPaint.alpha = if (keyboardView.computedLayout?.mode == KeyboardMode.CHARACTERS &&
|
||||
data.code == KeyCode.SPACE) { 120 } else { 255 }
|
||||
val isPortrait =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
if (keyboardView.prefs.looknfeel.oneHandedMode != "off" && isPortrait) {
|
||||
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")) {
|
||||
@@ -576,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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,9 +18,6 @@ package dev.patrickgold.florisboard.ime.text.keyboard
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
@@ -32,25 +29,27 @@ 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.util.getColorFromAttr
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeGesture
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Manages the layout of the keyboard, key measurement, key selection and all touch events.
|
||||
* Supports multi touch events.
|
||||
*
|
||||
* TODO: Implement swipe gesture support
|
||||
* Supports multi touch events. Note that the keyboard's background is transparent. The 'real'
|
||||
* 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.
|
||||
*
|
||||
* @property florisboard Reference to instance of core class [FlorisBoard].
|
||||
*/
|
||||
class KeyboardView : LinearLayout {
|
||||
class KeyboardView : LinearLayout, FlorisBoard.EventListener, SwipeGesture.Listener {
|
||||
private var activeKeyView: KeyView? = null
|
||||
private var activePointerId: Int? = null
|
||||
private var activeX: Float = 0.0f
|
||||
private var activeY: Float = 0.0f
|
||||
|
||||
private var colorDrawable: ColorDrawable
|
||||
var computedLayout: ComputedLayoutData? = null
|
||||
set(v) {
|
||||
field = v
|
||||
@@ -58,21 +57,23 @@ class KeyboardView : LinearLayout {
|
||||
}
|
||||
var desiredKeyWidth: Int = resources.getDimension(R.dimen.key_width).toInt()
|
||||
var desiredKeyHeight: Int = resources.getDimension(R.dimen.key_height).toInt()
|
||||
var florisboard: FlorisBoard? = null
|
||||
var florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private var initialKeyCode: Int = 0
|
||||
var isPreviewMode: Boolean = false
|
||||
var popupManager = KeyPopupManager<KeyboardView, KeyView>(this)
|
||||
lateinit var prefs: PrefHelper
|
||||
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)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
colorDrawable = ColorDrawable(getColorFromAttr(context, R.attr.keyboard_bgColor))
|
||||
background = colorDrawable
|
||||
orientation = VERTICAL
|
||||
layoutParams = layoutParams ?: FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
florisboard?.addEventListener(this)
|
||||
onWindowShown()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,6 +108,13 @@ class KeyboardView : LinearLayout {
|
||||
popupManager.dismissAllPopups()
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
swipeGestureDetector.apply {
|
||||
distanceThreshold = prefs.gestures.swipeDistanceThreshold
|
||||
velocityThreshold = prefs.gestures.swipeVelocityThreshold
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch all events which are designated for child views.
|
||||
*/
|
||||
@@ -124,6 +132,12 @@ class KeyboardView : LinearLayout {
|
||||
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) {
|
||||
@@ -134,6 +148,7 @@ class KeyboardView : LinearLayout {
|
||||
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
|
||||
@@ -142,6 +157,7 @@ class KeyboardView : LinearLayout {
|
||||
activeX = event.getX(pointerIndex)
|
||||
activeY = event.getY(pointerIndex)
|
||||
searchForActiveKeyView()
|
||||
initialKeyCode = activeKeyView?.data?.code ?: 0
|
||||
sendFlorisTouchEvent(eventFloris, MotionEvent.ACTION_DOWN)
|
||||
}
|
||||
}
|
||||
@@ -199,6 +215,45 @@ class KeyboardView : LinearLayout {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
@@ -237,32 +292,21 @@ class KeyboardView : LinearLayout {
|
||||
val keyMarginH = resources.getDimension((R.dimen.key_marginH)).toInt()
|
||||
desiredKeyWidth = (widthSize / 10) - (2 * keyMarginH)
|
||||
|
||||
val factor = prefs.looknfeel.heightFactor
|
||||
val keyHeightFactor = when (resources.configuration.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> 0.85f
|
||||
else -> if (prefs.looknfeel.oneHandedMode == "start" ||
|
||||
prefs.looknfeel.oneHandedMode == "end") {
|
||||
0.9f
|
||||
} else {
|
||||
1.0f
|
||||
}
|
||||
} * when (factor) {
|
||||
"extra_short" -> 0.85f
|
||||
"short" -> 0.90f
|
||||
"mid_short" -> 0.95f
|
||||
"normal" -> 1.00f
|
||||
"mid_tall" -> 1.05f
|
||||
"tall" -> 1.10f
|
||||
"extra_tall" -> 1.15f
|
||||
else -> 1.00f
|
||||
} * when (isPreviewMode) {
|
||||
val keyMarginV = resources.getDimension((R.dimen.key_marginV)).toInt()
|
||||
val keyHeightFactor = when (isPreviewMode) {
|
||||
true -> 0.90f
|
||||
else -> 1.00f
|
||||
}
|
||||
desiredKeyHeight = (resources.getDimension(R.dimen.key_height) * keyHeightFactor).toInt()
|
||||
florisboard?.textInputManager?.smartbarManager?.smartbarView?.setHeightFactor(keyHeightFactor)
|
||||
val desiredHeight = keyHeightFactor * (florisboard?.inputView?.desiredTextKeyboardViewHeight ?: resources.getDimension(R.dimen.textKeyboardView_baseHeight).toInt())
|
||||
desiredKeyHeight = (desiredHeight / 4 - 2 * keyMarginV).roundToInt()
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(desiredHeight.roundToInt(), MeasureSpec.EXACTLY))
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
if (isPreviewMode) {
|
||||
setBackgroundColor(prefs.theme.keyboardBgColor)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,10 +353,4 @@ class KeyboardView : LinearLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
colorDrawable.color = getColorFromAttr(context, R.attr.keyboard_bgColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -17,13 +17,17 @@
|
||||
package dev.patrickgold.florisboard.ime.text.smartbar
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor2
|
||||
|
||||
/**
|
||||
* Basically the same as an ImageButton.
|
||||
* @see [onMeasure] why this view class exists.
|
||||
*/
|
||||
class SmartbarQuickActionButton : androidx.appcompat.widget.AppCompatImageButton {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
@@ -37,4 +41,10 @@ class SmartbarQuickActionButton : androidx.appcompat.widget.AppCompatImageButton
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(heightMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
setBackgroundTintColor2(this, prefs.theme.smartbarButtonBgColor)
|
||||
setColorFilter(prefs.theme.smartbarButtonFgColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package dev.patrickgold.florisboard.ime.text.smartbar
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
@@ -25,8 +26,13 @@ import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.core.view.children
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.setImageTintColor2
|
||||
import kotlinx.android.synthetic.main.florisboard.view.*
|
||||
|
||||
/**
|
||||
* View class which keeps the references to important children and informs [SmartbarManager] that
|
||||
@@ -34,7 +40,8 @@ import dev.patrickgold.florisboard.R
|
||||
* a theme change).
|
||||
*/
|
||||
class SmartbarView : LinearLayout {
|
||||
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private val smartbarManager = SmartbarManager.getInstance()
|
||||
|
||||
private var variants: MutableList<ViewGroup> = mutableListOf()
|
||||
@@ -100,12 +107,58 @@ class SmartbarView : LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies the default smartbar height with the given [factor] and sets it.
|
||||
*/
|
||||
fun setHeightFactor(factor: Float) {
|
||||
val baseSize = resources.getDimension(R.dimen.smartbar_height)
|
||||
val size = (baseSize * factor).toInt()
|
||||
layoutParams?.height = size
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
|
||||
val height = when (heightMode) {
|
||||
MeasureSpec.EXACTLY -> {
|
||||
// Must be this size
|
||||
heightSize
|
||||
}
|
||||
MeasureSpec.AT_MOST -> {
|
||||
// Can't be bigger than...
|
||||
(florisboard?.inputView?.desiredSmartbarHeight ?: 0).coerceAtMost(heightSize)
|
||||
}
|
||||
else -> {
|
||||
// Be whatever you want
|
||||
florisboard?.inputView?.desiredSmartbarHeight ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
setBackgroundColor(prefs.theme.smartbarBgColor)
|
||||
for (container in containers) {
|
||||
when (container.id) {
|
||||
R.id.number_row -> {
|
||||
for (button in container.children) {
|
||||
if (button is Button) {
|
||||
button.setTextColor(prefs.theme.smartbarFgColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.clipboard_cursor_row -> {
|
||||
for (button in container.children) {
|
||||
if (button is ImageButton) {
|
||||
if (button.isEnabled) {
|
||||
setImageTintColor2(button, prefs.theme.smartbarFgColor)
|
||||
} else {
|
||||
setImageTintColor2(button, prefs.theme.smartbarFgColorAlt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.candidates -> {
|
||||
for (view in container.children) {
|
||||
if (view is Button) {
|
||||
view.setTextColor(prefs.theme.smartbarFgColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
310
app/src/main/java/dev/patrickgold/florisboard/ime/theme/Theme.kt
Normal file
310
app/src/main/java/dev/patrickgold/florisboard/ime/theme/Theme.kt
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 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.theme
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
|
||||
/**
|
||||
* Data class which holds a parsed theme json file. Used for loading a theme
|
||||
* preset in Settings.
|
||||
* Note: this implementation is generic and allows for any group/attr names.
|
||||
* FlorisBoard itself expects certain groups and attrs to be able to
|
||||
* color the controls accordingly. See 'ime/themes/floris_day.json'
|
||||
* for a good example of which attributes FlorisBoard needs!
|
||||
*
|
||||
* @property name A unique id/name for this theme. Must only contain certain
|
||||
* characters: upper/lower case letters, numbers (not at the beginning!) or
|
||||
* an underline (_).
|
||||
* @property displayName The name of this theme when shown to the user. Can
|
||||
* contain any valid Unicode character.
|
||||
* @property author The name of the author of this theme. Should be your
|
||||
* username on GitHub/GitLab/BitBucket/... or your full name.
|
||||
* @property isNightTheme If this theme is meant for display at day (false)
|
||||
* or night (true). This property is only used to auto-assign this theme to
|
||||
* either the day or night theme list in Settings, which is used when the
|
||||
* user wants to auto-set his theme based on the current time.
|
||||
* @property rawAttrs Map which holds the raw attributes of this theme. Note
|
||||
* that the name of this property is 'attributes' within the json file!
|
||||
* Attributes are always grouped together. This ensures a better structure
|
||||
* and easier storage. The group- as well as the attr-name has the same
|
||||
* limitations as the theme [name].
|
||||
* Attribute values can be of different format:
|
||||
* 1. A color
|
||||
* Either #RRGGBB or #AARRGGBB (case-insensitive) -> e.g. #A034FF23
|
||||
* 2. A static word
|
||||
* - transparent (=0x00000000)
|
||||
* - true (=0x1)
|
||||
* - false (=0x0)
|
||||
* 3. A reference to another attribute within the SAME theme, as follows:
|
||||
* @group/attrName -> e.g. @window/textColor
|
||||
* Note that referencing attributes has its limitations:
|
||||
* a. Recursive references will cause an exception.
|
||||
* b. Referencing an previously defined attribute is fine.
|
||||
* c. Referencing an attribute not-yet defined is also ok, as long as
|
||||
* the reference can be resolved at the next iteration.
|
||||
* d. If the next iteration cannot resolve a value, an exception is
|
||||
* thrown.
|
||||
* 4. If the value is of any other format, an exception will be thrown.
|
||||
*
|
||||
* @throws IllegalArgumentException either at an invalid value or when a
|
||||
* reference cannot be resolved.
|
||||
*/
|
||||
data class Theme(
|
||||
val name: String,
|
||||
val displayName: String,
|
||||
val author: String,
|
||||
val isNightTheme: Boolean = false,
|
||||
@Json(name = "attributes")
|
||||
private val rawAttrs: Map<String, Map<String, String>>
|
||||
) {
|
||||
/**
|
||||
* Holds the parsed attributes after init.
|
||||
*/
|
||||
val parsedAttrs: MutableMap<String, MutableMap<String, Int>> = mutableMapOf()
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Loads a theme from the specified [path].
|
||||
*
|
||||
* @param context A reference to the current [Context]. Used to request
|
||||
* asset file.
|
||||
* @param path The path to the json theme file in the asset folder.
|
||||
* @returns A parsed [Theme] or null. A null value may indicate that
|
||||
* the file does not exist or that an error during the reading
|
||||
* of the file occurred.
|
||||
*/
|
||||
fun fromJsonFile(context: Context, path: String): Theme? {
|
||||
val rawJsonData: String = try {
|
||||
context.assets.open(path).bufferedReader().use { it.readText() }
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
} ?: return null
|
||||
return fromJsonString(rawJsonData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a theme from the given [rawData].
|
||||
*
|
||||
* @param rawData The raw json theme file as a string.
|
||||
* @returns A parsed [Theme] or null. A null value may indicate that an error
|
||||
* during the reading of the [rawData] occurred.
|
||||
*/
|
||||
fun fromJsonString(rawData: String): Theme? {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val layoutAdapter = moshi.adapter(Theme::class.java)
|
||||
return layoutAdapter.fromJson(rawData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a given [theme] to the [prefs]. The default color values are based off the
|
||||
* Floris Day theme and are not intended to be modified. Instead, themes should be defined
|
||||
* in assets/ime/theme/<theme_id>.json
|
||||
*
|
||||
* @param theme The theme data.
|
||||
* @param prefs The preference object to write the theme to.
|
||||
*/
|
||||
fun writeThemeToPrefs(prefs: PrefHelper, theme: Theme) {
|
||||
// Internal prefs part I
|
||||
prefs.internal.themeCurrentBasedOn = theme.name
|
||||
prefs.internal.themeCurrentIsNight = theme.isNightTheme
|
||||
|
||||
// Theme attributes
|
||||
prefs.theme.colorPrimary = theme.getAttr("window/colorPrimary", "#4CAF50")
|
||||
prefs.theme.colorPrimaryDark = theme.getAttr("window/colorPrimaryDark", "#388E3C")
|
||||
prefs.theme.colorAccent = theme.getAttr("window/colorAccent", "#FF9800")
|
||||
prefs.theme.navBarColor = theme.getAttr("window/navigationBarColor", "#E0E0E0")
|
||||
prefs.theme.navBarIsLight = (theme.getAttrOrNull("window/navigationBarLight") ?: 0) > 0
|
||||
|
||||
prefs.theme.keyboardBgColor = theme.getAttr("keyboard/bgColor", "#E0E0E0")
|
||||
|
||||
prefs.theme.keyBgColor = theme.getAttr("key/bgColor", "#FFFFFF")
|
||||
prefs.theme.keyBgColorPressed = theme.getAttr("key/bgColorPressed", "#F5F5F5")
|
||||
prefs.theme.keyFgColor = theme.getAttr("key/fgColor", "#000000")
|
||||
|
||||
prefs.theme.keyEnterBgColor = theme.getAttr("keyEnter/bgColor", "#4CAF50")
|
||||
prefs.theme.keyEnterBgColorPressed = theme.getAttr("keyEnter/bgColorPressed", "#388E3C")
|
||||
prefs.theme.keyEnterFgColor = theme.getAttr("keyEnter/fgColor", "#FFFFFF")
|
||||
|
||||
prefs.theme.keyPopupBgColor = theme.getAttr("keyPopup/bgColor", "#EEEEEE")
|
||||
prefs.theme.keyPopupBgColorActive = theme.getAttr("keyPopup/bgColorActive", "#BDBDBD")
|
||||
prefs.theme.keyPopupFgColor = theme.getAttr("keyPopup/fgColor", "#000000")
|
||||
|
||||
prefs.theme.keyShiftBgColor = theme.getAttr("keyShift/bgColor", "#FFFFFF")
|
||||
prefs.theme.keyShiftBgColorPressed = theme.getAttr("keyShift/bgColorPressed", "#F5F5F5")
|
||||
prefs.theme.keyShiftFgColor = theme.getAttr("keyShift/fgColor", "#000000")
|
||||
prefs.theme.keyShiftFgColorCapsLock = theme.getAttr("keyShift/fgColorCapsLock", "#FF9800")
|
||||
|
||||
prefs.theme.mediaFgColor = theme.getAttr("media/fgColor", "#000000")
|
||||
prefs.theme.mediaFgColorAlt = theme.getAttr("media/fgColorAlt", "#757575")
|
||||
|
||||
prefs.theme.oneHandedBgColor = theme.getAttr("oneHanded/bgColor", "#E8F5E9")
|
||||
|
||||
prefs.theme.oneHandedButtonFgColor = theme.getAttr("oneHandedButton/fgColor", "#424242")
|
||||
|
||||
prefs.theme.smartbarBgColor = theme.getAttr("smartbar/bgColor", "#E0E0E0")
|
||||
prefs.theme.smartbarFgColor = theme.getAttr("smartbar/fgColor", "#000000")
|
||||
prefs.theme.smartbarFgColorAlt = theme.getAttr("smartbar/fgColorAlt", "#4A000000")
|
||||
|
||||
prefs.theme.smartbarButtonBgColor = theme.getAttr("smartbarButton/bgColor", "#FFFFFF")
|
||||
prefs.theme.smartbarButtonFgColor = theme.getAttr("smartbarButton/fgColor", "#000000")
|
||||
|
||||
// Internal prefs part II (must be written at the end!!)
|
||||
prefs.internal.themeCurrentIsModified = false
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val listOfAttrsToReevaluate = mutableListOf<Triple<String, String, String>>()
|
||||
for (group in rawAttrs) {
|
||||
val groupMap = mutableMapOf<String, Int>()
|
||||
parsedAttrs[group.key] = groupMap
|
||||
for (attr in group.value) {
|
||||
val colorRegex = """[#]([0-9a-fA-F]{8}|[0-9a-fA-F]{6})""".toRegex()
|
||||
val refRegex = """[@]([a-zA-Z_][a-zA-Z0-9_]*)[/]([a-zA-Z_][a-zA-Z0-9_]*)""".toRegex()
|
||||
when {
|
||||
attr.value.matches(colorRegex) -> {
|
||||
groupMap[attr.key] = Color.parseColor(attr.value)
|
||||
}
|
||||
attr.value == "transparent" -> {
|
||||
groupMap[attr.key] = Color.TRANSPARENT
|
||||
}
|
||||
attr.value == "true" -> {
|
||||
groupMap[attr.key] = 0x1
|
||||
}
|
||||
attr.value == "false" -> {
|
||||
groupMap[attr.key] = 0x0
|
||||
}
|
||||
attr.value.matches(refRegex) -> {
|
||||
val attrValue = getAttrOrNull(attr.value.substring(1))
|
||||
if (attrValue != null) {
|
||||
groupMap[attr.key] = attrValue
|
||||
} else {
|
||||
listOfAttrsToReevaluate.add(Triple(group.key, attr.key, attr.value))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("The specified attr '${attr.key}' = '${attr.value}' is not valid!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (attrToReevaluate in listOfAttrsToReevaluate) {
|
||||
val attrValue = getAttrOrNull(attrToReevaluate.third.substring(1))
|
||||
if (attrValue != null) {
|
||||
parsedAttrs[attrToReevaluate.first]?.put(attrToReevaluate.second, attrValue)
|
||||
} else {
|
||||
throw IllegalArgumentException("The specified attr '${attrToReevaluate.second}' = '${attrToReevaluate.third}' is not valid!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAttr(key: String, defaultColor: String): Int {
|
||||
return getAttrOrNull(key) ?: Color.parseColor(defaultColor)
|
||||
}
|
||||
fun getAttr(group: String, attr: String, defaultColor: String): Int {
|
||||
return getAttrOrNull(group, attr) ?: Color.parseColor(defaultColor)
|
||||
}
|
||||
|
||||
fun getAttrOrNull(key: String): Int? {
|
||||
val regex = """([a-zA-Z_][a-zA-Z0-9_]*)[/]([a-zA-Z_][a-zA-Z0-9_]*)""".toRegex()
|
||||
return if (key.matches(regex)) {
|
||||
val split = key.split("/")
|
||||
getAttrOrNull(split[0], split[1])
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
fun getAttrOrNull(group: String, attr: String): Int? {
|
||||
return parsedAttrs[group]?.get(attr)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class which is used to quickly parse only the relevant meta data to
|
||||
* display a theme in a selection list.
|
||||
*
|
||||
* @see [Theme] for details regarding the attributes and the theme structure.
|
||||
*/
|
||||
data class ThemeMetaOnly(
|
||||
val name: String,
|
||||
val displayName: String,
|
||||
val author: String,
|
||||
val isNightTheme: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Loads the theme meta data from the specified [path].
|
||||
*
|
||||
* @param context A reference to the current [Context]. Used to request
|
||||
* asset file.
|
||||
* @param path The path to the json theme file in the asset folder.
|
||||
* @returns [ThemeMetaOnly] or null. A null value may indicate that
|
||||
* the file does not exist or that an error during the reading
|
||||
* of the file occurred.
|
||||
*/
|
||||
fun loadFromJsonFile(context: Context, path: String): ThemeMetaOnly? {
|
||||
val rawJsonData: String = try {
|
||||
context.assets.open(path).bufferedReader().use { it.readText() }
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
} ?: return null
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val layoutAdapter = moshi.adapter(ThemeMetaOnly::class.java)
|
||||
return layoutAdapter.fromJson(rawJsonData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all theme meta data from the specified [path].
|
||||
*
|
||||
* @param context A reference to the current [Context]. Used to request
|
||||
* asset file.
|
||||
* @param path The path to the dir in the asset folder.
|
||||
* @returns [ThemeMetaOnly] or null. A null value may indicate that
|
||||
* the file does not exist or that an error during the reading
|
||||
* of the file occurred.
|
||||
*/
|
||||
fun loadAllFromDir(context: Context, path: String): List<ThemeMetaOnly> {
|
||||
val ret = mutableListOf<ThemeMetaOnly>()
|
||||
try {
|
||||
val list = context.assets.list(path)
|
||||
if (list != null && list.isNotEmpty()) {
|
||||
// Is a folder
|
||||
for (file in list) {
|
||||
val subList = context.assets.list("$path/$file")
|
||||
if (subList?.isEmpty() == true) {
|
||||
// Is file
|
||||
val metaData = loadFromJsonFile(context, "$path/$file")
|
||||
if (metaData != null) {
|
||||
ret.add(metaData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: java.lang.Exception) {}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.settings
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.AdvancedActivityBinding
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.PackageManagerUtils
|
||||
|
||||
class AdvancedActivity : AppCompatActivity(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private lateinit var binding: AdvancedActivityBinding
|
||||
private lateinit var prefs: PrefHelper
|
||||
|
||||
companion object {
|
||||
const val RESULT_APPLY_THEME = 0x322D
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
prefs = PrefHelper.getDefaultInstance(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = AdvancedActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
supportActionBar?.setTitle(R.string.settings__advanced__title)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
|
||||
prefs.sync()
|
||||
if (key == PrefHelper.Advanced.SETTINGS_THEME) {
|
||||
setResult(RESULT_APPLY_THEME)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
prefs.shared.registerOnSharedPreferenceChangeListener(this)
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
prefs.shared.unregisterOnSharedPreferenceChangeListener(this)
|
||||
updateLauncherIconStatus()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private fun updateLauncherIconStatus() {
|
||||
// Set LauncherAlias enabled/disabled state just before destroying/pausing this activity
|
||||
if (prefs.advanced.showAppIcon) {
|
||||
PackageManagerUtils.showAppIcon(this)
|
||||
} else {
|
||||
PackageManagerUtils.hideAppIcon(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentAdvancedBinding
|
||||
|
||||
class AdvancedFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private lateinit var binding: SettingsFragmentAdvancedBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = SettingsFragmentAdvancedBinding.inflate(inflater, container, false)
|
||||
|
||||
val transaction = childFragmentManager.beginTransaction()
|
||||
transaction.replace(
|
||||
binding.prefsAdvancedFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_advanced)
|
||||
)
|
||||
transaction.commit()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentLooknfeelBinding
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
import dev.patrickgold.florisboard.ime.text.layout.LayoutManager
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class LooknfeelFragment : SettingsMainActivity.SettingsFragment(), CoroutineScope by MainScope() {
|
||||
private lateinit var binding: SettingsFragmentLooknfeelBinding
|
||||
private lateinit var keyboardView: KeyboardView
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = SettingsFragmentLooknfeelBinding.inflate(inflater, container, false)
|
||||
|
||||
launch(Dispatchers.Default) {
|
||||
val themeContext = ContextThemeWrapper(context, prefs.theme.getSelectedThemeResId())
|
||||
val layoutManager = LayoutManager(themeContext)
|
||||
keyboardView = KeyboardView(themeContext)
|
||||
keyboardView.prefs = prefs
|
||||
keyboardView.isPreviewMode = true
|
||||
keyboardView.computedLayout = layoutManager.fetchComputedLayoutAsync(KeyboardMode.CHARACTERS, Subtype.DEFAULT).await()
|
||||
keyboardView.updateVisibility()
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.themeLinearLayout.addView(keyboardView, 0)
|
||||
}
|
||||
}
|
||||
|
||||
val transaction = childFragmentManager.beginTransaction()
|
||||
transaction.replace(
|
||||
binding.prefsLooknfeelFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_looknfeel)
|
||||
)
|
||||
transaction.replace(
|
||||
binding.prefsThemeFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_theme)
|
||||
)
|
||||
transaction.commit()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -28,32 +28,33 @@ import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsActivityBinding
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.core.SubtypeManager
|
||||
import dev.patrickgold.florisboard.settings.fragments.*
|
||||
import dev.patrickgold.florisboard.util.AppVersionUtils
|
||||
import dev.patrickgold.florisboard.util.PackageManagerUtils
|
||||
|
||||
private const val FRAGMENT_TAG = "FRAGMENT_TAG"
|
||||
internal const val FRAGMENT_TAG = "FRAGMENT_TAG"
|
||||
private const val PREF_RES_ID = "PREF_RES_ID"
|
||||
private const val SELECTED_ITEM_ID = "SELECTED_ITEM_ID"
|
||||
private const val ADVANCED_REQ_CODE = 0x145F
|
||||
|
||||
class SettingsMainActivity : AppCompatActivity(),
|
||||
BottomNavigationView.OnNavigationItemSelectedListener,
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
lateinit var binding: SettingsActivityBinding
|
||||
lateinit var prefs: PrefHelper
|
||||
private lateinit var prefs: PrefHelper
|
||||
lateinit var subtypeManager: SubtypeManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
prefs = PrefHelper(this, PreferenceManager.getDefaultSharedPreferences(this))
|
||||
prefs = PrefHelper.getDefaultInstance(this)
|
||||
prefs.initDefaultPreferences()
|
||||
subtypeManager =
|
||||
SubtypeManager(this, prefs)
|
||||
prefs.sync()
|
||||
subtypeManager = SubtypeManager(this, prefs)
|
||||
|
||||
val mode = when (prefs.advanced.settingsTheme) {
|
||||
"light" -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
@@ -99,9 +100,14 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
loadFragment(KeyboardFragment())
|
||||
true
|
||||
}
|
||||
R.id.settings__navigation__looknfeel -> {
|
||||
supportActionBar?.setTitle(R.string.settings__looknfeel__title)
|
||||
loadFragment(LooknfeelFragment())
|
||||
R.id.settings__navigation__typing -> {
|
||||
supportActionBar?.setTitle(R.string.settings__typing__title)
|
||||
loadFragment(TypingFragment())
|
||||
true
|
||||
}
|
||||
R.id.settings__navigation__theme -> {
|
||||
supportActionBar?.setTitle(R.string.settings__theme__title)
|
||||
loadFragment(ThemeFragment())
|
||||
true
|
||||
}
|
||||
R.id.settings__navigation__gestures -> {
|
||||
@@ -109,20 +115,15 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
loadFragment(GesturesFragment())
|
||||
true
|
||||
}
|
||||
R.id.settings__navigation__advanced -> {
|
||||
supportActionBar?.setTitle(R.string.settings__advanced__title)
|
||||
loadFragment(AdvancedFragment())
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFragment(fragment: Fragment) {
|
||||
val transaction = supportFragmentManager.beginTransaction()
|
||||
transaction.replace(binding.pageFrame.id, fragment, FRAGMENT_TAG)
|
||||
//transaction.addToBackStack(null)
|
||||
transaction.commit()
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(binding.pageFrame.id, fragment, FRAGMENT_TAG)
|
||||
.commit()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
@@ -130,6 +131,14 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (binding.bottomNavigation.selectedItemId != R.id.settings__navigation__home) {
|
||||
binding.bottomNavigation.selectedItemId = R.id.settings__navigation__home
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
@@ -144,6 +153,10 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
startActivity(browserIntent)
|
||||
true
|
||||
}
|
||||
R.id.settings__menu_advanced -> {
|
||||
startActivityForResult(Intent(this, AdvancedActivity::class.java), ADVANCED_REQ_CODE)
|
||||
true
|
||||
}
|
||||
R.id.settings__menu_about -> {
|
||||
startActivity(Intent(this, AboutActivity::class.java))
|
||||
true
|
||||
@@ -152,22 +165,18 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
|
||||
if (key == PrefHelper.Advanced.SETTINGS_THEME) {
|
||||
recreate()
|
||||
}
|
||||
val fragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG)
|
||||
if (fragment != null && fragment.isVisible) {
|
||||
if (fragment is LooknfeelFragment) {
|
||||
if (key == PrefHelper.Theme.NAME) {
|
||||
// TODO: recreate() is only a lazy solution, better would be to only recreate
|
||||
// the keyboard view
|
||||
recreate()
|
||||
}
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == ADVANCED_REQ_CODE) {
|
||||
if (resultCode == AdvancedActivity.RESULT_APPLY_THEME) {
|
||||
recreate()
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {}
|
||||
|
||||
private fun updateLauncherIconStatus() {
|
||||
// Set LauncherAlias enabled/disabled state just before destroying/pausing this activity
|
||||
if (prefs.advanced.showAppIcon) {
|
||||
@@ -195,7 +204,6 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
}
|
||||
|
||||
abstract class SettingsFragment : Fragment() {
|
||||
protected lateinit var prefs: PrefHelper
|
||||
protected lateinit var settingsMainActivity: SettingsMainActivity
|
||||
protected lateinit var subtypeManager: SubtypeManager
|
||||
|
||||
@@ -203,7 +211,6 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
settingsMainActivity = activity as SettingsMainActivity
|
||||
prefs = settingsMainActivity.prefs
|
||||
subtypeManager = settingsMainActivity.subtypeManager
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.settings.components
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.ThemeSelectorDialogBinding
|
||||
import dev.patrickgold.florisboard.databinding.ThemeSelectorListItemBinding
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMetaOnly
|
||||
|
||||
/**
|
||||
* Custom preference which handles the theme preset selection dialog and shows a summary in the
|
||||
* list.
|
||||
*/
|
||||
class ThemePresetSelectorPreference : Preference, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private var dialog: AlertDialog? = null
|
||||
private val metaDataCache: MutableMap<String, ThemeMetaOnly> = mutableMapOf()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
@Suppress("unused")
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
layoutResource = R.layout.list_item
|
||||
onPreferenceClickListener = OnPreferenceClickListener {
|
||||
showThemeSelectorDialog()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToHierarchy(preferenceManager: PreferenceManager?) {
|
||||
super.onAttachedToHierarchy(preferenceManager)
|
||||
summary = generateSummaryText()
|
||||
prefs.shared.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onDetached() {
|
||||
if (dialog?.isShowing == true) {
|
||||
dialog?.dismiss()
|
||||
}
|
||||
prefs.shared.unregisterOnSharedPreferenceChangeListener(this)
|
||||
super.onDetached()
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
|
||||
if (key == PrefHelper.Internal.THEME_CURRENT_IS_MODIFIED) {
|
||||
summary = generateSummaryText()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the summary text to display and returns it. Based on the prefs.internal.theme*
|
||||
* values and the theme meta cache.
|
||||
*/
|
||||
private fun generateSummaryText(): String {
|
||||
val themeKey = prefs.internal.themeCurrentBasedOn
|
||||
val isModified = prefs.internal.themeCurrentIsModified
|
||||
var metaOnly: ThemeMetaOnly? = metaDataCache[themeKey]
|
||||
if (metaOnly == null) {
|
||||
try {
|
||||
metaOnly = ThemeMetaOnly.loadFromJsonFile(context, "ime/theme/$themeKey.json")
|
||||
} catch (e: Exception) {
|
||||
return context.resources.getString(R.string.settings__theme__undefined)
|
||||
}
|
||||
}
|
||||
metaOnly ?: return context.resources.getString(R.string.settings__theme__undefined)
|
||||
return if (isModified) {
|
||||
String.format(context.resources.getString(R.string.settings__theme__preset_summary), metaOnly.displayName)
|
||||
} else {
|
||||
metaOnly.displayName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the theme selector dialog.
|
||||
*/
|
||||
private fun showThemeSelectorDialog() {
|
||||
val inflater =
|
||||
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
val dialogView = ThemeSelectorDialogBinding.inflate(inflater)
|
||||
val selectedThemeView = ThemeSelectorListItemBinding.inflate(inflater)
|
||||
selectedThemeView.title.text = generateSummaryText()
|
||||
dialogView.content.addView(selectedThemeView.root, 1)
|
||||
metaDataCache.clear()
|
||||
ThemeMetaOnly.loadAllFromDir(context, "ime/theme").forEach { metaData ->
|
||||
metaDataCache[metaData.name] = metaData
|
||||
}
|
||||
for ((themeKey, metaData) in metaDataCache) {
|
||||
if (themeKey == prefs.internal.themeCurrentBasedOn && !prefs.internal.themeCurrentIsModified) {
|
||||
continue
|
||||
}
|
||||
val availableThemeView = ThemeSelectorListItemBinding.inflate(inflater)
|
||||
availableThemeView.title.text = metaData.displayName
|
||||
availableThemeView.root.setOnClickListener {
|
||||
applyThemePreset(metaData.name)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
dialogView.content.addView(availableThemeView.root)
|
||||
}
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(this@ThemePresetSelectorPreference.title)
|
||||
setCancelable(true)
|
||||
setView(dialogView.root)
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
//
|
||||
}
|
||||
setNeutralButton(R.string.settings__default) { _, _ ->
|
||||
//
|
||||
}
|
||||
setNegativeButton(android.R.string.cancel, null)
|
||||
setOnDismissListener { summary = generateSummaryText() }
|
||||
create()
|
||||
dialog = show()
|
||||
dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the Theme for given [themeKey] to the preferences. Overrides any custom user-defined
|
||||
* theme in the shared prefs, if existent.
|
||||
*
|
||||
* @param themeKey The key of the Theme preset to be applied.
|
||||
*/
|
||||
private fun applyThemePreset(themeKey: String) {
|
||||
val theme = Theme.fromJsonFile(context, "ime/theme/$themeKey.json") ?: return
|
||||
Theme.writeThemeToPrefs(prefs, theme)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.settings.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
class AdvancedFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.prefs_advanced)
|
||||
}
|
||||
}
|
||||
@@ -14,25 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.settings
|
||||
package dev.patrickgold.florisboard.settings.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentGesturesBinding
|
||||
|
||||
class GesturesFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private lateinit var binding: SettingsFragmentGesturesBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = SettingsFragmentGesturesBinding.inflate(inflater, container, false)
|
||||
|
||||
return binding.root
|
||||
class GesturesFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.prefs_gestures)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.settings
|
||||
package dev.patrickgold.florisboard.settings.fragments
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
@@ -25,6 +25,7 @@ import android.view.ViewGroup
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentHomeBinding
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.settings.SettingsMainActivity
|
||||
import dev.patrickgold.florisboard.setup.SetupActivity
|
||||
|
||||
class HomeFragment : SettingsMainActivity.SettingsFragment() {
|
||||
@@ -56,6 +57,12 @@ class HomeFragment : SettingsMainActivity.SettingsFragment() {
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
binding.localizationCard.setOnClickListener {
|
||||
settingsMainActivity.binding.bottomNavigation.selectedItemId = R.id.settings__navigation__typing
|
||||
}
|
||||
binding.themeCard.setOnClickListener {
|
||||
settingsMainActivity.binding.bottomNavigation.selectedItemId = R.id.settings__navigation__theme
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.settings.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
class KeyboardFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.prefs_keyboard)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.settings.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
class ThemeCustomizeFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.prefs_theme)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.settings.fragments
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentThemeBinding
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
import dev.patrickgold.florisboard.ime.text.layout.LayoutManager
|
||||
import dev.patrickgold.florisboard.settings.SettingsMainActivity
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class ThemeFragment : SettingsMainActivity.SettingsFragment(), CoroutineScope by MainScope(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private lateinit var binding: SettingsFragmentThemeBinding
|
||||
private lateinit var keyboardView: KeyboardView
|
||||
private lateinit var prefs: PrefHelper
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
prefs = PrefHelper.getDefaultInstance(requireContext())
|
||||
binding = SettingsFragmentThemeBinding.inflate(inflater, container, false)
|
||||
|
||||
launch(Dispatchers.Default) {
|
||||
val themeContext = ContextThemeWrapper(context, FlorisBoard.getDayNightBaseThemeId(prefs.internal.themeCurrentIsNight))
|
||||
val layoutManager = LayoutManager(themeContext)
|
||||
keyboardView = KeyboardView(themeContext)
|
||||
keyboardView.layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
val m = resources.getDimension(R.dimen.keyboard_preview_margin).toInt()
|
||||
setMargins(m, m, m, m)
|
||||
}
|
||||
prefs.sync()
|
||||
keyboardView.isPreviewMode = true
|
||||
val subtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
|
||||
keyboardView.computedLayout = layoutManager.fetchComputedLayoutAsync(KeyboardMode.CHARACTERS, subtype).await()
|
||||
keyboardView.updateVisibility()
|
||||
keyboardView.onApplyThemeAttributes()
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.root.addView(keyboardView, 0)
|
||||
}
|
||||
}
|
||||
|
||||
loadThemePrefFragment()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun loadThemePrefFragment() {
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
binding.prefsFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_theme)
|
||||
)
|
||||
.commit()
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
|
||||
prefs.sync()
|
||||
key ?: return
|
||||
if (key == PrefHelper.Internal.THEME_CURRENT_BASED_ON ||
|
||||
key == PrefHelper.Internal.THEME_CURRENT_IS_MODIFIED && !prefs.internal.themeCurrentIsModified) {
|
||||
loadThemePrefFragment()
|
||||
}
|
||||
if (key.startsWith("theme__")) {
|
||||
prefs.internal.themeCurrentIsModified = true
|
||||
keyboardView.onApplyThemeAttributes()
|
||||
keyboardView.invalidate()
|
||||
keyboardView.invalidateAllKeys()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
prefs.shared.registerOnSharedPreferenceChangeListener(this)
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
prefs.shared.unregisterOnSharedPreferenceChangeListener(this)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.settings
|
||||
package dev.patrickgold.florisboard.settings.fragments
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
@@ -25,12 +25,13 @@ import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.ListItemBinding
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentKeyboardBinding
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentKeyboardSubtypeDialogBinding
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentTypingBinding
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentTypingSubtypeDialogBinding
|
||||
import dev.patrickgold.florisboard.settings.SettingsMainActivity
|
||||
import dev.patrickgold.florisboard.util.LocaleUtils
|
||||
|
||||
class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private lateinit var binding: SettingsFragmentKeyboardBinding
|
||||
class TypingFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private lateinit var binding: SettingsFragmentTypingBinding
|
||||
/**
|
||||
* Must always have a reference to the open AlertDialog to dismiss the AlertDialog in the event
|
||||
* of onDestroy(), if this is not done a memory leak will most likely happen!
|
||||
@@ -42,17 +43,18 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = SettingsFragmentKeyboardBinding.inflate(inflater, container, false)
|
||||
binding = SettingsFragmentTypingBinding.inflate(inflater, container, false)
|
||||
binding.subtypeAddBtn.setOnClickListener { showAddSubtypeDialog() }
|
||||
|
||||
updateSubtypeListView()
|
||||
|
||||
val transaction = childFragmentManager.beginTransaction()
|
||||
transaction.replace(
|
||||
binding.prefsKeyboardFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_keyboard)
|
||||
)
|
||||
transaction.commit()
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
binding.prefsFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_typing)
|
||||
)
|
||||
.commit()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
@@ -64,7 +66,7 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
|
||||
private fun showAddSubtypeDialog() {
|
||||
val dialogView =
|
||||
SettingsFragmentKeyboardSubtypeDialogBinding.inflate(layoutInflater)
|
||||
SettingsFragmentTypingSubtypeDialogBinding.inflate(layoutInflater)
|
||||
val languageAdapter: ArrayAdapter<String> = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
@@ -93,11 +95,11 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
)
|
||||
dialogView.layoutSpinner.adapter = layoutAdapter
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.settings__keyboard__subtype_add_title)
|
||||
setTitle(R.string.settings__localization__subtype_add_title)
|
||||
setCancelable(true)
|
||||
setView(dialogView.root)
|
||||
setPositiveButton(R.string.settings__keyboard__subtype_add, null)
|
||||
setNegativeButton(R.string.settings__keyboard__subtype_cancel) { _, _ -> }
|
||||
setPositiveButton(R.string.settings__localization__subtype_add, null)
|
||||
setNegativeButton(R.string.settings__localization__subtype_cancel) { _, _ -> }
|
||||
setOnDismissListener { activeDialogWindow = null }
|
||||
create()
|
||||
activeDialogWindow = show()
|
||||
@@ -108,7 +110,7 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
val layoutName = subtypeManager.imeConfig.characterLayouts.keys.toList()[dialogView.layoutSpinner.selectedItemPosition]
|
||||
val success = subtypeManager.addSubtype(LocaleUtils.stringToLocale(languageCode), layoutName)
|
||||
if (!success) {
|
||||
dialogView.errorBox.setText(R.string.settings__keyboard__subtype_error_already_exists)
|
||||
dialogView.errorBox.setText(R.string.settings__localization__subtype_error_already_exists)
|
||||
dialogView.errorBox.visibility = View.VISIBLE
|
||||
} else {
|
||||
updateSubtypeListView()
|
||||
@@ -121,7 +123,7 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private fun showEditSubtypeDialog(id: Int) {
|
||||
val subtype = subtypeManager.getSubtypeById(id) ?: return
|
||||
val dialogView =
|
||||
SettingsFragmentKeyboardSubtypeDialogBinding.inflate(layoutInflater)
|
||||
SettingsFragmentTypingSubtypeDialogBinding.inflate(layoutInflater)
|
||||
val languageAdapter: ArrayAdapter<String> = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
@@ -141,10 +143,10 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
subtypeManager.imeConfig.characterLayouts.keys.toList().indexOf(subtype.layout)
|
||||
)
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.settings__keyboard__subtype_edit_title)
|
||||
setTitle(R.string.settings__localization__subtype_edit_title)
|
||||
setCancelable(true)
|
||||
setView(dialogView.root)
|
||||
setPositiveButton(R.string.settings__keyboard__subtype_apply) { _, _ ->
|
||||
setPositiveButton(R.string.settings__localization__subtype_apply) { _, _ ->
|
||||
val languageCode = subtypeManager.imeConfig.defaultSubtypesLanguageCodes[dialogView.languageSpinner.selectedItemPosition]
|
||||
val layoutName = subtypeManager.imeConfig.characterLayouts.keys.toList()[dialogView.layoutSpinner.selectedItemPosition]
|
||||
subtype.locale = LocaleUtils.stringToLocale(languageCode)
|
||||
@@ -152,11 +154,11 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
subtypeManager.modifySubtypeWithSameId(subtype)
|
||||
updateSubtypeListView()
|
||||
}
|
||||
setNeutralButton(R.string.settings__keyboard__subtype_delete) { _, _ ->
|
||||
setNeutralButton(R.string.settings__localization__subtype_delete) { _, _ ->
|
||||
subtypeManager.removeSubtype(subtype)
|
||||
updateSubtypeListView()
|
||||
}
|
||||
setNegativeButton(R.string.settings__keyboard__subtype_cancel) { _, _ -> }
|
||||
setNegativeButton(R.string.settings__localization__subtype_cancel) { _, _ -> }
|
||||
setOnDismissListener { activeDialogWindow = null }
|
||||
create()
|
||||
activeDialogWindow = show()
|
||||
@@ -22,6 +22,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import dev.patrickgold.florisboard.databinding.SetupFragmentFinishBinding
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
|
||||
class FinishFragment : Fragment() {
|
||||
private lateinit var binding: SetupFragmentFinishBinding
|
||||
@@ -33,6 +34,12 @@ class FinishFragment : Fragment() {
|
||||
): View? {
|
||||
binding = SetupFragmentFinishBinding.inflate(inflater, container, false)
|
||||
|
||||
// Set theme to floris_day
|
||||
Theme.writeThemeToPrefs(
|
||||
(activity as SetupActivity).prefs,
|
||||
Theme.fromJsonFile(requireContext(), "ime/theme/floris_day.json")!!
|
||||
)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package dev.patrickgold.florisboard.setup
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -26,6 +27,7 @@ import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
|
||||
class MakeDefaultFragment : Fragment(), SetupActivity.EventListener {
|
||||
private lateinit var binding: SetupFragmentMakeDefaultBinding
|
||||
private var osHandler: Handler? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@@ -60,6 +62,11 @@ class MakeDefaultFragment : Fragment(), SetupActivity.EventListener {
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
if (hasFocus && context != null) {
|
||||
updateState()
|
||||
if (osHandler == null) {
|
||||
osHandler = Handler()
|
||||
}
|
||||
osHandler?.postDelayed({ updateState() }, 250)
|
||||
osHandler?.postDelayed({ updateState() }, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class SetupActivity : AppCompatActivity() {
|
||||
private lateinit var adapter: ViewPagerAdapter
|
||||
private lateinit var binding: SetupActivityBinding
|
||||
lateinit var imm: InputMethodManager
|
||||
private lateinit var prefs: PrefHelper
|
||||
lateinit var prefs: PrefHelper
|
||||
private var shouldFinish: Boolean = false
|
||||
private var shouldLaunchSettings: Boolean = true
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import androidx.core.view.children
|
||||
|
||||
fun getColorFromAttr(
|
||||
@@ -33,16 +34,28 @@ fun setBackgroundTintColor(view: View, colorId: Int) {
|
||||
getColorFromAttr(view.context, colorId)
|
||||
)
|
||||
}
|
||||
fun setBackgroundTintColor2(view: View, colorInt: Int) {
|
||||
view.backgroundTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
fun setDrawableTintColor(view: Button, colorId: Int) {
|
||||
view.compoundDrawableTintList = ColorStateList.valueOf(
|
||||
getColorFromAttr(view.context, colorId)
|
||||
)
|
||||
}
|
||||
fun setDrawableTintColor2(view: Button, colorInt: Int) {
|
||||
view.compoundDrawableTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
fun setImageTintColor2(view: ImageView, colorInt: Int) {
|
||||
view.imageTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
fun setTextTintColor(view: View, colorId: Int) {
|
||||
view.foregroundTintList = ColorStateList.valueOf(
|
||||
getColorFromAttr(view.context, colorId)
|
||||
)
|
||||
}
|
||||
fun setTextTintColor2(view: View, colorInt: Int) {
|
||||
view.foregroundTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
|
||||
fun refreshLayoutOf(view: View?) {
|
||||
if (view is ViewGroup) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false" android:color="?android:colorButtonNormal"/>
|
||||
<item android:color="?smartbar_button_fgColor"/>
|
||||
<item android:color="#FFFFFF"/>
|
||||
</selector>
|
||||
5
app/src/main/res/drawable/ic_spellcheck.xml
Normal file
5
app/src/main/res/drawable/ic_spellcheck.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12.45,16h2.09L9.43,3L7.57,3L2.46,16h2.09l1.12,-3h5.64l1.14,3zM6.43,11L8.5,5.48 10.57,11L6.43,11zM21.59,11.59l-8.09,8.09L9.83,16l-1.41,1.41 5.09,5.09L23,13l-1.41,-1.41z"/>
|
||||
</vector>
|
||||
@@ -2,16 +2,16 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape>
|
||||
<padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
|
||||
<solid android:color="?key_popup_extended_shadowColor" />
|
||||
<corners android:radius="@dimen/key_borderRadius" />
|
||||
<padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp"/>
|
||||
<solid android:color="#CDAFAFAF"/>
|
||||
<corners android:radius="@dimen/key_borderRadius"/>
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?key_popup_bgColor" />
|
||||
<corners android:radius="@dimen/key_borderRadius" />
|
||||
<solid android:color="@android:color/white"/>
|
||||
<corners android:radius="@dimen/key_borderRadius"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
||||
16
app/src/main/res/layout/advanced_activity.xml
Normal file
16
app/src/main/res/layout/advanced_activity.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/toolbar"/>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/advanced_fragment_frame"
|
||||
android:name="dev.patrickgold.florisboard.settings.fragments.AdvancedFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,12 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyboardView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyboardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/editing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white"
|
||||
android:backgroundTintMode="multiply">
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/arrow_left"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.2"
|
||||
@@ -16,7 +18,7 @@
|
||||
app:layout_constraintBottom_toTopOf="@+id/move_home"
|
||||
android:src="@drawable/ic_keyboard_arrow_left"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/arrow_up"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
@@ -25,7 +27,7 @@
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_left"
|
||||
android:src="@drawable/ic_keyboard_arrow_up"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/select"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
@@ -34,7 +36,7 @@
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_left"
|
||||
android:text="@android:string/selectTextMode"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/arrow_down"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
@@ -43,7 +45,7 @@
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_left"
|
||||
android:src="@drawable/ic_keyboard_arrow_down"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/arrow_right"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.2"
|
||||
@@ -53,7 +55,7 @@
|
||||
app:layout_constraintLeft_toRightOf="@+id/select"
|
||||
android:src="@drawable/ic_keyboard_arrow_right"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/move_home"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.35"
|
||||
@@ -62,7 +64,7 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:src="@drawable/ic_first_page"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/move_end"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.35"
|
||||
@@ -71,7 +73,7 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:src="@drawable/ic_last_page"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/select_all"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
@@ -82,7 +84,7 @@
|
||||
android:text="@android:string/selectAll"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/clipboard_cut"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
@@ -100,7 +102,7 @@
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="select_all,clipboard_cut"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/clipboard_copy"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
@@ -111,7 +113,7 @@
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:text="@android:string/copy"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/clipboard_paste"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
@@ -122,7 +124,7 @@
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:text="@android:string/paste"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
<dev.patrickgold.florisboard.ime.text.editing.EditingKeyView
|
||||
android:id="@+id/backspace"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
@@ -133,4 +135,4 @@
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:src="@drawable/ic_backspace"/>
|
||||
|
||||
</dev.patrickgold.florisboard.ime.editing.EditingKeyboardView>
|
||||
</dev.patrickgold.florisboard.ime.text.editing.EditingKeyboardView>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<dev.patrickgold.florisboard.ime.core.InputView
|
||||
<dev.patrickgold.florisboard.ime.core.InputWindowView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/florisboard"
|
||||
android:layout_width="match_parent"
|
||||
@@ -8,13 +8,13 @@
|
||||
android:gravity="bottom"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<dev.patrickgold.florisboard.ime.core.InputView
|
||||
android:id="@+id/inner_input_view_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="horizontal"
|
||||
android:background="?keyboard_bgColor"
|
||||
android:background="?inputView_bgColorFallback"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<LinearLayout
|
||||
@@ -25,32 +25,34 @@
|
||||
<ImageButton
|
||||
android:id="@+id/one_handed_ctrl_close_start"
|
||||
style="@style/OneHandedPanelButton"
|
||||
android:src="@drawable/ic_zoom_out_map"/>
|
||||
android:src="@drawable/ic_zoom_out_map"
|
||||
android:contentDescription="@string/one_handed__close_btn_content_description"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/one_handed_button_height"
|
||||
android:visibility="invisible" />
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/one_handed_ctrl_move_start"
|
||||
style="@style/OneHandedPanelButton"
|
||||
android:src="@drawable/ic_keyboard_arrow_left"/>
|
||||
android:src="@drawable/ic_keyboard_arrow_left"
|
||||
android:contentDescription="@string/one_handed__move_start_btn_content_description"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ViewFlipper
|
||||
<dev.patrickgold.florisboard.ime.core.FlorisViewFlipper
|
||||
android:id="@+id/main_view_flipper"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:measureAllChildren="false">
|
||||
|
||||
<include layout="@layout/text_input_layout" />
|
||||
<include layout="@layout/text_input_layout"/>
|
||||
|
||||
<include layout="@layout/media_input_layout" />
|
||||
<include layout="@layout/media_input_layout"/>
|
||||
|
||||
</ViewFlipper>
|
||||
</dev.patrickgold.florisboard.ime.core.FlorisViewFlipper>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/one_handed_ctrl_panel_end"
|
||||
@@ -60,20 +62,22 @@
|
||||
<ImageButton
|
||||
android:id="@+id/one_handed_ctrl_close_end"
|
||||
style="@style/OneHandedPanelButton"
|
||||
android:src="@drawable/ic_zoom_out_map"/>
|
||||
android:src="@drawable/ic_zoom_out_map"
|
||||
android:contentDescription="@string/one_handed__close_btn_content_description"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/one_handed_button_height"
|
||||
android:visibility="invisible" />
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/one_handed_ctrl_move_end"
|
||||
style="@style/OneHandedPanelButton"
|
||||
android:src="@drawable/ic_keyboard_arrow_right"/>
|
||||
android:src="@drawable/ic_keyboard_arrow_right"
|
||||
android:contentDescription="@string/one_handed__move_end_btn_content_description"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</dev.patrickgold.florisboard.ime.core.InputView>
|
||||
|
||||
</dev.patrickgold.florisboard.ime.core.InputView>
|
||||
</dev.patrickgold.florisboard.ime.core.InputWindowView>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<dev.patrickgold.florisboard.ime.popup.KeyPopupView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/key_popup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/key_popup_bgshape">
|
||||
android:background="@drawable/key_popup_bgshape"
|
||||
android:backgroundTintMode="multiply">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/key_popup_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/key_height"
|
||||
android:gravity="center"
|
||||
android:textColor="?attr/key_popup_fgColor"
|
||||
android:textSize="@dimen/key_popup_textSize"/>
|
||||
|
||||
<ImageView
|
||||
@@ -23,7 +24,6 @@
|
||||
android:layout_gravity="end"
|
||||
android:padding="0dp"
|
||||
android:src="@drawable/ic_more_horiz"
|
||||
android:tint="?attr/key_popup_fgColor"
|
||||
android:visibility="visible" />
|
||||
android:visibility="visible"/>
|
||||
|
||||
</LinearLayout>
|
||||
</dev.patrickgold.florisboard.ime.popup.KeyPopupView>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
<dev.patrickgold.florisboard.ime.popup.KeyPopupExtendedView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/key_popup_extended"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/key_popup_bgshape"
|
||||
android:backgroundTintMode="multiply"
|
||||
android:padding="0dp"
|
||||
android:textSize="@dimen/key_popup_textSize"
|
||||
app:flexDirection="row"
|
||||
app:flexWrap="wrap"
|
||||
app:showDivider="none" />
|
||||
app:showDivider="none"/>
|
||||
|
||||
@@ -3,15 +3,20 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:listPreferredItemHeightSmall"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
@@ -22,7 +27,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<dev.patrickgold.florisboard.ime.media.MediaInputView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/media_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ViewFlipper
|
||||
<dev.patrickgold.florisboard.ime.core.FlorisViewFlipper
|
||||
android:id="@+id/media_input_view_flipper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:measureAllChildren="true"/>
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
android:measureAllChildren="false"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.0"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
@@ -87,4 +90,4 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</dev.patrickgold.florisboard.ime.media.MediaInputView>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="GESTURES | not yet implemented..."/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -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">
|
||||
@@ -50,4 +60,28 @@
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/localization_card"
|
||||
style="@style/SettingsCardView.Clickable">
|
||||
|
||||
<!-- TODO: create lang preview and make nice feature graphic for this card -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Localization prefs -->"/>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/theme_card"
|
||||
style="@style/SettingsCardView.Clickable">
|
||||
|
||||
<!-- TODO: create theme preview and make nice feature graphic for this card -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Theme prefs -->"/>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
style="@style/SettingsCardView"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/themeLinearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- KeyboardView preview will be inserted here programmatically -->
|
||||
|
||||
<!-- Must be RelativeLayout, because it blocks RecyclerView to create a second scrollbar -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/prefs_theme_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Must be RelativeLayout, because it blocks RecyclerView to create a second scrollbar -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/prefs_looknfeel_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -4,9 +4,11 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- KeyboardView preview will be inserted here programmatically -->
|
||||
|
||||
<!-- Must be RelativeLayout, because it blocks RecyclerView to create a second scrollbar -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/prefs_advanced_frame"
|
||||
android:id="@+id/prefs_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
@@ -12,20 +12,31 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:theme="@style/PreferenceThemeOverlay">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtype_heading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/settings__localization__title"
|
||||
android:textAppearance="?preferenceCategoryTitleTextAppearance"
|
||||
android:textColor="?colorAccent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtype_not_conf_warning"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_margin="8dp"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/shape_rect_rounded"
|
||||
android:backgroundTint="?colorWarning"
|
||||
android:text="@string/settings__keyboard__subtype_no_subtypes_configured_warning"
|
||||
android:text="@string/settings__localization__subtype_no_subtypes_configured_warning"
|
||||
android:textColor="?textColorWarning"/>
|
||||
|
||||
<LinearLayout
|
||||
@@ -40,7 +51,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textAllCaps="false"
|
||||
android:text="@string/settings__keyboard__subtype_add_title"
|
||||
android:text="@string/settings__localization__subtype_add_title"
|
||||
android:drawableStart="@drawable/ic_add"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableTint="?colorAccent"/>
|
||||
@@ -51,7 +62,7 @@
|
||||
|
||||
<!-- Must be RelativeLayout, because it blocks RecyclerView to create a second scrollbar -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/prefs_keyboard_frame"
|
||||
android:id="@+id/prefs_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorError"
|
||||
tools:text="@string/settings__keyboard__subtype_error_already_exists"
|
||||
tools:text="@string/settings__localization__subtype_error_already_exists"
|
||||
android:textColor="@color/textColorError"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:padding="8dp"
|
||||
@@ -27,7 +27,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings__keyboard__subtype_locale"
|
||||
android:text="@string/settings__localization__subtype_locale"
|
||||
android:paddingHorizontal="8dp"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
@@ -46,7 +46,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings__keyboard__subtype_layout"
|
||||
android:text="@string/settings__localization__subtype_layout"
|
||||
android:paddingHorizontal="8dp"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
@@ -4,8 +4,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/smartbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/smartbar_height"
|
||||
android:background="?smartbar_bgColor">
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/smartbar_variant_default"
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/smartbar" />
|
||||
<include layout="@layout/smartbar"/>
|
||||
|
||||
<!-- KeyboardViews will be inserted in ViewFlipper below dynamically -->
|
||||
<ViewFlipper
|
||||
<dev.patrickgold.florisboard.ime.core.FlorisViewFlipper
|
||||
android:id="@+id/text_input_view_flipper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -33,6 +33,6 @@
|
||||
|
||||
<include layout="@layout/editing_layout"/>
|
||||
|
||||
</ViewFlipper>
|
||||
</dev.patrickgold.florisboard.ime.core.FlorisViewFlipper>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
29
app/src/main/res/layout/theme_selector_dialog.xml
Normal file
29
app/src/main/res/layout/theme_selector_dialog.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/settings__theme__preset_dialog_selected_theme"
|
||||
android:textColor="?android:textColorPrimary"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/settings__theme__preset_dialog_available_themes"
|
||||
android:textColor="?android:textColorPrimary"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
36
app/src/main/res/layout/theme_selector_list_item.xml
Normal file
36
app/src/main/res/layout/theme_selector_list_item.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
tools:text="Theme Name"
|
||||
android:textColor="?android:textColorSecondary"/>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:src="@drawable/ic_keyboard_arrow_right"
|
||||
android:contentDescription="@string/settings__theme__preset_dialog_alt_arrow_right"
|
||||
app:tint="?android:textColorSecondary"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -6,17 +6,24 @@
|
||||
android:icon="@drawable/ic_more_vert"
|
||||
android:title="@string/settings__menu"
|
||||
app:showAsAction="always">
|
||||
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/settings__menu_help"
|
||||
android:orderInCategory="1"
|
||||
android:title="@string/settings__menu_help" />
|
||||
android:title="@string/settings__menu_help"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__menu_advanced"
|
||||
android:orderInCategory="2"
|
||||
android:title="@string/settings__menu_advanced"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__menu_about"
|
||||
android:orderInCategory="2"
|
||||
android:title="@string/settings__menu_about" />
|
||||
android:orderInCategory="3"
|
||||
android:title="@string/settings__menu_about"/>
|
||||
</menu>
|
||||
|
||||
</item>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -4,26 +4,26 @@
|
||||
<item
|
||||
android:id="@+id/settings__navigation__home"
|
||||
android:icon="@drawable/ic_home"
|
||||
android:title="@string/settings__navigation__home" />
|
||||
android:title="@string/settings__navigation__home"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__keyboard"
|
||||
android:icon="@drawable/ic_keyboard"
|
||||
android:title="@string/settings__navigation__keyboard" />
|
||||
android:title="@string/settings__navigation__keyboard"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__looknfeel"
|
||||
android:id="@+id/settings__navigation__typing"
|
||||
android:icon="@drawable/ic_spellcheck"
|
||||
android:title="@string/settings__navigation__typing"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__theme"
|
||||
android:icon="@drawable/ic_palette"
|
||||
android:title="@string/settings__navigation__looknfeel" />
|
||||
android:title="@string/settings__navigation__theme"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__gestures"
|
||||
android:icon="@drawable/ic_gesture"
|
||||
android:title="@string/settings__navigation__gestures" />
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__advanced"
|
||||
android:icon="@drawable/ic_more_vert"
|
||||
android:title="@string/settings__navigation__advanced" />
|
||||
android:title="@string/settings__navigation__gestures"/>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -32,51 +32,54 @@
|
||||
<string name="settings__menu_help">Aiuto & feedback</string>
|
||||
<string name="settings__navigation__home">Home</string>
|
||||
<string name="settings__navigation__keyboard">Tastiera</string>
|
||||
<string name="settings__navigation__looknfeel">Aspetto & funzionalità</string>
|
||||
<string name="settings__navigation__typing">Digitazione</string>
|
||||
<string name="settings__navigation__theme">Tema</string>
|
||||
<string name="settings__navigation__gestures">Gesti</string>
|
||||
<string name="settings__navigation__advanced">Avanzate</string>
|
||||
|
||||
<string name="settings__home__title">Benvenuto in %s</string>
|
||||
<string name="settings__home__ime_not_enabled">FlorisBoard non è abilitato nel sistema e quindi non sarà disponibile come metodo di immissione.Clicca quì per risolvere questo problema.</string>
|
||||
<string name="settings__home__ime_not_selected">FlorisBoard non è la tastiera predefinita. Clicca quì per risolvere questo problema.</string>
|
||||
<string name="settings__home__contribute">Grazie per aver provato FlorisBoard! Questo progetto è ancora in fase alfa e quindi manca di alcune funzionalità. Se trovate qualche bug o volete dare un suggerimento, date un\'occhiata al repo su GitHub e segnalate un problema. Questo aiuta a rendere FlorisBoard migliore. Grazie!</string>
|
||||
|
||||
<string name="settings__keyboard__title">Tastiera & Correzione del testo</string>
|
||||
<string name="settings__keyboard__subtype_no_subtypes_configured_warning">Sembra che tu non abbia configurato nessuno stile di input personalizzato. Come ripiego verrà utilizzato lo stile input English/QWERTY!</string>
|
||||
<string name="settings__keyboard__subtype_add">Aggiungi</string>
|
||||
<string name="settings__keyboard__subtype_add_title">Aggiungi stile input</string>
|
||||
<string name="settings__keyboard__subtype_apply">Applica</string>
|
||||
<string name="settings__keyboard__subtype_cancel">Annulla</string>
|
||||
<string name="settings__keyboard__subtype_delete">Elimina</string>
|
||||
<string name="settings__keyboard__subtype_edit_title">Modifica stile di input</string>
|
||||
<string name="settings__keyboard__subtype_locale">Locale</string>
|
||||
<string name="settings__keyboard__subtype_layout">Layout della tastiera</string>
|
||||
<string name="settings__keyboard__subtype_error_already_exists">Questo stile di input esiste già !</string>
|
||||
<string name="settings__localization__title">Lingue & Layout della tastiera</string>
|
||||
<string name="settings__localization__subtype_no_subtypes_configured_warning">Sembra che tu non abbia configurato nessuno stile di input personalizzato. Come ripiego verrà utilizzato lo stile input English/QWERTY!</string>
|
||||
<string name="settings__localization__subtype_add">Aggiungi</string>
|
||||
<string name="settings__localization__subtype_add_title">Aggiungi stile input</string>
|
||||
<string name="settings__localization__subtype_apply">Applica</string>
|
||||
<string name="settings__localization__subtype_cancel">Annulla</string>
|
||||
<string name="settings__localization__subtype_delete">Elimina</string>
|
||||
<string name="settings__localization__subtype_edit_title">Modifica stile di input</string>
|
||||
<string name="settings__localization__subtype_locale">Locale</string>
|
||||
<string name="settings__localization__subtype_layout">Layout della tastiera</string>
|
||||
<string name="settings__localization__subtype_error_already_exists">Questo stile di input esiste già !</string>
|
||||
|
||||
<string name="settings__theme__title">Tema tastiera</string>
|
||||
<string name="pref__theme__name__label">Tema tastiera</string>
|
||||
|
||||
<string name="settings__keyboard__title">Tastiera preferenze</string>
|
||||
<string name="pref__keyboard__group_layout__label">Layout</string>
|
||||
<string name="pref__keyboard__one_handed_mode__label">Modalità ad una mano</string>
|
||||
<string name="pref__keyboard__height_factor__label">Altezza tastiera</string>
|
||||
<string name="pref__keyboard__group_keypress__label">Pressione tasti</string>
|
||||
<string name="pref__keyboard__sound_enabled__label">Suono pressione tasti</string>
|
||||
<string name="pref__keyboard__sound_volume__label">Volume del suono alla pressione dei tasti</string>
|
||||
<string name="pref__keyboard__vibration_enabled__label">Vibrazione alla pressione dei tasti</string>
|
||||
<string name="pref__keyboard__vibration_strength__label">Intensità della vibrazione alla pressione dei tasti</string>
|
||||
<string name="pref__keyboard__popup_visible__label">Visibilità Popup</string>
|
||||
<string name="pref__keyboard__popup_visible__summary">Mostra popup quando si preme un tasto</string>
|
||||
<string name="pref__keyboard__long_press_delay__label">Ritardo lunga pressione tasti</string>
|
||||
|
||||
<string name="settings__typing__title">Esperienza di digitazione</string>
|
||||
<string name="pref__suggestion__title">Suggerimenti</string>
|
||||
<string name="pref__suggestion__enabled__label">Visualizza suggerimenti mentre digiti</string>
|
||||
<string name="pref__suggestion__enabled__summary">Verrà visualizzato nella parte superiore della tastiera</string>
|
||||
<string name="pref__suggestion__use_pref_words__label">Suggerimenti per la parola successiva</string>
|
||||
<string name="pref__suggestion__use_pref_words__summary">Utilizzare le parole precedenti per generare suggerimenti</string>
|
||||
<string name="pref__correction__title">Correzioni</string>
|
||||
<string name="pref__popup__title">PopUp</string>
|
||||
<string name="pref__correction__double_space_period__label">Doppio tocco barra spaziatrice</string>
|
||||
<string name="pref__correction__double_space_period__summary">Doppio tocco su barra spaziatrice per mettere il punto (.) seguito da uno spazio</string>
|
||||
<string name="pref__popup__visible__label">Visibilità Popup</string>
|
||||
<string name="pref__popup__visible__summary">Mostra popup quando si preme un tasto</string>
|
||||
|
||||
<string name="settings__looknfeel__title">Aspetto & funzionalità</string>
|
||||
<string name="pref__looknfeel__group_layout__label">Layout</string>
|
||||
<string name="pref__looknfeel__height_factor__label">Altezza tastiera</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__label">Modalità ad una mano</string>
|
||||
<string name="pref__looknfeel__group_keypress__label">Pressione tasti</string>
|
||||
<string name="pref__looknfeel__long_press_delay__label">Ritardo lunga pressione tasti</string>
|
||||
<string name="pref__looknfeel__sound_enabled__label">Suono pressione tasti</string>
|
||||
<string name="pref__looknfeel__sound_volume__label">Volume del suono alla pressione dei tasti</string>
|
||||
<string name="pref__looknfeel__vibration_enabled__label">Vibrazione alla pressione dei tasti</string>
|
||||
<string name="pref__looknfeel__vibration_strength__label">Intensità della vibrazione alla pressione dei tasti</string>
|
||||
<string name="pref__theme__name__label">Tema tastiera</string>
|
||||
|
||||
<string name="settings__gestures__title">Gesti</string>
|
||||
<string name="settings__gestures__title">Gesti & Digitazione a scorrimento</string>
|
||||
|
||||
<string name="settings__advanced__title">Avanzate</string>
|
||||
<string name="pref__advanced__settings_theme__label">Impostazioni tema</string>
|
||||
@@ -100,7 +103,7 @@
|
||||
<string name="setup__ok_button">OK</string>
|
||||
|
||||
<string name="setup__welcome__title">Benvenuto!</string>
|
||||
<string name="setup__welcome__intro">TGrazie per aver provato FlorisBoard! Prima che possiate iniziare ad usarlo, dobbiamo fare le solite cose e abilitarlo nelle impostazioni di sistema, impostare la vostra lingua/ il layout preferito, ecc... Ma non preoccuparti: segui questa procedura guidata </string>
|
||||
<string name="setup__welcome__intro">Grazie per aver provato FlorisBoard! Prima che possiate iniziare ad usarlo, dobbiamo fare le solite cose e abilitarlo nelle impostazioni di sistema, impostare la vostra lingua/ il layout preferito, ecc... Ma non preoccuparti: segui questa procedura guidata </string>
|
||||
<string name="setup__welcome__privacy">[[ TODO: inserisci quì la descrizione della privacy ]]</string>
|
||||
<string name="setup__welcome__trust">Il codice sorgente di FlorisBoard è accessibile pubblicamente a chiunque, quindi puoi facilmente rivedere cosa fa FlorisBoard in background. Controlla il link nel repository in basso.</string>
|
||||
<string name="setup__welcome__contribute">Un\'ultima cosa prima di iniziare l\'installazione - se riscontri errori / arresti anomali / problemi con FlorisBoard o hai una richiesta di funzionalità - vai al repository GitHub collegato di seguito e presenta un problema. Questo aiuta a migliorare l\'esperienza per tutti gli utenti!</string>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref__looknfeel__height_factor__entries">
|
||||
<item>@string/pref__looknfeel__height_factor__extra_short</item>
|
||||
<item>@string/pref__looknfeel__height_factor__short</item>
|
||||
<item>@string/pref__looknfeel__height_factor__mid_short</item>
|
||||
<item>@string/pref__looknfeel__height_factor__normal</item>
|
||||
<item>@string/pref__looknfeel__height_factor__mid_tall</item>
|
||||
<item>@string/pref__looknfeel__height_factor__tall</item>
|
||||
<item>@string/pref__looknfeel__height_factor__extra_tall</item>
|
||||
<string-array name="pref__keyboard__height_factor__entries">
|
||||
<item>@string/pref__keyboard__height_factor__extra_short</item>
|
||||
<item>@string/pref__keyboard__height_factor__short</item>
|
||||
<item>@string/pref__keyboard__height_factor__mid_short</item>
|
||||
<item>@string/pref__keyboard__height_factor__normal</item>
|
||||
<item>@string/pref__keyboard__height_factor__mid_tall</item>
|
||||
<item>@string/pref__keyboard__height_factor__tall</item>
|
||||
<item>@string/pref__keyboard__height_factor__extra_tall</item>
|
||||
</string-array>
|
||||
<string-array name="pref__looknfeel__height_factor__values">
|
||||
<string-array name="pref__keyboard__height_factor__values">
|
||||
<item>extra_short</item>
|
||||
<item>short</item>
|
||||
<item>mid_short</item>
|
||||
@@ -19,12 +19,12 @@
|
||||
<item>extra_tall</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__looknfeel__one_handed_mode__entries">
|
||||
<item>@string/pref__looknfeel__one_handed_mode__off</item>
|
||||
<item>@string/pref__looknfeel__one_handed_mode__right</item>
|
||||
<item>@string/pref__looknfeel__one_handed_mode__left</item>
|
||||
<string-array name="pref__keyboard__one_handed_mode__entries">
|
||||
<item>@string/pref__keyboard__one_handed_mode__off</item>
|
||||
<item>@string/pref__keyboard__one_handed_mode__right</item>
|
||||
<item>@string/pref__keyboard__one_handed_mode__left</item>
|
||||
</string-array>
|
||||
<string-array name="pref__looknfeel__one_handed_mode__values">
|
||||
<string-array name="pref__keyboard__one_handed_mode__values">
|
||||
<item>off</item>
|
||||
<item>end</item>
|
||||
<item>start</item>
|
||||
@@ -50,12 +50,58 @@
|
||||
<item>clipboard_cursor_tools</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__theme__name__entries">
|
||||
<item>Floris Light</item>
|
||||
<item>Floris Dark</item>
|
||||
<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__theme__name__values">
|
||||
<item>floris_light</item>
|
||||
<item>floris_dark</item>
|
||||
<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>
|
||||
|
||||
@@ -14,39 +14,12 @@
|
||||
<attr name="android:text" format="string|reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="KeyboardTheme">
|
||||
<attr name="keyboardViewStyle" format="reference" />
|
||||
<attr name="keyboardRowViewStyle" format="reference" />
|
||||
<declare-styleable name="KeyboardThemeBase">
|
||||
<attr name="keyboardViewStyle" format="reference"/>
|
||||
<attr name="keyboardRowViewStyle" format="reference"/>
|
||||
|
||||
<attr name="semiTransparentColor" format="color" />
|
||||
|
||||
<attr name="key_bgColor" format="color" />
|
||||
<attr name="key_bgColorPressed" format="color" />
|
||||
<attr name="key_fgColor" format="color" />
|
||||
<attr name="key_enter_fgColor" format="color" />
|
||||
|
||||
<attr name="key_popup_bgColor" format="color" />
|
||||
<attr name="key_popup_fgColor" format="color" />
|
||||
|
||||
<attr name="key_popup_extended_bgColor" format="color" />
|
||||
<attr name="key_popup_extended_bgColorActive" format="color" />
|
||||
<attr name="key_popup_extended_shadowColor" format="color" />
|
||||
|
||||
<attr name="keyboard_bgColor" format="color" />
|
||||
|
||||
<attr name="emoji_key_bgColor" format="color" />
|
||||
<attr name="emoji_key_bgColorPressed" format="color" />
|
||||
<attr name="emoji_key_fgColor" format="color" />
|
||||
|
||||
<attr name="one_handed_bgColor" format="color" />
|
||||
<attr name="one_handed_button_fgColor" format="color" />
|
||||
|
||||
<attr name="smartbar_bgColor" format="color" />
|
||||
<attr name="smartbar_fgColor" format="color" />
|
||||
<attr name="smartbar_button_bgColor" format="color" />
|
||||
<attr name="smartbar_button_bgColorPressed" format="color" />
|
||||
<attr name="smartbar_button_fgColor" format="color" />
|
||||
<attr name="smartbar_candidate_fgColor" format="color" />
|
||||
<attr name="semiTransparentColor" format="color"/>
|
||||
<attr name="inputView_bgColorFallback" format="color"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SettingsTheme">
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="inputView_baseHeight">248dp</dimen>
|
||||
<dimen name="smartbar_baseHeight">40dp</dimen>
|
||||
<dimen name="textKeyboardView_baseHeight">208dp</dimen>
|
||||
<dimen name="mediaKeyboardView_baseHeight">@dimen/inputView_baseHeight</dimen>
|
||||
|
||||
<dimen name="key_width">33dp</dimen>
|
||||
<dimen name="key_height">42dp</dimen>
|
||||
<dimen name="emoji_key_width">@dimen/key_height</dimen>
|
||||
@@ -8,10 +13,12 @@
|
||||
<dimen name="key_marginH">2dp</dimen>
|
||||
<dimen name="key_marginV">5dp</dimen>
|
||||
<dimen name="keyboard_row_marginH">@dimen/key_marginH</dimen>
|
||||
<dimen name="keyboard_preview_margin">16dp</dimen>
|
||||
|
||||
<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>
|
||||
@@ -28,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>
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
<string name="key__phone_wait">Wait</string>
|
||||
<string name="key_popup__threedots_alt">Three-dot icon. If visible, indicates that more letters can be used if longer pressed.</string>
|
||||
|
||||
<!-- One-handed strings -->
|
||||
<string name="one_handed__close_btn_content_description">Close one-handed mode.</string>
|
||||
<string name="one_handed__move_start_btn_content_description">Move keyboard to the left.</string>
|
||||
<string name="one_handed__move_end_btn_content_description">Move keyboard to the right.</string>
|
||||
|
||||
<!-- Media strings -->
|
||||
<string name="media__tab__emojis">Emojis</string>
|
||||
<string name="media__tab__emoticons">Emoticons</string>
|
||||
@@ -31,12 +36,13 @@
|
||||
<string name="settings__title">Settings</string>
|
||||
<string name="settings__menu">More options</string>
|
||||
<string name="settings__menu_about">About</string>
|
||||
<string name="settings__menu_advanced">@string/settings__advanced__title</string>
|
||||
<string name="settings__menu_help">Help & feedback</string>
|
||||
<string name="settings__navigation__home">Home</string>
|
||||
<string name="settings__navigation__keyboard">Keyboard</string>
|
||||
<string name="settings__navigation__looknfeel">Look & feel</string>
|
||||
<string name="settings__navigation__typing">Typing</string>
|
||||
<string name="settings__navigation__theme">Theme</string>
|
||||
<string name="settings__navigation__gestures">Gestures</string>
|
||||
<string name="settings__navigation__advanced">Advanced</string>
|
||||
<string name="settings__default">Default</string>
|
||||
<string name="settings__system_default">System default</string>
|
||||
|
||||
@@ -45,19 +51,87 @@
|
||||
<string name="settings__home__ime_not_selected">FlorisBoard is not selected as the default input method. Click here to resolve this issue.</string>
|
||||
<string name="settings__home__contribute">Thanks for trying out FlorisBoard! This project is still in alpha and therefore missing features. If you find any bugs or want to make a suggestion, please check out the repo on GitHub and file an issue. This helps making FlorisBoard better. Thank you!</string>
|
||||
|
||||
<string name="settings__keyboard__title">Keyboard & Text Correction</string>
|
||||
<string name="settings__keyboard__subtype_no_subtypes_configured_warning">It seems that you haven\'t configured any subtypes. As a fallback the subtype English/QWERTY will be used!</string>
|
||||
<string name="settings__keyboard__subtype_add">Add</string>
|
||||
<string name="settings__keyboard__subtype_add_title">Add subtype</string>
|
||||
<string name="settings__keyboard__subtype_apply">Apply</string>
|
||||
<string name="settings__keyboard__subtype_cancel">Cancel</string>
|
||||
<string name="settings__keyboard__subtype_delete">Delete</string>
|
||||
<string name="settings__keyboard__subtype_edit_title">Edit subtype</string>
|
||||
<string name="settings__keyboard__subtype_locale">Locale</string>
|
||||
<string name="settings__keyboard__subtype_layout">Keyboard layout</string>
|
||||
<string name="settings__keyboard__subtype_error_already_exists">This subtype already exists!</string>
|
||||
<string name="settings__localization__title">Languages & Keyboard layouts</string>
|
||||
<string name="settings__localization__subtype_no_subtypes_configured_warning">It seems that you haven\'t configured any subtypes. As a fallback the subtype English/QWERTY will be used!</string>
|
||||
<string name="settings__localization__subtype_add">Add</string>
|
||||
<string name="settings__localization__subtype_add_title">Add subtype</string>
|
||||
<string name="settings__localization__subtype_apply">Apply</string>
|
||||
<string name="settings__localization__subtype_cancel">Cancel</string>
|
||||
<string name="settings__localization__subtype_delete">Delete</string>
|
||||
<string name="settings__localization__subtype_edit_title">Edit subtype</string>
|
||||
<string name="settings__localization__subtype_locale">Locale</string>
|
||||
<string name="settings__localization__subtype_layout">Keyboard layout</string>
|
||||
<string name="settings__localization__subtype_error_already_exists">This subtype already exists!</string>
|
||||
|
||||
<string name="settings__theme__title">Keyboard theme</string>
|
||||
<string name="settings__theme__undefined">Undefined</string>
|
||||
<string name="settings__theme__preset_title">Theme</string>
|
||||
<string name="settings__theme__preset_summary">Custom (based on %s)</string>
|
||||
<string name="settings__theme__preset_dialog_selected_theme">Selected theme:</string>
|
||||
<string name="settings__theme__preset_dialog_available_themes">Available themes:</string>
|
||||
<string name="settings__theme__preset_dialog_alt_arrow_right">Arrow right</string>
|
||||
<string name="settings__theme__background">Background color</string>
|
||||
<string name="settings__theme__background_active">Background color when active</string>
|
||||
<string name="settings__theme__background_pressed">Background color when pressed</string>
|
||||
<string name="settings__theme__foreground">Foreground color</string>
|
||||
<string name="settings__theme__foreground_alt">Foreground color (alternative)</string>
|
||||
<string name="settings__theme__foreground_capslock">Foreground color (caps lock)</string>
|
||||
<string name="settings__theme__dialog_title">Select a color</string>
|
||||
<string name="settings__theme__group_window">Window & System</string>
|
||||
<string name="settings__theme__group_keyboard">Keyboard</string>
|
||||
<string name="settings__theme__group_key">Key</string>
|
||||
<string name="settings__theme__group_key_enter">Enter key</string>
|
||||
<string name="settings__theme__group_key_popup">Key popup</string>
|
||||
<string name="settings__theme__group_key_shift">Shift key</string>
|
||||
<string name="settings__theme__group_media">Media context</string>
|
||||
<string name="settings__theme__group_one_handed">One-handed</string>
|
||||
<string name="settings__theme__group_one_handed_button">One-handed button</string>
|
||||
<string name="settings__theme__group_smartbar">Smartbar</string>
|
||||
<string name="settings__theme__group_smartbar_button">Smartbar button</string>
|
||||
<string name="pref__theme__name__label">Keyboard Theme</string>
|
||||
<string name="pref__theme__colorPrimary_title">Primary color</string>
|
||||
<string name="pref__theme__colorPrimary_summary">Applied to main media tab ripple and selection highlight</string>
|
||||
<string name="pref__theme__colorPrimaryDark_title">Primary color (dark)</string>
|
||||
<string name="pref__theme__colorPrimaryDark_summary">Currently not used, reserved for future implementation</string>
|
||||
<string name="pref__theme__colorAccent_title">Accent color</string>
|
||||
<string name="pref__theme__colorAccent_summary">Applied to emoji tab ripple</string>
|
||||
<string name="pref__theme__navBarColor_title">Navigation bar color</string>
|
||||
<string name="pref__theme__navBarColor_summary">The background of the navigation bar.</string>
|
||||
<string name="pref__theme__navBarIsLight_title">Navigation bar dark foreground</string>
|
||||
<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>
|
||||
<string name="pref__keyboard__one_handed_mode__right">Right-handed mode</string>
|
||||
<string name="pref__keyboard__one_handed_mode__left">Left-handed mode</string>
|
||||
<string name="pref__keyboard__height_factor__label">Keyboard height</string>
|
||||
<string name="pref__keyboard__height_factor__extra_short">Extra-short</string>
|
||||
<string name="pref__keyboard__height_factor__short">Short</string>
|
||||
<string name="pref__keyboard__height_factor__mid_short">Mid-short</string>
|
||||
<string name="pref__keyboard__height_factor__normal">Normal</string>
|
||||
<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>
|
||||
<string name="pref__keyboard__vibration_enabled__label">Vibrate on key press</string>
|
||||
<string name="pref__keyboard__vibration_strength__label">Vibration strength on key press</string>
|
||||
<string name="pref__keyboard__popup_visible__label">PopUp Visibility</string>
|
||||
<string name="pref__keyboard__popup_visible__summary">Show popup when you press a key</string>
|
||||
<string name="pref__keyboard__long_press_delay__label">Long key press delay</string>
|
||||
|
||||
<string name="settings__typing__title">Typing experience</string>
|
||||
<string name="pref__suggestion__title">Suggestions</string>
|
||||
<string name="pref__suggestion__enabled__label">Display suggestions while you type</string>
|
||||
<string name="pref__suggestion__enabled__label">[NYI] Display suggestions while you type</string>
|
||||
<string name="pref__suggestion__enabled__summary">Will show on top of the keyboard</string>
|
||||
<string name="pref__suggestion__show_instead__label">What to show instead of suggestions</string>
|
||||
<string name="pref__suggestion__show_instead__number_row">Number row</string>
|
||||
@@ -65,35 +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__popup__title">PopUp</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="pref__popup__visible__label">PopUp Visibility</string>
|
||||
<string name="pref__popup__visible__summary">Show popup when you press a key</string>
|
||||
|
||||
<string name="settings__looknfeel__title">Look & feel</string>
|
||||
<string name="pref__looknfeel__group_layout__label">Layout</string>
|
||||
<string name="pref__looknfeel__height_factor__label">Keyboard height</string>
|
||||
<string name="pref__looknfeel__height_factor__extra_short">Extra-short</string>
|
||||
<string name="pref__looknfeel__height_factor__short">Short</string>
|
||||
<string name="pref__looknfeel__height_factor__mid_short">Mid-short</string>
|
||||
<string name="pref__looknfeel__height_factor__normal">Normal</string>
|
||||
<string name="pref__looknfeel__height_factor__mid_tall">Mid-tall</string>
|
||||
<string name="pref__looknfeel__height_factor__tall">Tall</string>
|
||||
<string name="pref__looknfeel__height_factor__extra_tall">Extra-tall</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__label">One-handed mode</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__off">Off</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__right">Right-handed mode</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__left">Left-handed mode</string>
|
||||
<string name="pref__looknfeel__group_keypress__label">Key press</string>
|
||||
<string name="pref__looknfeel__long_press_delay__label">Long key press delay</string>
|
||||
<string name="pref__looknfeel__sound_enabled__label">Sound on key press</string>
|
||||
<string name="pref__looknfeel__sound_volume__label">Sound volume on key press</string>
|
||||
<string name="pref__looknfeel__vibration_enabled__label">Vibrate on key press</string>
|
||||
<string name="pref__looknfeel__vibration_strength__label">Vibration strength on key press</string>
|
||||
<string name="pref__theme__name__label">Keyboard Theme</string>
|
||||
|
||||
<string name="settings__gestures__title">Gestures</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>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user