Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0eb5ca318b | ||
|
|
dfa9df6cd6 | ||
|
|
3f5dfbc852 | ||
|
|
59caafbf19 | ||
|
|
037a452baf | ||
|
|
ffa405f289 | ||
|
|
5d7091582f | ||
|
|
b4096f2cfb | ||
|
|
81c62f3e91 | ||
|
|
5c7db2b344 | ||
|
|
30bca99092 | ||
|
|
9a9445dab1 | ||
|
|
1fbfc32429 | ||
|
|
645b682451 | ||
|
|
63ed46ccf4 | ||
|
|
236f682622 | ||
|
|
bef69b3187 | ||
|
|
235224aefd | ||
|
|
2489872589 | ||
|
|
33a9504707 | ||
|
|
0fb7bbb034 | ||
|
|
beef54941f | ||
|
|
634ff4972d | ||
|
|
52f3477e24 | ||
|
|
1086464b09 | ||
|
|
2e4267aad4 | ||
|
|
b14fe8ad03 | ||
|
|
82d6141fb2 | ||
|
|
e0e7bcc08a |
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,15 +7,5 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Short description of your idea
|
||||
A short but clear and concise description of your idea.
|
||||
|
||||
#### Detailed description of your idea
|
||||
A clear and concise description of what you want to be added or changed. If you also have
|
||||
an idea how to implement it, please describe it here.
|
||||
|
||||
#### Alternatives to your idea
|
||||
If you have considered an alternative solution for your idea, describe it here.
|
||||
|
||||
#### Additional context
|
||||
Add any other context or screenshots about the feature request here.
|
||||
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!
|
||||
|
||||
@@ -20,13 +20,53 @@ configs because some features and structures may change, please do not add this
|
||||
kind of content yet. As FlorisBoard's state progresses and its core stabilizes,
|
||||
you will be able to add keyboard layouts.
|
||||
|
||||
## Translating FlorisBoard
|
||||
|
||||
Before starting to translate, when adding a new translation please file
|
||||
an issue stating that you want to translate FlorisBoard into a language.
|
||||
Once this gets approved you can start translating. When updating an
|
||||
already existing translation file you can just send a PR directly.
|
||||
|
||||
If you are not familiar with PRs, check out this guide:
|
||||
[https://www.gun.io/blog/how-to-github-fork-branch-and-pull-request](https://www.gun.io/blog/how-to-github-fork-branch-and-pull-request)
|
||||
|
||||
Notes for tips below:
|
||||
- Replace `<language>` with the language you want to add
|
||||
- Replace `<code>` with the ISO 639-1 code of the language you want to
|
||||
add
|
||||
([List of codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes))
|
||||
|
||||
### Tips when adding a new translation
|
||||
|
||||
- To add the new translation file, navigate to `app/src/main/res/values`
|
||||
and copy the file `strings.xml` into the folder
|
||||
`app/src/main/res/values-<code>` (you have to create this folder)
|
||||
- Translate only the phrases inside the brackets, leave the name
|
||||
attribute as it is
|
||||
E.g.: `<string name="hello_string">Hello World!</string>`
|
||||
`<string name="hello_string">Ciao mondo!</string>`
|
||||
- When finished translating, commit your changes locally, as the commit
|
||||
message use `Add <language> translation`
|
||||
- Push your change(s) and create the PR. When everything checks out, it
|
||||
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>`
|
||||
- When finished translating, commit your changes locally, as the commit
|
||||
message use `Update <language> translation`
|
||||
- Push your change(s) and create the PR. When everything checks out, it
|
||||
will get accepted.
|
||||
|
||||
## Bug reporting
|
||||
|
||||
This kind of contribution is the most important, as it tells where FlorisBoard
|
||||
has flaws and thus should be imroved to maximize stability and user experience.
|
||||
To make this process as smooth as possible, please use the premade [issue
|
||||
template](.github/ISSUE_TEMPLATE/bug_report.md) for bug reporting. This makes it
|
||||
easy for us to understand what the bug is and how to solve it.
|
||||
This kind of contribution is the most important, as it tells where
|
||||
FlorisBoard has flaws and thus should be improved to maximize stability
|
||||
and user experience. To make this process as smooth as possible, please
|
||||
use the premade [issue template](.github/ISSUE_TEMPLATE/bug_report.md)
|
||||
for bug reporting. This makes it easy for us to understand what the bug
|
||||
is and how to solve it.
|
||||
|
||||
### Capturing ADB debug logs
|
||||
|
||||
|
||||
@@ -52,9 +52,9 @@ timeline for this, but I aim for the 0.2.0 or 0.3.0 release.
|
||||
* [ ] Tablet screen support
|
||||
|
||||
### Layouts
|
||||
* [x] Latin character layout (QWERTY)
|
||||
* [x] Other character layouts (both latin and non-latin) (Currently
|
||||
QWERTZ, AZERTY, swiss and spanish are supported besides QWERTY)
|
||||
* [x] Latin character layouts (QWERTY, QWERTZ, AZERTY, Swiss, Spanish,
|
||||
Norwegian, Swedish/Finnish, Icelandic, Danish)
|
||||
* [x] Non-latin character layouts (Persian)
|
||||
* [x] Adapt to situation in app (password, url, text, etc. )
|
||||
* [x] Special character layout(s)
|
||||
* [x] Numeric layout
|
||||
@@ -84,7 +84,7 @@ timeline for this, but I aim for the 0.2.0 or 0.3.0 release.
|
||||
|
||||
### Other useful features
|
||||
* [x] One-handed mode
|
||||
* [ ] Clipboard manager (?)
|
||||
* [x] Clipboard/cursor tools
|
||||
* [ ] Floating keyboard
|
||||
* [ ] Gesture support
|
||||
* [ ] Glide typing (?)
|
||||
|
||||
@@ -10,8 +10,8 @@ android {
|
||||
applicationId "dev.patrickgold.florisboard"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 9
|
||||
versionName "0.1.0"
|
||||
versionCode 11
|
||||
versionName "0.1.2"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -34,6 +34,7 @@ dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.0'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'androidx.test:core:1.2.0'
|
||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"icelandic": "Icelandic (QWERTY)",
|
||||
"swiss_german": "Swiss German (QWERTZ)",
|
||||
"swiss_french": "Swiss French (QWERTZ)",
|
||||
"swiss_italian": "Swiss Italian (QWERTZ)"
|
||||
"swiss_italian": "Swiss Italian (QWERTZ)",
|
||||
"persian": "Persian"
|
||||
},
|
||||
"defaultSubtypes": [
|
||||
{
|
||||
@@ -174,6 +175,13 @@
|
||||
"preferredLayout": "icelandic",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
},
|
||||
{
|
||||
"id": 800,
|
||||
"languageTag": "fa-FA",
|
||||
"preferredLayout": "persian",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"ض": [
|
||||
{ "code": 1777, "label": "۱" }
|
||||
],
|
||||
"ص": [
|
||||
{ "code": 1778, "label": "۲" }
|
||||
],
|
||||
"ث": [
|
||||
{ "code": 1779, "label": "۳" }
|
||||
],
|
||||
"ق": [
|
||||
{ "code": 1780, "label": "۴" }
|
||||
],
|
||||
"ف": [
|
||||
{ "code": 1781, "label": "۵" }
|
||||
],
|
||||
"غ": [
|
||||
{ "code": 1782, "label": "۶" }
|
||||
],
|
||||
"ع": [
|
||||
{ "code": 1783, "label": "۷" }
|
||||
],
|
||||
"ه": [
|
||||
{ "code": 1784, "label": "۸" }
|
||||
],
|
||||
"خ": [
|
||||
{ "code": 1785, "label": "۹" }
|
||||
],
|
||||
"ح": [
|
||||
{ "code": 1776, "label": "۰" }
|
||||
],
|
||||
"ی": [
|
||||
{ "code": 1574, "label": "ئ" },
|
||||
{ "code": 1610, "label": "ي" }
|
||||
],
|
||||
"ا": [
|
||||
{ "code": 1649, "label": "ٱ" },
|
||||
{ "code": 1569, "label": "ء" },
|
||||
{ "code": 1570, "label": "آ" },
|
||||
{ "code": 1571, "label": "أ" },
|
||||
{ "code": 1573, "label": "إ" }
|
||||
],
|
||||
"ت": [
|
||||
{ "code": 1577, "label": "ة" }
|
||||
],
|
||||
"ک": [
|
||||
{ "code": 1603, "label": "ك" },
|
||||
{ "code": 1706, "label": "ڪ"}
|
||||
],
|
||||
"ز": [
|
||||
{ "code": 1688, "label": "ژ" }
|
||||
],
|
||||
"و": [
|
||||
{ "code": 1572, "label": "ؤ" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 1622, "label": "ٖ" },
|
||||
{ "code": 1648, "label": "ٰ" },
|
||||
{ "code": 1619, "label": "ٓ" },
|
||||
{ "code": 1615, "label": "ُ" },
|
||||
{ "code": 1616, "label": "ِ" },
|
||||
{ "code": 1614, "label": "َ" },
|
||||
{ "code": 1600, "label": "ـ" },
|
||||
{ "code": 1621, "label": "ٕ" },
|
||||
{ "code": 1618, "label": "ْ" },
|
||||
{ "code": 1617, "label": "ّ" },
|
||||
{ "code": 1612, "label": "ٌ" },
|
||||
{ "code": 1613, "label": "ٍ" },
|
||||
{ "code": 1611, "label": "ً" },
|
||||
{ "code": 1620, "label": "ٔ" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" },
|
||||
{ "code": -255, "label": ".ir"}
|
||||
]
|
||||
}
|
||||
34
app/src/main/assets/ime/text/characters/mod/persian.json
Normal file
34
app/src/main/assets/ime/text/characters/mod/persian.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"type": "characters/mod",
|
||||
"name": "persian",
|
||||
"direction": "rtl",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 0 },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
],
|
||||
[
|
||||
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
|
||||
{ "code": 64, "label": "@", "variation": "email_address" },
|
||||
{ "code": 1548, "label": "،", "variation": "normal" },
|
||||
{ "code": 47, "label": "/", "variation": "uri" },
|
||||
{ "code": -210, "label": "language_switch", "type": "system_gui", "popup": [
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
|
||||
{ "code": -100, "label": "settings", "type": "system_gui" }
|
||||
] },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui", "popup": [
|
||||
{ "code": -100, "label": "settings", "type": "system_gui" }
|
||||
] },
|
||||
{ "code": 32, "label": " " },
|
||||
{ "code": 8204, "label": "half_space", "variation": "normal" },
|
||||
{ "code": 1600, "label": "kashida", "variation": "normal" },
|
||||
{ "code": 46, "label": ".", "variation": "email_address" },
|
||||
{ "code": 46, "label": ".", "variation": "normal" },
|
||||
{ "code": 46, "label": ".", "variation": "uri" },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing", "popup": [
|
||||
{ "code": -215, "label": "toggle_one_handed_mode", "type": "system_gui" },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" }
|
||||
] }
|
||||
]
|
||||
]
|
||||
}
|
||||
45
app/src/main/assets/ime/text/characters/persian.json
Normal file
45
app/src/main/assets/ime/text/characters/persian.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "persian",
|
||||
"direction": "rtl",
|
||||
"modifier": "persian",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 1590, "label": "ض" },
|
||||
{ "code": 1589, "label": "ص" },
|
||||
{ "code": 1579, "label": "ث" },
|
||||
{ "code": 1602, "label": "ق" },
|
||||
{ "code": 1601, "label": "ف" },
|
||||
{ "code": 1594, "label": "غ" },
|
||||
{ "code": 1593, "label": "ع" },
|
||||
{ "code": 1607, "label": "ه" },
|
||||
{ "code": 1582, "label": "خ" },
|
||||
{ "code": 1581, "label": "ح" },
|
||||
{ "code": 1580, "label": "ج" }
|
||||
],
|
||||
[
|
||||
{ "code": 1588, "label": "ش" },
|
||||
{ "code": 1587, "label": "س" },
|
||||
{ "code": 1740, "label": "ی" },
|
||||
{ "code": 1576, "label": "ب" },
|
||||
{ "code": 1604, "label": "ل" },
|
||||
{ "code": 1575, "label": "ا" },
|
||||
{ "code": 1578, "label": "ت" },
|
||||
{ "code": 1606, "label": "ن" },
|
||||
{ "code": 1605, "label": "م" },
|
||||
{ "code": 1705, "label": "ک" },
|
||||
{ "code": 1711, "label": "گ" }
|
||||
],
|
||||
[
|
||||
{ "code": 1592, "label": "ظ" },
|
||||
{ "code": 1591, "label": "ط" },
|
||||
{ "code": 1586, "label": "ز" },
|
||||
{ "code": 1585, "label": "ر" },
|
||||
{ "code": 1584, "label": "ذ" },
|
||||
{ "code": 1583, "label": "د" },
|
||||
{ "code": 1662, "label": "پ" },
|
||||
{ "code": 1608, "label": "و" },
|
||||
{ "code": 1670, "label": "چ" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package dev.patrickgold.florisboard.ime.core
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
@@ -60,8 +61,10 @@ class FlorisBoard : InputMethodService() {
|
||||
val context: Context
|
||||
get() = inputView?.context ?: this
|
||||
private var inputView: InputView? = null
|
||||
private var eventListeners: MutableList<EventListener> = mutableListOf()
|
||||
|
||||
private var audioManager: AudioManager? = null
|
||||
var clipboardManager: ClipboardManager? = null
|
||||
private var vibrator: Vibrator? = null
|
||||
private val osHandler = Handler()
|
||||
|
||||
@@ -104,6 +107,11 @@ class FlorisBoard : InputMethodService() {
|
||||
fun getInstance(): FlorisBoard {
|
||||
return florisboardInstance!!
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun getInstanceOrNull(): FlorisBoard? {
|
||||
return florisboardInstance
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
@@ -128,6 +136,7 @@ class FlorisBoard : InputMethodService() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onCreate()")
|
||||
|
||||
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.initDefaultPreferences()
|
||||
@@ -141,8 +150,7 @@ class FlorisBoard : InputMethodService() {
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
|
||||
super.onCreate()
|
||||
textInputManager.onCreate()
|
||||
mediaInputManager.onCreate()
|
||||
eventListeners.forEach { it.onCreate() }
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@@ -153,8 +161,7 @@ class FlorisBoard : InputMethodService() {
|
||||
|
||||
inputView = layoutInflater.inflate(R.layout.florisboard, null) as InputView
|
||||
|
||||
textInputManager.onCreateInputView()
|
||||
mediaInputManager.onCreateInputView()
|
||||
eventListeners.forEach { it.onCreateInputView() }
|
||||
|
||||
return inputView
|
||||
}
|
||||
@@ -167,8 +174,7 @@ class FlorisBoard : InputMethodService() {
|
||||
updateSoftInputWindowLayoutParameters()
|
||||
updateOneHandedPanelVisibility()
|
||||
|
||||
textInputManager.onRegisterInputView(inputView)
|
||||
mediaInputManager.onRegisterInputView(inputView)
|
||||
eventListeners.forEach { it.onRegisterInputView(inputView) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -178,24 +184,21 @@ class FlorisBoard : InputMethodService() {
|
||||
florisboardInstance = null
|
||||
|
||||
super.onDestroy()
|
||||
textInputManager.onDestroy()
|
||||
mediaInputManager.onDestroy()
|
||||
eventListeners.forEach { it.onDestroy() }
|
||||
}
|
||||
|
||||
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
|
||||
currentInputConnection?.requestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR)
|
||||
|
||||
super.onStartInputView(info, restarting)
|
||||
textInputManager.onStartInputView(info, restarting)
|
||||
mediaInputManager.onStartInputView(info, restarting)
|
||||
eventListeners.forEach { it.onStartInputView(info, restarting) }
|
||||
}
|
||||
|
||||
override fun onFinishInputView(finishingInput: Boolean) {
|
||||
currentInputConnection?.requestCursorUpdates(0)
|
||||
|
||||
super.onFinishInputView(finishingInput)
|
||||
textInputManager.onFinishInputView(finishingInput)
|
||||
mediaInputManager.onFinishInputView(finishingInput)
|
||||
eventListeners.forEach { it.onFinishInputView(finishingInput) }
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
@@ -209,16 +212,14 @@ class FlorisBoard : InputMethodService() {
|
||||
setActiveInput(R.id.text_input)
|
||||
|
||||
super.onWindowShown()
|
||||
textInputManager.onWindowShown()
|
||||
mediaInputManager.onWindowShown()
|
||||
eventListeners.forEach { it.onWindowShown() }
|
||||
}
|
||||
|
||||
override fun onWindowHidden() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onWindowHidden()")
|
||||
|
||||
super.onWindowHidden()
|
||||
textInputManager.onWindowHidden()
|
||||
mediaInputManager.onWindowHidden()
|
||||
eventListeners.forEach { it.onWindowHidden() }
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
@@ -227,14 +228,11 @@ class FlorisBoard : InputMethodService() {
|
||||
}
|
||||
|
||||
super.onConfigurationChanged(newConfig)
|
||||
textInputManager.onConfigurationChanged(newConfig)
|
||||
mediaInputManager.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
override fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {
|
||||
super.onUpdateCursorAnchorInfo(cursorAnchorInfo)
|
||||
textInputManager.onUpdateCursorAnchorInfo(cursorAnchorInfo)
|
||||
mediaInputManager.onUpdateCursorAnchorInfo(cursorAnchorInfo)
|
||||
eventListeners.forEach { it.onUpdateCursorAnchorInfo(cursorAnchorInfo) }
|
||||
}
|
||||
|
||||
override fun onUpdateSelection(
|
||||
@@ -253,22 +251,16 @@ class FlorisBoard : InputMethodService() {
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
textInputManager.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
newSelStart,
|
||||
newSelEnd,
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
mediaInputManager.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
newSelStart,
|
||||
newSelEnd,
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
eventListeners.forEach {
|
||||
it.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
newSelStart,
|
||||
newSelEnd,
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,7 +333,7 @@ class FlorisBoard : InputMethodService() {
|
||||
fun keyPressVibrate() {
|
||||
if (prefs.looknfeel.vibrationEnabled) {
|
||||
var vibrationStrength = prefs.looknfeel.vibrationStrength
|
||||
if (vibrationStrength == 0 && prefs.looknfeel.vibrationEnabledSystem) {
|
||||
if (vibrationStrength == -1 && prefs.looknfeel.vibrationEnabledSystem) {
|
||||
vibrationStrength = 36
|
||||
}
|
||||
if (vibrationStrength > 0) {
|
||||
@@ -371,7 +363,7 @@ class FlorisBoard : InputMethodService() {
|
||||
KeyCode.ENTER -> AudioManager.FX_KEYPRESS_RETURN
|
||||
else -> AudioManager.FX_KEYPRESS_STANDARD
|
||||
}
|
||||
if (soundVolume == 0 && prefs.looknfeel.soundEnabledSystem) {
|
||||
if (soundVolume == -1 && prefs.looknfeel.soundEnabledSystem) {
|
||||
audioManager!!.playSoundEffect(effect)
|
||||
} else if (soundVolume > 0) {
|
||||
audioManager!!.playSoundEffect(effect, soundVolume / 100f)
|
||||
@@ -486,6 +478,27 @@ class FlorisBoard : InputMethodService() {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a given [listener] to the list which will receive FlorisBoard events.
|
||||
*
|
||||
* @param listener The listener object which receives the events.
|
||||
* @returns True if the listener has been added successfully, false otherwise.
|
||||
*/
|
||||
fun addEventListener(listener: EventListener): Boolean {
|
||||
return eventListeners.add(listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given [listener] from the list which will receive FlorisBoard events.
|
||||
*
|
||||
* @param listener The same listener object which was used in [addEventListener].
|
||||
* @returns True if the listener has been removed successfully, false otherwise. A false return
|
||||
* value may also indicate that the [listener] was not added previously.
|
||||
*/
|
||||
fun removeEventListener(listener: EventListener): Boolean {
|
||||
return eventListeners.remove(listener)
|
||||
}
|
||||
|
||||
interface EventListener {
|
||||
fun onCreate() {}
|
||||
fun onCreateInputView() {}
|
||||
@@ -498,8 +511,6 @@ class FlorisBoard : InputMethodService() {
|
||||
fun onWindowShown() {}
|
||||
fun onWindowHidden() {}
|
||||
|
||||
fun onConfigurationChanged(newConfig: Configuration) {}
|
||||
|
||||
fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {}
|
||||
fun onUpdateSelection(
|
||||
oldSelStart: Int,
|
||||
|
||||
@@ -41,6 +41,7 @@ class PrefHelper(
|
||||
val keyboard = Keyboard(this)
|
||||
val looknfeel = Looknfeel(this)
|
||||
val suggestion = Suggestion(this)
|
||||
val popup = PopUp(this)
|
||||
val theme = Theme(this)
|
||||
|
||||
/**
|
||||
@@ -240,14 +241,14 @@ class PrefHelper(
|
||||
private set
|
||||
var soundEnabledSystem: Boolean = false
|
||||
var soundVolume: Int = 0
|
||||
get() = prefHelper.getPref(SOUND_VOLUME, 0)
|
||||
get() = prefHelper.getPref(SOUND_VOLUME, -1)
|
||||
private set
|
||||
var vibrationEnabled: Boolean = false
|
||||
get() = prefHelper.getPref(VIBRATION_ENABLED, true)
|
||||
private set
|
||||
var vibrationEnabledSystem: Boolean = false
|
||||
var vibrationStrength: Int = 0
|
||||
get() = prefHelper.getPref(VIBRATION_STRENGTH, 0)
|
||||
get() = prefHelper.getPref(VIBRATION_STRENGTH, -1)
|
||||
private set
|
||||
}
|
||||
|
||||
@@ -257,17 +258,34 @@ class PrefHelper(
|
||||
class Suggestion(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ENABLED = "suggestion__enabled"
|
||||
const val SHOW_INSTEAD = "suggestion__show_instead"
|
||||
const val USE_PREV_WORDS = "suggestion__use_prev_words"
|
||||
}
|
||||
|
||||
var enabled: Boolean = false
|
||||
get() = prefHelper.getPref(ENABLED, true)
|
||||
private set
|
||||
var showInstead: String = ""
|
||||
get() = prefHelper.getPref(SHOW_INSTEAD, "number_row")
|
||||
private set
|
||||
var usePrevWords: Boolean = false
|
||||
get() = prefHelper.getPref(USE_PREV_WORDS, true)
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.editing
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Typeface
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
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.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.util.getColorFromAttr
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* View class for managing and rendering an editing key.
|
||||
*/
|
||||
class EditingKeyView : AppCompatImageButton {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val data: KeyData
|
||||
private var isKeyPressed: Boolean = false
|
||||
private var osTimer: Timer? = null
|
||||
|
||||
private var label: String? = null
|
||||
private var labelPaint: Paint = Paint().apply {
|
||||
alpha = 255
|
||||
color = 0
|
||||
isAntiAlias = true
|
||||
isFakeBoldText = false
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = Button(context).textSize
|
||||
typeface = Typeface.DEFAULT
|
||||
}
|
||||
|
||||
var isHighlighted: Boolean = false
|
||||
set(value) { field = value; invalidate() }
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, R.style.TextEditingButton)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
val code = when (id) {
|
||||
R.id.arrow_down -> KeyCode.ARROW_DOWN
|
||||
R.id.arrow_left -> KeyCode.ARROW_LEFT
|
||||
R.id.arrow_right -> KeyCode.ARROW_RIGHT
|
||||
R.id.arrow_up -> KeyCode.ARROW_UP
|
||||
R.id.backspace -> KeyCode.DELETE
|
||||
R.id.clipboard_copy -> KeyCode.CLIPBOARD_COPY
|
||||
R.id.clipboard_cut -> KeyCode.CLIPBOARD_CUT
|
||||
R.id.clipboard_paste -> KeyCode.CLIPBOARD_PASTE
|
||||
R.id.move_home -> KeyCode.MOVE_HOME
|
||||
R.id.move_end -> KeyCode.MOVE_END
|
||||
R.id.select -> KeyCode.CLIPBOARD_SELECT
|
||||
R.id.select_all -> KeyCode.CLIPBOARD_SELECT_ALL
|
||||
else -> 0
|
||||
}
|
||||
data = KeyData(code)
|
||||
context.obtainStyledAttributes(attrs, R.styleable.EditingKeyView).apply {
|
||||
label = getString(R.styleable.EditingKeyView_android_text)
|
||||
recycle()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||
if (!isEnabled || event == null) {
|
||||
return false
|
||||
}
|
||||
super.onTouchEvent(event)
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
isKeyPressed = true
|
||||
florisboard?.keyPressVibrate()
|
||||
florisboard?.keyPressSound(data)
|
||||
when (data.code) {
|
||||
KeyCode.ARROW_DOWN,
|
||||
KeyCode.ARROW_LEFT,
|
||||
KeyCode.ARROW_RIGHT,
|
||||
KeyCode.ARROW_UP,
|
||||
KeyCode.DELETE -> {
|
||||
osTimer = Timer()
|
||||
osTimer?.scheduleAtFixedRate(object : TimerTask() {
|
||||
override fun run() {
|
||||
florisboard?.textInputManager?.sendKeyPress(data)
|
||||
if (!isKeyPressed) {
|
||||
osTimer?.cancel()
|
||||
osTimer = null
|
||||
}
|
||||
}
|
||||
}, 500, 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
isKeyPressed = false
|
||||
osTimer?.cancel()
|
||||
osTimer = null
|
||||
if (event.actionMasked != MotionEvent.ACTION_CANCEL) {
|
||||
florisboard?.textInputManager?.sendKeyPress(data)
|
||||
}
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the key label / drawable.
|
||||
*/
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
canvas ?: return
|
||||
|
||||
imageTintList = ColorStateList.valueOf(getColorFromAttr(context, when {
|
||||
isEnabled -> R.attr.key_fgColor
|
||||
else -> android.R.attr.colorButtonNormal
|
||||
}))
|
||||
|
||||
// Draw label
|
||||
val label = label
|
||||
if (label != null) {
|
||||
labelPaint.color = if (isHighlighted && isEnabled) {
|
||||
getColorFromAttr(context, R.attr.colorPrimary)
|
||||
} else if (!isEnabled) {
|
||||
getColorFromAttr(context, android.R.attr.colorButtonNormal)
|
||||
} else {
|
||||
getColorFromAttr(context, R.attr.key_fgColor)
|
||||
}
|
||||
val isPortrait =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
if (!isPortrait) {
|
||||
labelPaint.textSize *= 0.9f
|
||||
}
|
||||
val centerX = measuredWidth / 2.0f
|
||||
val centerY = measuredHeight / 2.0f + (labelPaint.textSize - labelPaint.descent()) / 2
|
||||
if (label.contains("\n")) {
|
||||
// Even if more lines may be existing only the first 2 are shown
|
||||
val labelLines = label.split("\n")
|
||||
canvas.drawText(labelLines[0], centerX, centerY * 0.70f, labelPaint)
|
||||
canvas.drawText(labelLines[1], centerX, centerY * 1.30f, labelPaint)
|
||||
} else {
|
||||
canvas.drawText(label, centerX, centerY, labelPaint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.editing
|
||||
|
||||
import android.content.Context
|
||||
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
|
||||
|
||||
/**
|
||||
* 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 var arrowUpKey: EditingKeyView? = null
|
||||
private var arrowDownKey: EditingKeyView? = null
|
||||
private var selectKey: EditingKeyView? = null
|
||||
private var selectAllKey: EditingKeyView? = null
|
||||
private var cutKey: EditingKeyView? = null
|
||||
private var copyKey: EditingKeyView? = null
|
||||
private var pasteKey: EditingKeyView? = null
|
||||
|
||||
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()
|
||||
|
||||
arrowUpKey = findViewById(R.id.arrow_up)
|
||||
arrowDownKey = findViewById(R.id.arrow_down)
|
||||
selectKey = findViewById(R.id.select)
|
||||
selectAllKey = findViewById(R.id.select_all)
|
||||
cutKey = findViewById(R.id.clipboard_cut)
|
||||
copyKey = findViewById(R.id.clipboard_copy)
|
||||
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
|
||||
arrowUpKey?.isEnabled = !(isSelectionActive || isSelectionMode)
|
||||
arrowDownKey?.isEnabled = !(isSelectionActive || isSelectionMode)
|
||||
selectKey?.isHighlighted = isSelectionActive || isSelectionMode
|
||||
selectAllKey?.visibility = when {
|
||||
isSelectionActive -> View.GONE
|
||||
else -> View.VISIBLE
|
||||
}
|
||||
cutKey?.visibility = when {
|
||||
isSelectionActive -> View.VISIBLE
|
||||
else -> View.GONE
|
||||
}
|
||||
copyKey?.isEnabled = isSelectionActive
|
||||
pasteKey?.isEnabled = florisboard?.clipboardManager?.hasPrimaryClip() ?: false
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,10 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
florisboard.addEventListener(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new input view has been registered. Used to initialize all media-relevant
|
||||
* views and layouts.
|
||||
@@ -125,6 +129,7 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onDestroy()")
|
||||
|
||||
cancel()
|
||||
florisboard.removeEventListener(this)
|
||||
instance = null
|
||||
}
|
||||
|
||||
|
||||
@@ -169,20 +169,10 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a preview popup for the passed [keyView]. Ignores show requests for key views which
|
||||
* key code is equal to or less than [KeyCode.SPACE]. KeyViews with a code defined in
|
||||
* [exceptionsForKeyCodes] will only shadow-calculating the size of the key popup, as these
|
||||
* sizes are needed for the extended popup. No popup will be shown to the user in this case.
|
||||
*
|
||||
* @param keyView Reference to the keyView currently controlling the popup.
|
||||
* Calculates all attributes required by both the normal and the extended popup, regardless of
|
||||
* the passed [keyView]'s code.
|
||||
*/
|
||||
fun show(keyView: T_KV) {
|
||||
if (keyView is KeyView && keyView.data.code <= KeyCode.SPACE
|
||||
&& !exceptionsForKeyCodes.contains(keyView.data.code)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Update keyPopupWidth and keyPopupHeight
|
||||
private fun calc(keyView: T_KV) {
|
||||
if (keyboardView is KeyboardView) {
|
||||
when (keyboardView.resources.configuration.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> {
|
||||
@@ -199,11 +189,21 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
keyPopupHeight = (keyView.measuredHeight * 2.5f).toInt()
|
||||
}
|
||||
keyPopupDiffX = (keyView.measuredWidth - keyPopupWidth) / 2
|
||||
// Calculating is done, so exit show() here if this key view is a special one.
|
||||
if (keyView is KeyView && exceptionsForKeyCodes.contains(keyView.data.code)) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a preview popup for the passed [keyView]. Ignores show requests for key views which
|
||||
* key code is equal to or less than [KeyCode.SPACE].
|
||||
*
|
||||
* @param keyView Reference to the keyView currently controlling the popup.
|
||||
*/
|
||||
fun show(keyView: T_KV) {
|
||||
if (keyView is KeyView && keyView.data.code <= KeyCode.SPACE) {
|
||||
return
|
||||
}
|
||||
|
||||
calc(keyView)
|
||||
|
||||
val keyPopupX = keyPopupDiffX
|
||||
val keyPopupY = -keyPopupHeight
|
||||
if (window.isShowing) {
|
||||
@@ -256,6 +256,10 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
return
|
||||
}
|
||||
|
||||
if (!isShowingPopup) {
|
||||
calc(keyView)
|
||||
}
|
||||
|
||||
// Anchor left if keyView is in left half of keyboardView, else anchor right
|
||||
if (keyView is KeyView) {
|
||||
anchorLeft = keyView.x < keyboardView.measuredWidth / 2
|
||||
|
||||
@@ -16,15 +16,13 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.text.InputType
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.view.inputmethod.CursorAnchorInfo
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.ExtractedTextRequest
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.view.inputmethod.*
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ViewFlipper
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
@@ -32,6 +30,7 @@ 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.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyType
|
||||
@@ -62,6 +61,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
|
||||
private var activeKeyboardMode: KeyboardMode? = null
|
||||
private val keyboardViews = EnumMap<KeyboardMode, KeyboardView>(KeyboardMode::class.java)
|
||||
private var editingKeyboardView: EditingKeyboardView? = null
|
||||
private val osHandler = Handler()
|
||||
private var textViewFlipper: ViewFlipper? = null
|
||||
var textViewGroup: LinearLayout? = null
|
||||
@@ -85,7 +85,16 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
private var composingTextStart: Int? = null
|
||||
private var cursorPos: Int = 0
|
||||
private var isComposingEnabled: Boolean = false
|
||||
private var isTextSelected: Boolean = false
|
||||
var isManualSelectionMode: Boolean = false
|
||||
private var isManualSelectionModeLeft: Boolean = false
|
||||
private var isManualSelectionModeRight: Boolean = false
|
||||
val isTextSelected: Boolean
|
||||
get() = selectionEnd - selectionStart != 0
|
||||
private var lastCursorAnchorInfo: CursorAnchorInfo? = null
|
||||
private var selectionStart: Int = 0
|
||||
private val selectionStartMin: Int = 0
|
||||
private var selectionEnd: Int = 0
|
||||
private var selectionEndMax: Int = 0
|
||||
|
||||
companion object {
|
||||
private var instance: TextInputManager? = null
|
||||
@@ -99,6 +108,10 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
florisboard.addEventListener(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-UI-related setup + preloading of all required computed layouts (asynchronous in the
|
||||
* background).
|
||||
@@ -142,6 +155,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
launch(Dispatchers.Default) {
|
||||
textViewGroup = inputView.findViewById(R.id.text_input)
|
||||
textViewFlipper = inputView.findViewById(R.id.text_input_view_flipper)
|
||||
editingKeyboardView = inputView.findViewById(R.id.editing)
|
||||
|
||||
val activeKeyboardMode = getActiveKeyboardMode()
|
||||
addKeyboardView(activeKeyboardMode)
|
||||
@@ -166,6 +180,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
osHandler.removeCallbacksAndMessages(null)
|
||||
layoutManager.onDestroy()
|
||||
smartbarManager.onDestroy()
|
||||
florisboard.removeEventListener(this)
|
||||
instance = null
|
||||
}
|
||||
|
||||
@@ -216,7 +231,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
KeyboardMode.NUMERIC,
|
||||
KeyboardMode.PHONE,
|
||||
KeyboardMode.PHONE2 -> false
|
||||
else -> keyVariation != KeyVariation.PASSWORD
|
||||
else -> keyVariation != KeyVariation.PASSWORD && florisboard.prefs.suggestion.enabled
|
||||
}
|
||||
updateCapsState()
|
||||
resetComposingText()
|
||||
@@ -233,6 +248,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
|
||||
override fun onWindowShown() {
|
||||
keyboardViews[KeyboardMode.CHARACTERS]?.updateVisibility()
|
||||
smartbarManager.onWindowShown()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,16 +261,21 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets [activeKeyboardMode] and updates the [SmartbarManager.activeContainerId].
|
||||
* Sets [activeKeyboardMode] and updates the [SmartbarManager.isQuickActionsVisible].
|
||||
*/
|
||||
private fun setActiveKeyboardMode(mode: KeyboardMode) {
|
||||
textViewFlipper?.displayedChild =
|
||||
textViewFlipper?.indexOfChild(keyboardViews[mode]) ?: 0
|
||||
fun setActiveKeyboardMode(mode: KeyboardMode) {
|
||||
textViewFlipper?.displayedChild = textViewFlipper?.indexOfChild(when (mode) {
|
||||
KeyboardMode.EDITING -> editingKeyboardView
|
||||
else -> keyboardViews[mode]
|
||||
}) ?: 0
|
||||
keyboardViews[mode]?.updateVisibility()
|
||||
keyboardViews[mode]?.requestLayout()
|
||||
keyboardViews[mode]?.requestLayoutAllKeys()
|
||||
activeKeyboardMode = mode
|
||||
smartbarManager.activeContainerId = smartbarManager.getPreferredContainerId()
|
||||
smartbarManager.isQuickActionsVisible = false
|
||||
isManualSelectionMode = false
|
||||
isManualSelectionModeLeft = false
|
||||
isManualSelectionModeRight = false
|
||||
}
|
||||
|
||||
override fun onSubtypeChanged(newSubtype: Subtype) {
|
||||
@@ -271,15 +292,24 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
*/
|
||||
override fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {
|
||||
cursorAnchorInfo ?: return
|
||||
lastCursorAnchorInfo = cursorAnchorInfo
|
||||
|
||||
val ic = florisboard.currentInputConnection
|
||||
|
||||
val isNewSelectionInBoundsOfOld =
|
||||
cursorAnchorInfo.selectionStart >= (selectionStart - 1) &&
|
||||
cursorAnchorInfo.selectionStart <= (selectionStart + 1) &&
|
||||
cursorAnchorInfo.selectionEnd >= (selectionEnd - 1) &&
|
||||
cursorAnchorInfo.selectionEnd <= (selectionEnd + 1)
|
||||
selectionStart = cursorAnchorInfo.selectionStart
|
||||
selectionEnd = cursorAnchorInfo.selectionEnd
|
||||
val inputText =
|
||||
(ic?.getExtractedText(ExtractedTextRequest(), 0)?.text ?: "").toString()
|
||||
selectionEndMax = inputText.length
|
||||
if (isComposingEnabled) {
|
||||
if (cursorAnchorInfo.selectionEnd - cursorAnchorInfo.selectionStart == 0) {
|
||||
if (!isTextSelected) {
|
||||
val newCursorPos = cursorAnchorInfo.selectionStart
|
||||
val prevComposingText = (cursorAnchorInfo.composingText ?: "").toString()
|
||||
val inputText =
|
||||
(ic?.getExtractedText(ExtractedTextRequest(), 0)?.text ?: "").toString()
|
||||
setComposingTextBasedOnInput(inputText, newCursorPos)
|
||||
if ((newCursorPos == cursorPos) && (composingText == prevComposingText)) {
|
||||
// Ignore this, as nothing has changed
|
||||
@@ -299,8 +329,13 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
}
|
||||
smartbarManager.generateCandidatesFromComposing(composingText)
|
||||
}
|
||||
isTextSelected = cursorAnchorInfo.selectionEnd - cursorAnchorInfo.selectionStart != 0
|
||||
if (!isNewSelectionInBoundsOfOld) {
|
||||
isManualSelectionMode = false
|
||||
isManualSelectionModeLeft = false
|
||||
isManualSelectionModeRight = false
|
||||
}
|
||||
updateCapsState()
|
||||
smartbarManager.onUpdateCursorAnchorInfo(cursorAnchorInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,6 +434,36 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given [keyCode] as a [KeyEvent.ACTION_DOWN].
|
||||
*
|
||||
* @param ic The input connection on which this operation should be performed.
|
||||
* @param keyCode The key code to send, use a key code defined in Android's [KeyEvent], not in
|
||||
* [KeyCode] or this call may send a weird character, as this key codes do not match!!
|
||||
*/
|
||||
private fun sendSystemKeyEvent(ic: InputConnection?, keyCode: Int) {
|
||||
ic?.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, keyCode))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given [keyCode] as a [KeyEvent.ACTION_DOWN] with ALT pressed.
|
||||
*
|
||||
* @param ic The input connection on which this operation should be performed.
|
||||
* @param keyCode The key code to send, use a key code defined in Android's [KeyEvent], not in
|
||||
* [KeyCode] or this call may send a weird character, as this key codes do not match!!
|
||||
*/
|
||||
private fun sendSystemKeyEventAlt(ic: InputConnection?, keyCode: Int) {
|
||||
ic?.sendKeyEvent(
|
||||
KeyEvent(
|
||||
0,
|
||||
1,
|
||||
KeyEvent.ACTION_DOWN, keyCode,
|
||||
0,
|
||||
KeyEvent.META_ALT_LEFT_ON
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.DELETE] event.
|
||||
*/
|
||||
@@ -406,12 +471,10 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
val ic = florisboard.currentInputConnection
|
||||
ic?.beginBatchEdit()
|
||||
resetComposingText()
|
||||
ic?.sendKeyEvent(
|
||||
KeyEvent(
|
||||
KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_DEL
|
||||
)
|
||||
)
|
||||
isManualSelectionMode = false
|
||||
isManualSelectionModeLeft = false
|
||||
isManualSelectionModeRight = false
|
||||
sendSystemKeyEvent(ic, KeyEvent.KEYCODE_DEL)
|
||||
ic?.endBatchEdit()
|
||||
}
|
||||
|
||||
@@ -424,12 +487,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
resetComposingText()
|
||||
val action = florisboard.currentInputEditorInfo?.imeOptions ?: 0
|
||||
if (action and EditorInfo.IME_FLAG_NO_ENTER_ACTION > 0) {
|
||||
ic?.sendKeyEvent(
|
||||
KeyEvent(
|
||||
KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_ENTER
|
||||
)
|
||||
)
|
||||
sendSystemKeyEvent(ic, KeyEvent.KEYCODE_ENTER)
|
||||
} else {
|
||||
when (action and EditorInfo.IME_MASK_ACTION) {
|
||||
EditorInfo.IME_ACTION_DONE,
|
||||
@@ -440,14 +498,7 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
EditorInfo.IME_ACTION_SEND -> {
|
||||
ic?.performEditorAction(action)
|
||||
}
|
||||
else -> {
|
||||
ic?.sendKeyEvent(
|
||||
KeyEvent(
|
||||
KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_ENTER
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> sendSystemKeyEvent(ic, KeyEvent.KEYCODE_ENTER)
|
||||
}
|
||||
}
|
||||
ic?.endBatchEdit()
|
||||
@@ -498,6 +549,194 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
ic?.commitText(KeyCode.SPACE.toChar().toString(), 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles [KeyCode] arrow and move events, behaves differently depending on text selection.
|
||||
*/
|
||||
private fun handleArrow(code: Int) {
|
||||
val ic = florisboard.currentInputConnection
|
||||
resetComposingText()
|
||||
if (isTextSelected && isManualSelectionMode) {
|
||||
// Text is selected and it is manual selection -> Expand selection depending on started
|
||||
// direction.
|
||||
when (code) {
|
||||
KeyCode.ARROW_DOWN -> {}
|
||||
KeyCode.ARROW_LEFT -> {
|
||||
if (isManualSelectionModeLeft) {
|
||||
ic?.setSelection(
|
||||
(selectionStart - 1).coerceAtLeast(selectionStartMin),
|
||||
selectionEnd
|
||||
)
|
||||
} else {
|
||||
ic?.setSelection(selectionStart, selectionEnd - 1)
|
||||
}
|
||||
}
|
||||
KeyCode.ARROW_RIGHT -> {
|
||||
if (isManualSelectionModeRight) {
|
||||
ic?.setSelection(
|
||||
selectionStart,
|
||||
(selectionEnd + 1).coerceAtMost(selectionEndMax)
|
||||
)
|
||||
} else {
|
||||
ic?.setSelection(selectionStart + 1, selectionEnd)
|
||||
}
|
||||
}
|
||||
KeyCode.ARROW_UP -> {}
|
||||
KeyCode.MOVE_HOME -> {
|
||||
if (isManualSelectionModeLeft) {
|
||||
ic?.setSelection(selectionStartMin, selectionEnd)
|
||||
} else {
|
||||
ic?.setSelection(selectionStartMin, selectionStart)
|
||||
}
|
||||
}
|
||||
KeyCode.MOVE_END -> {
|
||||
if (isManualSelectionModeRight) {
|
||||
ic?.setSelection(selectionStart, selectionEndMax)
|
||||
} else {
|
||||
ic?.setSelection(selectionEnd, selectionEndMax)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isTextSelected && !isManualSelectionMode) {
|
||||
// Text is selected but no manual selection mode -> arrows behave as if selection was
|
||||
// started in manual left mode
|
||||
when (code) {
|
||||
KeyCode.ARROW_DOWN -> {}
|
||||
KeyCode.ARROW_LEFT -> {
|
||||
ic?.setSelection(selectionStart, selectionEnd - 1)
|
||||
}
|
||||
KeyCode.ARROW_RIGHT -> {
|
||||
ic?.setSelection(
|
||||
selectionStart,
|
||||
(selectionEnd + 1).coerceAtMost(selectionEndMax)
|
||||
)
|
||||
}
|
||||
KeyCode.ARROW_UP -> {}
|
||||
KeyCode.MOVE_HOME -> {
|
||||
ic?.setSelection(selectionStartMin, selectionStart)
|
||||
}
|
||||
KeyCode.MOVE_END -> {
|
||||
ic?.setSelection(selectionStart, selectionEndMax)
|
||||
}
|
||||
}
|
||||
} else if (!isTextSelected && isManualSelectionMode) {
|
||||
// No text is selected but manual selection mode is active, user wants to start a new
|
||||
// selection. Must set manual selection direction.
|
||||
when (code) {
|
||||
KeyCode.ARROW_DOWN -> {}
|
||||
KeyCode.ARROW_LEFT -> {
|
||||
ic?.setSelection(
|
||||
(selectionStart - 1).coerceAtLeast(selectionStartMin),
|
||||
selectionStart
|
||||
)
|
||||
isManualSelectionModeLeft = true
|
||||
isManualSelectionModeRight = false
|
||||
}
|
||||
KeyCode.ARROW_RIGHT -> {
|
||||
ic?.setSelection(
|
||||
selectionEnd,
|
||||
(selectionEnd + 1).coerceAtMost(selectionEndMax)
|
||||
)
|
||||
isManualSelectionModeLeft = false
|
||||
isManualSelectionModeRight = true
|
||||
}
|
||||
KeyCode.ARROW_UP -> {}
|
||||
KeyCode.MOVE_HOME -> {
|
||||
ic?.setSelection(selectionStartMin, selectionStart)
|
||||
isManualSelectionModeLeft = true
|
||||
isManualSelectionModeRight = false
|
||||
}
|
||||
KeyCode.MOVE_END -> {
|
||||
ic?.setSelection(selectionEnd, selectionEndMax)
|
||||
isManualSelectionModeLeft = false
|
||||
isManualSelectionModeRight = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No selection and no manual selection mode -> move cursor around
|
||||
when (code) {
|
||||
KeyCode.ARROW_DOWN -> sendSystemKeyEvent(ic, KeyEvent.KEYCODE_DPAD_DOWN)
|
||||
KeyCode.ARROW_LEFT -> sendSystemKeyEvent(ic, KeyEvent.KEYCODE_DPAD_LEFT)
|
||||
KeyCode.ARROW_RIGHT -> sendSystemKeyEvent(ic, KeyEvent.KEYCODE_DPAD_RIGHT)
|
||||
KeyCode.ARROW_UP -> sendSystemKeyEvent(ic, KeyEvent.KEYCODE_DPAD_UP)
|
||||
KeyCode.MOVE_HOME -> sendSystemKeyEventAlt(ic, KeyEvent.KEYCODE_DPAD_UP)
|
||||
KeyCode.MOVE_END -> sendSystemKeyEventAlt(ic, KeyEvent.KEYCODE_DPAD_DOWN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.CLIPBOARD_CUT] event.
|
||||
* TODO: handle other data than text too, e.g. Uri, Intent, ...
|
||||
*/
|
||||
private fun handleClipboardCut() {
|
||||
val ic = florisboard.currentInputConnection
|
||||
val selectedText = ic?.getSelectedText(0)
|
||||
if (selectedText != null) {
|
||||
florisboard.clipboardManager
|
||||
?.setPrimaryClip(ClipData.newPlainText(selectedText, selectedText))
|
||||
}
|
||||
resetComposingText()
|
||||
ic?.commitText("", 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.CLIPBOARD_COPY] event.
|
||||
* TODO: handle other data than text too, e.g. Uri, Intent, ...
|
||||
*/
|
||||
private fun handleClipboardCopy() {
|
||||
val ic = florisboard.currentInputConnection
|
||||
val selectedText = ic?.getSelectedText(0)
|
||||
if (selectedText != null) {
|
||||
florisboard.clipboardManager
|
||||
?.setPrimaryClip(ClipData.newPlainText(selectedText, selectedText))
|
||||
}
|
||||
resetComposingText()
|
||||
ic?.setSelection(selectionEnd, selectionEnd)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.CLIPBOARD_PASTE] event.
|
||||
* TODO: handle other data than text too, e.g. Uri, Intent, ...
|
||||
*/
|
||||
private fun handleClipboardPaste() {
|
||||
val ic = florisboard.currentInputConnection
|
||||
val item = florisboard.clipboardManager?.primaryClip?.getItemAt(0)
|
||||
val pasteText = item?.text
|
||||
if (pasteText != null) {
|
||||
resetComposingText()
|
||||
ic?.commitText(pasteText, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.CLIPBOARD_SELECT] event.
|
||||
*/
|
||||
private fun handleClipboardSelect() {
|
||||
val ic = florisboard.currentInputConnection
|
||||
resetComposingText()
|
||||
if (isTextSelected) {
|
||||
if (isManualSelectionMode && isManualSelectionModeLeft) {
|
||||
ic?.setSelection(selectionStart, selectionStart)
|
||||
} else {
|
||||
ic?.setSelection(selectionEnd, selectionEnd)
|
||||
}
|
||||
isManualSelectionMode = false
|
||||
} else {
|
||||
isManualSelectionMode = !isManualSelectionMode
|
||||
// Must recall to update UI properly
|
||||
florisboard.onUpdateCursorAnchorInfo(lastCursorAnchorInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.CLIPBOARD_SELECT_ALL] event.
|
||||
*/
|
||||
private fun handleClipboardSelectAll() {
|
||||
val ic = florisboard.currentInputConnection
|
||||
resetComposingText()
|
||||
ic?.setSelection(selectionStartMin, selectionEndMax)
|
||||
}
|
||||
|
||||
/**
|
||||
* Main logic point for sending a key press. Different actions may occur depending on the given
|
||||
* [KeyData]. This method handles all key press send events, which are text based. For media
|
||||
@@ -509,6 +748,17 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
val ic = florisboard.currentInputConnection
|
||||
|
||||
when (keyData.code) {
|
||||
KeyCode.ARROW_DOWN,
|
||||
KeyCode.ARROW_LEFT,
|
||||
KeyCode.ARROW_RIGHT,
|
||||
KeyCode.ARROW_UP,
|
||||
KeyCode.MOVE_HOME,
|
||||
KeyCode.MOVE_END -> handleArrow(keyData.code)
|
||||
KeyCode.CLIPBOARD_CUT -> handleClipboardCut()
|
||||
KeyCode.CLIPBOARD_COPY -> handleClipboardCopy()
|
||||
KeyCode.CLIPBOARD_PASTE -> handleClipboardPaste()
|
||||
KeyCode.CLIPBOARD_SELECT -> handleClipboardSelect()
|
||||
KeyCode.CLIPBOARD_SELECT_ALL -> handleClipboardSelectAll()
|
||||
KeyCode.DELETE -> handleDelete()
|
||||
KeyCode.ENTER -> handleEnter()
|
||||
KeyCode.LANGUAGE_SWITCH -> florisboard.switchToNextSubtype()
|
||||
|
||||
@@ -69,4 +69,7 @@ object KeyCode {
|
||||
const val SWITCH_TO_CLIPBOARD_CONTEXT = -214
|
||||
const val TOGGLE_ONE_HANDED_MODE = -215
|
||||
const val URI_COMPONENT_TLD = -255
|
||||
|
||||
const val KESHIDA = 1600
|
||||
const val HALF_SPACE = 8204
|
||||
}
|
||||
|
||||
@@ -174,7 +174,11 @@ class KeyView(
|
||||
event ?: return false
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
keyboardView.popupManager.show(this)
|
||||
florisboard?.prefs?.popup?.let {
|
||||
if (it.enabled){
|
||||
keyboardView.popupManager.show(this)
|
||||
}
|
||||
}
|
||||
isKeyPressed = true
|
||||
florisboard?.keyPressVibrate()
|
||||
florisboard?.keyPressSound(data)
|
||||
@@ -426,7 +430,7 @@ class KeyView(
|
||||
updateKeyPressedBackground()
|
||||
|
||||
if (data.type == KeyType.CHARACTER && data.code != KeyCode.SPACE
|
||||
|| data.type == KeyType.NUMERIC
|
||||
&& data.code != KeyCode.HALF_SPACE && data.code != KeyCode.KESHIDA || data.type == KeyType.NUMERIC
|
||||
) {
|
||||
label = getComputedLetter()
|
||||
} else {
|
||||
@@ -513,6 +517,12 @@ class KeyView(
|
||||
KeyCode.VIEW_SYMBOLS2 -> {
|
||||
label = resources.getString(R.string.key__view_symbols2)
|
||||
}
|
||||
KeyCode.HALF_SPACE -> {
|
||||
label = resources.getString(R.string.key__view_half_space)
|
||||
}
|
||||
KeyCode.KESHIDA -> {
|
||||
label = resources.getString(R.string.key__view_keshida)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package dev.patrickgold.florisboard.ime.text.keyboard
|
||||
|
||||
enum class KeyboardMode {
|
||||
CHARACTERS,
|
||||
EDITING,
|
||||
SYMBOLS,
|
||||
SYMBOLS2,
|
||||
NUMERIC,
|
||||
|
||||
@@ -24,6 +24,7 @@ data class LayoutData(
|
||||
val type: LayoutType,
|
||||
val name: String,
|
||||
val direction: String,
|
||||
val modifier: String?,
|
||||
val arrangement: LayoutDataArrangement = listOf()
|
||||
) {
|
||||
private fun getComputedLayoutDataArrangement(): ComputedLayoutDataArrangement {
|
||||
|
||||
@@ -120,7 +120,12 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
val computedArrangement: ComputedLayoutDataArrangement = mutableListOf()
|
||||
|
||||
val mainLayout = loadLayout(main)
|
||||
val modifierLayout = loadLayout(modifier)
|
||||
val modifierToLoad = if (mainLayout?.modifier != null) {
|
||||
LTN(LayoutType.CHARACTERS_MOD, mainLayout.modifier)
|
||||
} else {
|
||||
modifier
|
||||
}
|
||||
val modifierLayout = loadLayout(modifierToLoad)
|
||||
val extensionLayout = loadLayout(extension)
|
||||
|
||||
if (extensionLayout != null) {
|
||||
@@ -139,14 +144,12 @@ class LayoutManager(private val context: Context) : CoroutineScope by MainScope(
|
||||
// merge main and mod here
|
||||
val mergedRow = mutableListOf<KeyData>()
|
||||
val firstModRow = modifierLayout.arrangement.firstOrNull()
|
||||
val firstModKey = firstModRow?.firstOrNull()
|
||||
if (firstModKey != null) {
|
||||
mergedRow.add(firstModKey)
|
||||
}
|
||||
mergedRow.addAll(mainRow)
|
||||
val lastModKey = firstModRow?.lastOrNull()
|
||||
if (lastModKey != null && firstModKey != lastModKey) {
|
||||
mergedRow.add(lastModKey)
|
||||
for (modKey in (firstModRow ?: listOf())) {
|
||||
if (modKey.code == 0) {
|
||||
mergedRow.addAll(mainRow)
|
||||
} else {
|
||||
mergedRow.add(modKey)
|
||||
}
|
||||
}
|
||||
computedArrangement.add(mergedRow)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package dev.patrickgold.florisboard.ime.text.smartbar
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.textservice.SentenceSuggestionsInfo
|
||||
import android.view.textservice.SpellCheckerSession
|
||||
import android.view.textservice.SuggestionsInfo
|
||||
import android.view.textservice.TextServicesManager
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.CursorAnchorInfo
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
@@ -15,22 +12,21 @@ import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.text.TextInputManager
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
|
||||
// TODO: Implement suggestion creation functionality
|
||||
// TODO: Cleanup and reorganize SmartbarManager
|
||||
class SmartbarManager private constructor() :
|
||||
SpellCheckerSession.SpellCheckerSessionListener, FlorisBoard.EventListener {
|
||||
class SmartbarManager private constructor() : FlorisBoard.EventListener {
|
||||
|
||||
private val florisboard: FlorisBoard = FlorisBoard.getInstance()
|
||||
private var isComposingEnabled: Boolean = false
|
||||
private var spellCheckerSession: SpellCheckerSession? = null
|
||||
private val textInputManager: TextInputManager = TextInputManager.getInstance()
|
||||
var smartbarView: SmartbarView? = null
|
||||
private set
|
||||
|
||||
var activeContainerId: Int = R.id.candidates
|
||||
var isQuickActionsVisible: Boolean = false
|
||||
set(value) { field = value; updateActiveContainerVisibility() }
|
||||
|
||||
private val candidateViewOnClickListener = View.OnClickListener { v ->
|
||||
@@ -43,7 +39,7 @@ class SmartbarManager private constructor() :
|
||||
private val candidateViewOnLongClickListener = View.OnLongClickListener { v ->
|
||||
true
|
||||
}
|
||||
private val numberRowButtonOnClickListener = View.OnClickListener { v ->
|
||||
private val keyButtonOnClickListener = View.OnClickListener { v ->
|
||||
val keyData = when (v.id) {
|
||||
R.id.number_row_0 -> KeyData(48, "0")
|
||||
R.id.number_row_1 -> KeyData(49, "1")
|
||||
@@ -55,26 +51,40 @@ class SmartbarManager private constructor() :
|
||||
R.id.number_row_7 -> KeyData(55, "7")
|
||||
R.id.number_row_8 -> KeyData(56, "8")
|
||||
R.id.number_row_9 -> KeyData(57, "9")
|
||||
R.id.cc_select_all -> KeyData(KeyCode.CLIPBOARD_SELECT_ALL)
|
||||
R.id.cc_copy -> KeyData(KeyCode.CLIPBOARD_COPY)
|
||||
R.id.cc_arrow_left -> KeyData(KeyCode.ARROW_LEFT)
|
||||
R.id.cc_arrow_right -> KeyData(KeyCode.ARROW_RIGHT)
|
||||
R.id.cc_cut -> KeyData(KeyCode.CLIPBOARD_CUT)
|
||||
R.id.cc_paste -> KeyData(KeyCode.CLIPBOARD_PASTE)
|
||||
else -> KeyData(0)
|
||||
}
|
||||
florisboard.textInputManager.sendKeyPress(keyData)
|
||||
}
|
||||
private val quickActionOnClickListener = View.OnClickListener { v ->
|
||||
when (v.id) {
|
||||
R.id.quick_action_switch_to_media_context -> {
|
||||
activeContainerId = getPreferredContainerId()
|
||||
florisboard.setActiveInput(R.id.media_input)
|
||||
R.id.back_button -> {
|
||||
florisboard.textInputManager.setActiveKeyboardMode(KeyboardMode.CHARACTERS)
|
||||
smartbarView?.setActiveVariant(R.id.smartbar_variant_default)
|
||||
}
|
||||
R.id.quick_action_switch_to_editing_context -> {
|
||||
if (florisboard.textInputManager.getActiveKeyboardMode() == KeyboardMode.EDITING) {
|
||||
florisboard.textInputManager.setActiveKeyboardMode(KeyboardMode.CHARACTERS)
|
||||
smartbarView?.setActiveVariant(R.id.smartbar_variant_default)
|
||||
} else {
|
||||
florisboard.textInputManager.setActiveKeyboardMode(KeyboardMode.EDITING)
|
||||
smartbarView?.setActiveVariant(R.id.smartbar_variant_back_only)
|
||||
}
|
||||
}
|
||||
R.id.quick_action_switch_to_media_context -> florisboard.setActiveInput(R.id.media_input)
|
||||
R.id.quick_action_open_settings -> florisboard.launchSettings()
|
||||
R.id.quick_action_one_handed_toggle -> florisboard.toggleOneHandedMode()
|
||||
else -> return@OnClickListener
|
||||
}
|
||||
isQuickActionsVisible = false
|
||||
}
|
||||
private val quickActionToggleOnClickListener = View.OnClickListener {
|
||||
activeContainerId = when (activeContainerId) {
|
||||
R.id.quick_actions -> getPreferredContainerId()
|
||||
else -> R.id.quick_actions
|
||||
}
|
||||
isQuickActionsVisible = !isQuickActionsVisible
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -94,7 +104,7 @@ class SmartbarManager private constructor() :
|
||||
|
||||
this.smartbarView = smartbarView
|
||||
|
||||
smartbarView.quickActionToggle?.setOnClickListener(quickActionToggleOnClickListener)
|
||||
smartbarView.findViewById<View>(R.id.quick_action_toggle)?.setOnClickListener(quickActionToggleOnClickListener)
|
||||
val quickActions = smartbarView.findViewById<LinearLayout>(R.id.quick_actions)
|
||||
for (quickAction in quickActions.children) {
|
||||
if (quickAction is ImageButton) {
|
||||
@@ -104,13 +114,27 @@ class SmartbarManager private constructor() :
|
||||
val numberRow = smartbarView.findViewById<LinearLayout>(R.id.number_row)
|
||||
for (numberRowButton in numberRow.children) {
|
||||
if (numberRowButton is Button) {
|
||||
numberRowButton.setOnClickListener(numberRowButtonOnClickListener)
|
||||
numberRowButton.setOnClickListener(keyButtonOnClickListener)
|
||||
}
|
||||
}
|
||||
val clipboardCursorRow = smartbarView.findViewById<ViewGroup>(R.id.clipboard_cursor_row)
|
||||
for (clipboardCursorRowButton in clipboardCursorRow.children) {
|
||||
if (clipboardCursorRowButton is ImageButton) {
|
||||
clipboardCursorRowButton.setOnClickListener(keyButtonOnClickListener)
|
||||
}
|
||||
}
|
||||
val backButton = smartbarView.findViewById<View>(R.id.back_button)
|
||||
backButton.setOnClickListener(quickActionOnClickListener)
|
||||
for (candidateView in smartbarView.candidateViewList) {
|
||||
candidateView.setOnClickListener(candidateViewOnClickListener)
|
||||
candidateView.setOnLongClickListener(candidateViewOnLongClickListener)
|
||||
}
|
||||
|
||||
smartbarView.setActiveVariant(R.id.smartbar_variant_default)
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
isQuickActionsVisible = false
|
||||
}
|
||||
|
||||
// TODO: clean up resources here
|
||||
@@ -120,49 +144,15 @@ class SmartbarManager private constructor() :
|
||||
instance = null
|
||||
}
|
||||
|
||||
override fun onGetSuggestions(arr: Array<out SuggestionsInfo>?) {
|
||||
if (arr == null || arr.isEmpty()) {
|
||||
return
|
||||
}
|
||||
/*val suggestions = arr[0]
|
||||
for (i in 0 until suggestions.suggestionsCount) {
|
||||
candidateViewList[i].text = suggestions.getSuggestionAt(i)
|
||||
if (i == 2) {
|
||||
break
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
override fun onGetSentenceSuggestions(arr: Array<out SentenceSuggestionsInfo>?) {
|
||||
if (arr == null || arr.isEmpty()) {
|
||||
return
|
||||
}
|
||||
/*val suggestions = arr[0].getSuggestionsInfoAt(0)
|
||||
for (i in 0 until suggestions.suggestionsCount) {
|
||||
candidateViewList[i].text = suggestions.getSuggestionAt(i)
|
||||
if (i == 2) {
|
||||
break
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
fun onStartInputView(keyboardMode: KeyboardMode, isComposingEnabled: Boolean) {
|
||||
this.isComposingEnabled = isComposingEnabled
|
||||
when {
|
||||
keyboardMode == KeyboardMode.NUMERIC ||
|
||||
keyboardMode == KeyboardMode.PHONE ||
|
||||
keyboardMode == KeyboardMode.PHONE2 -> {
|
||||
smartbarView?.visibility = View.GONE
|
||||
}
|
||||
!isComposingEnabled -> {
|
||||
smartbarView?.visibility = View.VISIBLE
|
||||
activeContainerId = R.id.number_row
|
||||
when (keyboardMode) {
|
||||
KeyboardMode.NUMERIC, KeyboardMode.PHONE, KeyboardMode.PHONE2 -> {
|
||||
smartbarView?.setActiveVariant(null)
|
||||
}
|
||||
else -> {
|
||||
smartbarView?.visibility = View.VISIBLE
|
||||
activeContainerId = R.id.candidates
|
||||
//val tsm = florisboard.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE) as TextServicesManager
|
||||
//spellCheckerSession = tsm.newSpellCheckerSession(null, null, this, true)
|
||||
smartbarView?.setActiveVariant(R.id.smartbar_variant_default)
|
||||
isQuickActionsVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,6 +161,14 @@ class SmartbarManager private constructor() :
|
||||
//spellCheckerSession?.close()
|
||||
}
|
||||
|
||||
override fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {
|
||||
val isSelectionActive = florisboard.textInputManager.isTextSelected
|
||||
smartbarView?.findViewById<View>(R.id.cc_cut)?.isEnabled = isSelectionActive
|
||||
smartbarView?.findViewById<View>(R.id.cc_copy)?.isEnabled = isSelectionActive
|
||||
smartbarView?.findViewById<View>(R.id.cc_paste)?.isEnabled =
|
||||
florisboard.clipboardManager?.hasPrimaryClip() ?: false
|
||||
}
|
||||
|
||||
fun deleteCandidateFromDictionary(candidate: String) {
|
||||
//
|
||||
}
|
||||
@@ -187,8 +185,6 @@ class SmartbarManager private constructor() :
|
||||
smartbarView.candidateViewList[1].text = "suggestions"
|
||||
smartbarView.candidateViewList[2].text = "nyi"
|
||||
} else {
|
||||
activeContainerId = R.id.candidates
|
||||
updateActiveContainerVisibility()
|
||||
smartbarView.candidateViewList[0].text = ""
|
||||
smartbarView.candidateViewList[1].text = composingText + "test"
|
||||
smartbarView.candidateViewList[2].text = ""
|
||||
@@ -213,44 +209,25 @@ class SmartbarManager private constructor() :
|
||||
//
|
||||
}
|
||||
|
||||
fun getPreferredContainerId(): Int {
|
||||
return when {
|
||||
!isComposingEnabled -> when(textInputManager.getActiveKeyboardMode()) {
|
||||
KeyboardMode.CHARACTERS -> R.id.number_row
|
||||
else -> 0
|
||||
}
|
||||
else -> R.id.candidates
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateActiveContainerVisibility() {
|
||||
val smartbarView = smartbarView ?: return
|
||||
|
||||
when (activeContainerId) {
|
||||
R.id.quick_actions -> {
|
||||
smartbarView.candidatesView?.visibility = View.GONE
|
||||
smartbarView.numberRowView?.visibility = View.GONE
|
||||
smartbarView.quickActionsView?.visibility = View.VISIBLE
|
||||
smartbarView.quickActionToggle?.rotation = -180.0f
|
||||
}
|
||||
R.id.number_row -> {
|
||||
smartbarView.candidatesView?.visibility = View.GONE
|
||||
smartbarView.numberRowView?.visibility = View.VISIBLE
|
||||
smartbarView.quickActionsView?.visibility = View.GONE
|
||||
smartbarView.quickActionToggle?.rotation = 0.0f
|
||||
}
|
||||
R.id.candidates -> {
|
||||
smartbarView.candidatesView?.visibility = View.VISIBLE
|
||||
smartbarView.numberRowView?.visibility = View.GONE
|
||||
smartbarView.quickActionsView?.visibility = View.GONE
|
||||
smartbarView.quickActionToggle?.rotation = 0.0f
|
||||
}
|
||||
else -> {
|
||||
smartbarView.candidatesView?.visibility = View.GONE
|
||||
smartbarView.numberRowView?.visibility = View.GONE
|
||||
smartbarView.quickActionsView?.visibility = View.GONE
|
||||
smartbarView.quickActionToggle?.rotation = 0.0f
|
||||
if (isQuickActionsVisible) {
|
||||
smartbarView.setActiveContainer(R.id.quick_actions)
|
||||
smartbarView.findViewById<View>(R.id.quick_action_toggle)?.rotation = -180.0f
|
||||
} else {
|
||||
if (isComposingEnabled) {
|
||||
smartbarView.setActiveContainer(R.id.candidates)
|
||||
} else if (textInputManager.getActiveKeyboardMode() == KeyboardMode.CHARACTERS) {
|
||||
smartbarView.setActiveContainer(when (florisboard.prefs.suggestion.showInstead) {
|
||||
"number_row" -> R.id.number_row
|
||||
"clipboard_cursor_tools" -> R.id.clipboard_cursor_row
|
||||
else -> null
|
||||
})
|
||||
} else {
|
||||
smartbarView.setActiveContainer(null)
|
||||
}
|
||||
smartbarView.findViewById<View>(R.id.quick_action_toggle)?.rotation = 0.0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,12 @@ package dev.patrickgold.florisboard.ime.text.smartbar
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.IdRes
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
@@ -34,16 +37,11 @@ class SmartbarView : LinearLayout {
|
||||
|
||||
private val smartbarManager = SmartbarManager.getInstance()
|
||||
|
||||
var candidatesView: LinearLayout? = null
|
||||
private set
|
||||
private var variants: MutableList<ViewGroup> = mutableListOf()
|
||||
private var containers: MutableList<ViewGroup> = mutableListOf()
|
||||
|
||||
var candidateViewList: MutableList<Button> = mutableListOf()
|
||||
private set
|
||||
var numberRowView: LinearLayout? = null
|
||||
private set
|
||||
var quickActionsView: LinearLayout? = null
|
||||
private set
|
||||
var quickActionToggle: ImageButton? = null
|
||||
private set
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
@@ -54,18 +52,54 @@ class SmartbarView : LinearLayout {
|
||||
|
||||
super.onAttachedToWindow()
|
||||
|
||||
candidatesView = findViewById(R.id.candidates)
|
||||
variants.add(findViewById(R.id.smartbar_variant_default))
|
||||
variants.add(findViewById(R.id.smartbar_variant_back_only))
|
||||
|
||||
containers.add(findViewById(R.id.candidates))
|
||||
containers.add(findViewById(R.id.clipboard_cursor_row))
|
||||
containers.add(findViewById(R.id.number_row))
|
||||
containers.add(findViewById(R.id.quick_actions))
|
||||
|
||||
candidateViewList.add(findViewById(R.id.candidate0))
|
||||
candidateViewList.add(findViewById(R.id.candidate1))
|
||||
candidateViewList.add(findViewById(R.id.candidate2))
|
||||
|
||||
numberRowView = findViewById(R.id.number_row)
|
||||
quickActionsView = findViewById(R.id.quick_actions)
|
||||
quickActionToggle = findViewById(R.id.quick_action_toggle)
|
||||
|
||||
smartbarManager.registerSmartbarView(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the active Smartbar variant based on the given id. Pass null to hide all variants and
|
||||
* show an empty Smartbar.
|
||||
*
|
||||
* @param which Which variant to show. Pass null to hide all.
|
||||
*/
|
||||
fun setActiveVariant(@IdRes which: Int?) {
|
||||
for (variant in variants) {
|
||||
if (variant.id == which) {
|
||||
variant.visibility = View.VISIBLE
|
||||
} else {
|
||||
variant.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the active Smartbar container based on the given id. Does only work if the currently
|
||||
* shown Smartbar variant is [R.id.smartbar_variant_default]. Pass null to hide all containers
|
||||
* and show only the quick action toggle.
|
||||
*
|
||||
* @param which Which container to show. Pass null to hide all.
|
||||
*/
|
||||
fun setActiveContainer(@IdRes which: Int?) {
|
||||
for (container in containers) {
|
||||
if (container.id == which) {
|
||||
container.visibility = View.VISIBLE
|
||||
} else {
|
||||
container.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies the default smartbar height with the given [factor] and sets it.
|
||||
*/
|
||||
|
||||
@@ -23,12 +23,10 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.FrameLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
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.SettingsFragmentKeyboardSubtypeListItemBinding
|
||||
import dev.patrickgold.florisboard.util.LocaleUtils
|
||||
|
||||
class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
@@ -174,9 +172,9 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
binding.subtypeNotConfWarning.visibility = View.GONE
|
||||
for (subtype in subtypes) {
|
||||
val itemView =
|
||||
SettingsFragmentKeyboardSubtypeListItemBinding.inflate(layoutInflater)
|
||||
ListItemBinding.inflate(layoutInflater)
|
||||
itemView.title.text = subtype.locale.displayName
|
||||
itemView.caption.text = subtypeManager.imeConfig.characterLayouts[subtype.layout]
|
||||
itemView.summary.text = subtypeManager.imeConfig.characterLayouts[subtype.layout]
|
||||
itemView.root.setOnClickListener { showEditSubtypeDialog(subtype.id) }
|
||||
binding.subtypeListView.addView(itemView.root)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.SeekBar
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SeekBarDialogBinding
|
||||
|
||||
/**
|
||||
* Custom preference which represents a seek bar which shows the current value in the summary. The
|
||||
* value can be changed by clicking on the preference, which brings up a dialog which a seek bar.
|
||||
* This implementation also allows for a min / max step value, while being backwards compatible.
|
||||
*
|
||||
* @see R.styleable.DialogSeekBarPreferenceAttrs for which xml attributes this preference accepts
|
||||
* besides the default Preference attributes.
|
||||
*
|
||||
* @property defaultValue The default value of this preference.
|
||||
* @property systemDefaultValue At this exact value [systemDefaultValueText] should be shown instead
|
||||
* of the actual value.
|
||||
* @property systemDefaultValueText The text to show if this preference's value or seek bar is
|
||||
* [systemDefaultValue]. Set to null to disable the system default text feature.
|
||||
* @property min The minimum value of the seek bar. Must not be greater or equal than [max].
|
||||
* @property max The maximum value of the seek bar. Must not be lesser or equal than [min].
|
||||
* @property step The step in which the seek bar increases per move. If the provided value is less
|
||||
* than 1, 1 will be used as step. Note that the xml attribute's name for this property is
|
||||
* [R.styleable.DialogSeekBarPreferenceAttrs_seekBarIncrement].
|
||||
* @property unit The unit to show after the value. Set to an empty string to disable this feature.
|
||||
*/
|
||||
class DialogSeekBarPreference : Preference {
|
||||
private var defaultValue: Int = 0
|
||||
private var systemDefaultValue: Int = -1
|
||||
private var systemDefaultValueText: String? = null
|
||||
private var min: Int = 0
|
||||
private var max: Int = 100
|
||||
private var step: Int = 1
|
||||
private var unit: String = ""
|
||||
|
||||
@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
|
||||
context.obtainStyledAttributes(attrs, R.styleable.DialogSeekBarPreferenceAttrs).apply {
|
||||
min = getInt(R.styleable.DialogSeekBarPreferenceAttrs_min, min)
|
||||
max = getInt(R.styleable.DialogSeekBarPreferenceAttrs_max, max)
|
||||
step = getInt(R.styleable.DialogSeekBarPreferenceAttrs_seekBarIncrement, step)
|
||||
if (step < 1) {
|
||||
step = 1
|
||||
}
|
||||
defaultValue = getInt(R.styleable.DialogSeekBarPreferenceAttrs_android_defaultValue, defaultValue)
|
||||
systemDefaultValue = getInt(R.styleable.DialogSeekBarPreferenceAttrs_systemDefaultValue, min - 1)
|
||||
systemDefaultValueText = getString(R.styleable.DialogSeekBarPreferenceAttrs_systemDefaultValueText)
|
||||
unit = getString(R.styleable.DialogSeekBarPreferenceAttrs_unit) ?: unit
|
||||
recycle()
|
||||
}
|
||||
onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue ->
|
||||
summary = getTextForValue(newValue.toString())
|
||||
true
|
||||
}
|
||||
onPreferenceClickListener = OnPreferenceClickListener {
|
||||
showSeekBarDialog()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToHierarchy(preferenceManager: PreferenceManager?) {
|
||||
super.onAttachedToHierarchy(preferenceManager)
|
||||
summary = getTextForValue(sharedPreferences.getInt(key, defaultValue))
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the text for the given [value] and adds the defined [unit] at the end.
|
||||
* If [systemDefaultValueText] is not null this method tries to match the given [value] with
|
||||
* [systemDefaultValue] and returns [systemDefaultValueText] upon matching.
|
||||
*/
|
||||
private fun getTextForValue(value: Any): String {
|
||||
if (value !is Int) {
|
||||
return "??$unit"
|
||||
}
|
||||
val systemDefValText = systemDefaultValueText
|
||||
return if (value == systemDefaultValue && systemDefValText != null) {
|
||||
systemDefValText
|
||||
} else {
|
||||
value.toString() + unit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the seek bar dialog.
|
||||
*/
|
||||
private fun showSeekBarDialog() {
|
||||
val inflater =
|
||||
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
val dialogView = SeekBarDialogBinding.inflate(inflater)
|
||||
val initValue = sharedPreferences.getInt(key, defaultValue)
|
||||
dialogView.seekBar.max = actualValueToSeekBarProgress(max)
|
||||
dialogView.seekBar.progress = actualValueToSeekBarProgress(initValue)
|
||||
dialogView.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
dialogView.seekBarValue.text = getTextForValue(seekBarProgressToActualValue(progress))
|
||||
}
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
dialogView.seekBarValue.text = getTextForValue(initValue)
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(this@DialogSeekBarPreference.title)
|
||||
setCancelable(true)
|
||||
setView(dialogView.root)
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val actualValue = seekBarProgressToActualValue(dialogView.seekBar.progress)
|
||||
sharedPreferences.edit().putInt(key, actualValue).apply()
|
||||
}
|
||||
setNeutralButton(R.string.settings__default) { _, _ ->
|
||||
sharedPreferences.edit().putInt(key, defaultValue).apply()
|
||||
}
|
||||
setNegativeButton(android.R.string.cancel, null)
|
||||
setOnDismissListener { summary = getTextForValue(sharedPreferences.getInt(key, defaultValue)) }
|
||||
create()
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the actual value to a progress value which the Android SeekBar implementation can
|
||||
* handle. (Android's SeekBar step is fixed at 1 and min at 0)
|
||||
*
|
||||
* @param actual The actual value.
|
||||
* @returns the internal value which is used to allow different min and step values.
|
||||
*/
|
||||
private fun actualValueToSeekBarProgress(actual: Int): Int {
|
||||
return (actual - min) / step
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the Android SeekBar value to the actual value.
|
||||
*
|
||||
* @param progress The progress value of the SeekBar.
|
||||
* @returns the actual value which is ready to use.
|
||||
*/
|
||||
private fun seekBarProgressToActualValue(progress: Int): Int {
|
||||
return (progress * step) + min
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="?semiTransparentColor"/>
|
||||
<stroke android:width="0.5dp" android:color="?semiTransparentColor"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_focused="true">
|
||||
<shape>
|
||||
<solid android:color="?semiTransparentColor"/>
|
||||
<stroke android:width="0.5dp" android:color="?semiTransparentColor"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<stroke android:width="0.5dp" android:color="?semiTransparentColor"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
5
app/src/main/res/drawable/ic_arrow_back.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_back.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="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_content_copy.xml
Normal file
5
app/src/main/res/drawable/ic_content_copy.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="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_content_cut.xml
Normal file
5
app/src/main/res/drawable/ic_content_cut.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="M9.64,7.64c0.23,-0.5 0.36,-1.05 0.36,-1.64 0,-2.21 -1.79,-4 -4,-4S2,3.79 2,6s1.79,4 4,4c0.59,0 1.14,-0.13 1.64,-0.36L10,12l-2.36,2.36C7.14,14.13 6.59,14 6,14c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4c0,-0.59 -0.13,-1.14 -0.36,-1.64L12,14l7,7h3v-1L9.64,7.64zM6,8c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zM6,20c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zM12,12.5c-0.28,0 -0.5,-0.22 -0.5,-0.5s0.22,-0.5 0.5,-0.5 0.5,0.22 0.5,0.5 -0.22,0.5 -0.5,0.5zM19,3l-6,6 2,2 7,-7L22,3z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_content_paste.xml
Normal file
5
app/src/main/res/drawable/ic_content_paste.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="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_first_page.xml
Normal file
5
app/src/main/res/drawable/ic_first_page.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="M18.41,16.59L13.82,12l4.59,-4.59L17,6l-6,6 6,6zM6,6h2v12H6z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_format_italic.xml
Normal file
5
app/src/main/res/drawable/ic_format_italic.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="M10,4v3h2.21l-3.42,8H6v3h8v-3h-2.21l3.42,-8H18V4z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_last_page.xml
Normal file
5
app/src/main/res/drawable/ic_last_page.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="M5.59,7.41L10.18,12l-4.59,4.59L7,18l6,-6 -6,-6zM16,6h2v12h-2z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_select_all.xml
Normal file
5
app/src/main/res/drawable/ic_select_all.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="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/>
|
||||
</vector>
|
||||
136
app/src/main/res/layout/editing_layout.xml
Normal file
136
app/src/main/res/layout/editing_layout.xml
Normal file
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<dev.patrickgold.florisboard.ime.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">
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/arrow_left"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.2"
|
||||
app:layout_constraintHeight_percent="0.75"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/move_home"
|
||||
android:src="@drawable/ic_keyboard_arrow_left"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/arrow_up"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_left"
|
||||
android:src="@drawable/ic_keyboard_arrow_up"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/select"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintTop_toBottomOf="@+id/arrow_up"
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_left"
|
||||
android:text="@android:string/selectTextMode"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/arrow_down"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintTop_toBottomOf="@+id/select"
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_left"
|
||||
android:src="@drawable/ic_keyboard_arrow_down"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/arrow_right"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.2"
|
||||
app:layout_constraintHeight_percent="0.75"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/move_end"
|
||||
app:layout_constraintLeft_toRightOf="@+id/select"
|
||||
android:src="@drawable/ic_keyboard_arrow_right"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/move_home"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.35"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:src="@drawable/ic_first_page"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/move_end"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.35"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintLeft_toRightOf="@+id/move_home"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:src="@drawable/ic_last_page"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/select_all"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_right"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:text="@android:string/selectAll"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/clipboard_cut"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintTop_toTopOf="@+id/select_all"
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_right"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:text="@android:string/cut"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="select_all,clipboard_cut"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/clipboard_copy"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintTop_toBottomOf="@+id/barrier1"
|
||||
app:layout_constraintBottom_toTopOf="@+id/clipboard_paste"
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_right"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:text="@android:string/copy"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/clipboard_paste"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintTop_toBottomOf="@+id/clipboard_copy"
|
||||
app:layout_constraintBottom_toTopOf="@+id/backspace"
|
||||
app:layout_constraintLeft_toRightOf="@+id/arrow_right"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:text="@android:string/paste"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.editing.EditingKeyView
|
||||
android:id="@+id/backspace"
|
||||
style="@style/TextEditingButton"
|
||||
app:layout_constraintWidth_percent="0.3"
|
||||
app:layout_constraintHeight_percent="0.25"
|
||||
app:layout_constraintTop_toBottomOf="@+id/clipboard_paste"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@+id/move_end"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:src="@drawable/ic_backspace"/>
|
||||
|
||||
</dev.patrickgold.florisboard.ime.editing.EditingKeyboardView>
|
||||
@@ -2,7 +2,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:clickable="true"
|
||||
@@ -10,19 +10,23 @@
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
tools:text="Title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/caption"
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="normal"
|
||||
tools:text="Caption"/>
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:maxLines="4"
|
||||
tools:text="Summary"/>
|
||||
|
||||
</LinearLayout>
|
||||
22
app/src/main/res/layout/seek_bar_dialog.xml
Normal file
22
app/src/main/res/layout/seek_bar_dialog.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/seek_bar_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
tools:text="0%"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSeekBar
|
||||
android:id="@+id/seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -7,126 +7,202 @@
|
||||
android:layout_height="@dimen/smartbar_height"
|
||||
android:background="?smartbar_bgColor">
|
||||
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/quick_action_toggle"
|
||||
style="@style/SmartbarQuickAction.Toggle"
|
||||
android:contentDescription="@string/smartbar__quick_action_toggle__alt"
|
||||
android:src="@drawable/ic_keyboard_arrow_right" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/candidates"
|
||||
style="@style/SmartbarContainer"
|
||||
android:visibility="gone">
|
||||
|
||||
<Button
|
||||
android:id="@+id/candidate0"
|
||||
style="@style/SmartbarCandidate" />
|
||||
|
||||
<View style="@style/SmartbarDivider" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/candidate1"
|
||||
style="@style/SmartbarCandidate" />
|
||||
|
||||
<View style="@style/SmartbarDivider" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/candidate2"
|
||||
style="@style/SmartbarCandidate" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/quick_actions"
|
||||
style="@style/SmartbarContainer"
|
||||
android:id="@+id/smartbar_variant_default"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/quick_action_switch_to_media_context"
|
||||
style="@style/SmartbarQuickAction"
|
||||
android:contentDescription="@string/smartbar__quick_action__switch_to_media_context"
|
||||
android:src="@drawable/ic_sentiment_satisfied" />
|
||||
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/quick_action_open_settings"
|
||||
style="@style/SmartbarQuickAction"
|
||||
android:contentDescription="@string/smartbar__quick_action__open_settings"
|
||||
android:src="@drawable/ic_settings" />
|
||||
|
||||
<!-- TODO: find better icon for one-handed mode -->
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/quick_action_one_handed_toggle"
|
||||
style="@style/SmartbarQuickAction"
|
||||
android:contentDescription="@string/smartbar__quick_action__one_handed_mode"
|
||||
android:id="@+id/quick_action_toggle"
|
||||
style="@style/SmartbarQuickAction.Toggle"
|
||||
android:contentDescription="@string/smartbar__quick_action_toggle__alt"
|
||||
android:src="@drawable/ic_keyboard_arrow_right" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/candidates"
|
||||
style="@style/SmartbarContainer"
|
||||
android:visibility="gone">
|
||||
|
||||
<Button
|
||||
android:id="@+id/candidate0"
|
||||
style="@style/SmartbarCandidate"/>
|
||||
|
||||
<View style="@style/SmartbarDivider"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/candidate1"
|
||||
style="@style/SmartbarCandidate"/>
|
||||
|
||||
<View style="@style/SmartbarDivider"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/candidate2"
|
||||
style="@style/SmartbarCandidate"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/quick_actions"
|
||||
style="@style/SmartbarContainer"
|
||||
android:visibility="gone">
|
||||
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/quick_action_switch_to_media_context"
|
||||
style="@style/SmartbarQuickAction"
|
||||
android:contentDescription="@string/smartbar__quick_action__switch_to_media_context"
|
||||
android:src="@drawable/ic_sentiment_satisfied"/>
|
||||
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/quick_action_open_settings"
|
||||
style="@style/SmartbarQuickAction"
|
||||
android:contentDescription="@string/smartbar__quick_action__open_settings"
|
||||
android:src="@drawable/ic_settings"/>
|
||||
|
||||
<!-- TODO: find better icon for one-handed mode -->
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/quick_action_one_handed_toggle"
|
||||
style="@style/SmartbarQuickAction"
|
||||
android:contentDescription="@string/smartbar__quick_action__one_handed_mode"
|
||||
android:src="@drawable/ic_keyboard_arrow_right"/>
|
||||
|
||||
<!-- TODO: find better icon for editing -->
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/quick_action_switch_to_editing_context"
|
||||
style="@style/SmartbarQuickAction"
|
||||
android:contentDescription="@string/smartbar__quick_action__switch_to_editing_context"
|
||||
android:src="@drawable/ic_format_italic"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- TODO: integrate a KeyboardView instead of hardcoding these buttons -->
|
||||
<LinearLayout
|
||||
android:id="@+id/number_row"
|
||||
style="@style/SmartbarContainer"
|
||||
android:visibility="gone"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_1"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="1"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_2"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="2"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_3"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="3"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_4"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="4"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_5"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="5"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_6"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="6"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_7"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="7"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_8"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="8"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_9"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="9"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_0"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="0"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- TODO: integrate a KeyboardView instead of hardcoding these buttons -->
|
||||
<LinearLayout
|
||||
android:id="@+id/clipboard_cursor_row"
|
||||
style="@style/SmartbarContainer"
|
||||
android:visibility="gone"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/cc_select_all"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:src="@drawable/ic_select_all"
|
||||
android:tint="@drawable/button_key_enable_color_selector"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/cc_copy"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:src="@drawable/ic_content_copy"
|
||||
android:tint="@drawable/button_key_enable_color_selector"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/cc_arrow_left"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:src="@drawable/ic_keyboard_arrow_left"
|
||||
android:tint="@drawable/button_key_enable_color_selector"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/cc_arrow_right"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:src="@drawable/ic_keyboard_arrow_right"
|
||||
android:tint="@drawable/button_key_enable_color_selector"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/cc_cut"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:src="@drawable/ic_content_cut"
|
||||
android:tint="@drawable/button_key_enable_color_selector"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/cc_paste"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:src="@drawable/ic_content_paste"
|
||||
android:tint="@drawable/button_key_enable_color_selector"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Placeholder on the right which reserves the space for a second button -->
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/smartbar_button_margin"
|
||||
android:clickable="false"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- TODO: integrate a KeyboardView instead of hardcoding these buttons -->
|
||||
<LinearLayout
|
||||
android:id="@+id/number_row"
|
||||
style="@style/SmartbarContainer"
|
||||
android:visibility="gone"
|
||||
tools:ignore="HardcodedText">
|
||||
android:id="@+id/smartbar_variant_back_only"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_1"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_2"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="2" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_3"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="3" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_4"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="4" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_5"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="5" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_6"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="6" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_7"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="7" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_8"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="8" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_9"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="9" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/number_row_0"
|
||||
style="@style/SmartbarCandidate"
|
||||
android:text="0" />
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:id="@+id/back_button"
|
||||
style="@style/SmartbarQuickAction"
|
||||
android:contentDescription="@string/smartbar__quick_action__exit_editing"
|
||||
android:src="@drawable/ic_arrow_back"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Placeholder on the right which reserves the space for a second button -->
|
||||
<dev.patrickgold.florisboard.ime.text.smartbar.SmartbarQuickActionButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/smartbar_button_margin"
|
||||
android:clickable="false"
|
||||
android:visibility="invisible" />
|
||||
|
||||
</dev.patrickgold.florisboard.ime.text.smartbar.SmartbarView>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
android:id="@+id/text_input_view_flipper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:measureAllChildren="false">
|
||||
android:measureAllChildren="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/keyboard_preview"
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/editing_layout"/>
|
||||
|
||||
</ViewFlipper>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
120
app/src/main/res/values-it/strings.xml
Normal file
120
app/src/main/res/values-it/strings.xml
Normal file
@@ -0,0 +1,120 @@
|
||||
<resources>
|
||||
<string name="key__phone_pause">Pausa</string>
|
||||
<string name="key__phone_wait">Attendi</string>
|
||||
<string name="key_popup__threedots_alt">Icona a tre puntini.Se visibile, indica che è possibile utilizzare più lettere se premuto a lungo.</string>
|
||||
|
||||
<!-- Media strings -->
|
||||
<string name="media__tab__emojis">Emojis</string>
|
||||
<string name="media__tab__emoticons">Emoticons</string>
|
||||
<string name="media__tab__kaomoji">Kaomoji</string>
|
||||
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion">Smileys & Emotions</string>
|
||||
<string name="emoji__category__people_body">Persone & Corpo</string>
|
||||
<string name="emoji__category__animals_nature">Animali & Natura</string>
|
||||
<string name="emoji__category__food_drink">Cibo & Bevande</string>
|
||||
<string name="emoji__category__travel_places">Viaggi & Luoghi</string>
|
||||
<string name="emoji__category__activities">Attività</string>
|
||||
<string name="emoji__category__objects">Oggetti</string>
|
||||
<string name="emoji__category__symbols">Simboli</string>
|
||||
<string name="emoji__category__flags">Bandiere</string>
|
||||
|
||||
<!-- Smartbar strings -->
|
||||
<string name="smartbar__quick_action_toggle__alt">Attiva / disattiva azione rapida. Se premuto, alterna i suggerimenti di parole ed i pulsanti di azione rapida.</string>
|
||||
<string name="smartbar__quick_action__one_handed_mode">Attiva / disattiva la modalità a una mano.</string>
|
||||
<string name="smartbar__quick_action__open_settings">Apri Impostazioni.</string>
|
||||
<string name="smartbar__quick_action__switch_to_media_context">Passa alla visualizzazione dei media.</string>
|
||||
|
||||
<!-- Settings UI strings -->
|
||||
<string name="settings__title">Impostazioni</string>
|
||||
<string name="settings__menu">Altre opzioni</string>
|
||||
<string name="settings__menu_about">Informazioni su</string>
|
||||
<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__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="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__advanced__title">Avanzate</string>
|
||||
<string name="pref__advanced__settings_theme__label">Impostazioni tema</string>
|
||||
<string name="pref__advanced__show_app_icon__label">Mostra icona nel launcher</string>
|
||||
|
||||
<!-- About UI strings -->
|
||||
<string name="about__title">Informazioni su</string>
|
||||
<string name="about__app_icon_content_description">Icona dell\'app FlorisBoard</string>
|
||||
<string name="about__view_licenses">Licenze open source</string>
|
||||
<string name="about__view_privacy_policy">Norme sulla privacy</string>
|
||||
<string name="about__view_source_code">Codice sorgente</string>
|
||||
|
||||
<string name="about__license__title">Licenze open source</string>
|
||||
|
||||
<!-- Setup UI strings -->
|
||||
<string name="setup__title">Configurazione</string>
|
||||
<string name="setup__prev_button">Precedente</string>
|
||||
<string name="setup__cancel_button">Annulla</string>
|
||||
<string name="setup__next_button">Avanti</string>
|
||||
<string name="setup__finish_button">Fine</string>
|
||||
<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__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>
|
||||
<string name="setup__welcome__outro">Per avviare l\'installazione, fai clic su <i>AVANTI</i>.</string>
|
||||
|
||||
<string name="setup__enable_ime__title">Abilita FlorisBoard</string>
|
||||
<string name="setup__enable_ime__text_before_enabled">Android richiede che ogni tastiera personalizzata debba essere abilitata manualmente prima di poterla utilizzare. Fai clic sul pulsante in basso per passare a <i>Lingua & Input</i>impostazioni, quindi assicurati di selezionare\'<i>FlorisBoard</i>\'.</string>
|
||||
<string name="setup__enable_ime__text_button_language_and_input">Apri lingue & Impostazioni di input</string>
|
||||
<string name="setup__enable_ime__text_after_enabled">FlorisBoard è stato abilitato con successo. Per continuare, fai clic su <i>AVANTI</i>!</string>
|
||||
|
||||
<string name="setup__make_default__title">Rendi FlorisBoard predefinita</string>
|
||||
<string name="setup__make_default__text_before_switch">FlorisBoard è ora abilitato nel tuo sistema. Per usarlo attivamente, passa a FlorisBoard selezionandolo nella finestra di dialogo del selettore di input!</string>
|
||||
<string name="setup__make_default__text_switch_button">Cambia tastiera</string>
|
||||
<string name="setup__make_default__text_after_switch">Hai cambiato con successo la tastiera predefinita su FlorisBoard!</string>
|
||||
|
||||
<string name="setup__finish__title">Installazione terminata!</string>
|
||||
</resources>
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref__looknfeel__height_factor__entries">
|
||||
<item>Extra-short</item>
|
||||
<item>Short</item>
|
||||
<item>Mid-short</item>
|
||||
<item>Normal</item>
|
||||
<item>Mid-tall</item>
|
||||
<item>Tall</item>
|
||||
<item>Extra-tall</item>
|
||||
<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>
|
||||
<string-array name="pref__looknfeel__height_factor__values">
|
||||
<item>extra_short</item>
|
||||
@@ -20,9 +20,9 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__looknfeel__one_handed_mode__entries">
|
||||
<item>Off</item>
|
||||
<item>Right-handed mode</item>
|
||||
<item>Left-handed mode</item>
|
||||
<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>
|
||||
<string-array name="pref__looknfeel__one_handed_mode__values">
|
||||
<item>off</item>
|
||||
@@ -31,9 +31,9 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__advanced__settings_theme__entries">
|
||||
<item>System default</item>
|
||||
<item>Light</item>
|
||||
<item>Dark</item>
|
||||
<item>@string/settings__system_default</item>
|
||||
<item>@string/pref__advanced__settings_theme__light</item>
|
||||
<item>@string/pref__advanced__settings_theme__dark</item>
|
||||
</string-array>
|
||||
<string-array name="pref__advanced__settings_theme__values">
|
||||
<item>auto</item>
|
||||
@@ -41,6 +41,15 @@
|
||||
<item>dark</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__suggestion__show_instead__entries">
|
||||
<item>@string/pref__suggestion__show_instead__number_row</item>
|
||||
<item>@string/pref__suggestion__show_instead__clipboard_cursor_tools</item>
|
||||
</string-array>
|
||||
<string-array name="pref__suggestion__show_instead__values">
|
||||
<item>number_row</item>
|
||||
<item>clipboard_cursor_tools</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__theme__name__entries">
|
||||
<item>Floris Light</item>
|
||||
<item>Floris Dark</item>
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="DialogSeekBarPreferenceAttrs">
|
||||
<attr name="android:defaultValue" format="integer"/>
|
||||
<attr name="systemDefaultValue" format="integer"/>
|
||||
<attr name="systemDefaultValueText" format="reference|string"/>
|
||||
<attr name="min" format="integer"/>
|
||||
<attr name="max" format="integer"/>
|
||||
<attr name="seekBarIncrement" format="integer"/>
|
||||
<attr name="unit" format="string"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="EditingKeyView">
|
||||
<attr name="android:text" format="string|reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="KeyboardTheme">
|
||||
<attr name="keyboardViewStyle" format="reference" />
|
||||
|
||||
@@ -1,26 +1,12 @@
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">FlorisBoard</string>
|
||||
|
||||
<string name="florisboard__repo_url" translatable="false">https://github.com/florisboard/florisboard</string>
|
||||
<string name="florisboard__privacy_policy_url" translatable="false">https://gist.github.com/patrickgold/a18f1e47468d72f0868afc69d6faaf0b</string>
|
||||
|
||||
<string name="key__phone_pause">Pause</string>
|
||||
<string name="key__phone_wait">Wait</string>
|
||||
<string name="key__view_characters">ABC</string>
|
||||
<string name="key__view_numeric">1 2\n3 4</string>
|
||||
<string name="key__view_phone">123</string>
|
||||
<string name="key__view_phone2">* #</string>
|
||||
<string name="key__view_symbols">\?123</string>
|
||||
<string name="key__view_symbols2">=\\<</string>
|
||||
|
||||
<string name="key_popup__threedots_alt">Three-dot icon. If visible, indicates that more letters can be used if longer pressed.</string>
|
||||
|
||||
<!-- Media strings -->
|
||||
<string name="media__tab__emojis">Emojis</string>
|
||||
<string name="media__tab__emoticons">Emoticons</string>
|
||||
<string name="media__tab__emoticons_label">;-)</string>
|
||||
<string name="media__tab__kaomoji">Kaomoji</string>
|
||||
<string name="media__tab__kaomoji_label">(^-^*)/</string>
|
||||
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion">Smileys & Emotions</string>
|
||||
@@ -35,8 +21,10 @@
|
||||
|
||||
<!-- Smartbar strings -->
|
||||
<string name="smartbar__quick_action_toggle__alt">Quick action toggle. If pressed, toggles between the word suggestions and the quick action buttons.</string>
|
||||
<string name="smartbar__quick_action__exit_editing">Exit text editing panel.</string>
|
||||
<string name="smartbar__quick_action__one_handed_mode">Toggle the state of the one-handed mode.</string>
|
||||
<string name="smartbar__quick_action__open_settings">Open settings.</string>
|
||||
<string name="smartbar__quick_action__switch_to_editing_context">Switch to text editing panel.</string>
|
||||
<string name="smartbar__quick_action__switch_to_media_context">Switch to media input view.</string>
|
||||
|
||||
<!-- Settings UI strings -->
|
||||
@@ -49,6 +37,8 @@
|
||||
<string name="settings__navigation__looknfeel">Look & feel</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>
|
||||
|
||||
<string name="settings__home__title">Welcome to %s</string>
|
||||
<string name="settings__home__ime_not_enabled">FlorisBoard is not enabled in the system and thus won\'t be available as an input method in the input picker. Click here to resolve this issue.</string>
|
||||
@@ -67,18 +57,34 @@
|
||||
<string name="settings__keyboard__subtype_layout">Keyboard layout</string>
|
||||
<string name="settings__keyboard__subtype_error_already_exists">This subtype already exists!</string>
|
||||
<string name="pref__suggestion__title">Suggestions</string>
|
||||
<string name="pref__suggestion__enabled__label">[NYI] Display suggestions while you type</string>
|
||||
<string name="pref__suggestion__enabled__label">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>
|
||||
<string name="pref__suggestion__show_instead__clipboard_cursor_tools">Clipboard cursor tools</string>
|
||||
<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__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>
|
||||
@@ -91,6 +97,8 @@
|
||||
|
||||
<string name="settings__advanced__title">Advanced</string>
|
||||
<string name="pref__advanced__settings_theme__label">Settings theme</string>
|
||||
<string name="pref__advanced__settings_theme__light">Light</string>
|
||||
<string name="pref__advanced__settings_theme__dark">Dark</string>
|
||||
<string name="pref__advanced__show_app_icon__label">Show app icon in launcher</string>
|
||||
|
||||
<!-- About UI strings -->
|
||||
|
||||
19
app/src/main/res/values/strings_dont_translate.xml
Normal file
19
app/src/main/res/values/strings_dont_translate.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">FlorisBoard</string>
|
||||
|
||||
<string name="florisboard__repo_url" translatable="false">https://github.com/florisboard/florisboard</string>
|
||||
<string name="florisboard__privacy_policy_url" translatable="false">https://gist.github.com/patrickgold/a18f1e47468d72f0868afc69d6faaf0b</string>
|
||||
|
||||
<string name="key__view_characters" translatable="false">ABC</string>
|
||||
<string name="key__view_numeric" translatable="false">1 2\n3 4</string>
|
||||
<string name="key__view_phone" translatable="false">123</string>
|
||||
<string name="key__view_phone2" translatable="false">* #</string>
|
||||
<string name="key__view_symbols" translatable="false">\?123</string>
|
||||
<string name="key__view_symbols2" translatable="false">=\\<</string>
|
||||
<string name="key__view_half_space" translatable="false">↲</string>
|
||||
<string name="key__view_keshida" translatable="false">"یــــ"</string>
|
||||
|
||||
<!-- Media strings -->
|
||||
<string name="media__tab__emoticons_label" translatable="false">;-)</string>
|
||||
<string name="media__tab__kaomoji_label" translatable="false">(^-^*)/</string>
|
||||
</resources>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="OneHandedPanel">
|
||||
<item name="android:layout_width">@dimen/one_handed_width</item>
|
||||
@@ -65,6 +64,15 @@
|
||||
<item name="android:autoMirrored">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextEditingButton" parent="Widget.AppCompat.Button.Borderless">
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:layout_height">0dp</item>
|
||||
<item name="android:background">@drawable/button_transparent_bg_on_press_with_border</item>
|
||||
<item name="android:soundEffectsEnabled">false</item>
|
||||
<item name="android:hapticFeedbackEnabled">false</item>
|
||||
<item name="android:scaleType">center</item>
|
||||
</style>
|
||||
|
||||
<style name="SettingsCardView" parent="Widget.MaterialComponents.CardView">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<item name="android:navigationBarColor" tools:targetApi="o_mr1">?keyboard_bgColor</item>
|
||||
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
|
||||
<item name="android:colorControlNormal">#8A000000</item><!-- Black, semi transparent -->
|
||||
<item name="android:colorButtonNormal">#4A000000</item><!-- Black, semi transparent -->
|
||||
<item name="android:textColor">#000000</item><!-- Black -->
|
||||
<item name="semiTransparentColor">#20000000</item><!-- Black, semi transparent -->
|
||||
|
||||
@@ -53,6 +54,7 @@
|
||||
<item name="android:navigationBarColor" tools:targetApi="o_mr1">?keyboard_bgColor</item>
|
||||
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item>
|
||||
<item name="android:colorControlNormal">#B3FFFFFF</item><!-- White, semi transparent -->
|
||||
<item name="android:colorButtonNormal">#73FFFFFF</item><!-- White, semi transparent -->
|
||||
<item name="android:textColor">#FFFFFF</item><!-- White -->
|
||||
<item name="semiTransparentColor">#20FFFFFF</item><!-- White, semi transparent -->
|
||||
|
||||
|
||||
@@ -13,6 +13,15 @@
|
||||
app:title="@string/pref__suggestion__enabled__label"
|
||||
app:summary="@string/pref__suggestion__enabled__summary"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="number_row"
|
||||
app:entries="@array/pref__suggestion__show_instead__entries"
|
||||
app:entryValues="@array/pref__suggestion__show_instead__values"
|
||||
app:key="suggestion__show_instead"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__suggestion__show_instead__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:dependency="suggestion__enabled"
|
||||
@@ -36,4 +45,19 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__popup__title">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:key="popup__enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__popup__visible__label"
|
||||
app:summary="@string/pref__popup__visible__summary"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
app:key="looknfeel__one_handed_mode"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__one_handed_mode__label"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="normal"
|
||||
@@ -22,7 +22,7 @@
|
||||
app:key="looknfeel__height_factor"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__height_factor__label"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
@@ -34,48 +34,52 @@
|
||||
android:defaultValue="true"
|
||||
android:key="looknfeel__sound_enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__sound_enabled__label" />
|
||||
app:title="@string/pref__looknfeel__sound_enabled__label"/>
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
<dev.patrickgold.florisboard.settings.components.DialogSeekBarPreference
|
||||
app:allowDividerAbove="false"
|
||||
android:defaultValue="-1"
|
||||
app:systemDefaultValue="-1"
|
||||
app:systemDefaultValueText="@string/settings__system_default"
|
||||
app:dependency="looknfeel__sound_enabled"
|
||||
app:key="looknfeel__sound_volume"
|
||||
app:min="0"
|
||||
android:max="100"
|
||||
app:max="100"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__sound_volume__label"
|
||||
app:seekBarIncrement="1"
|
||||
app:showSeekBarValue="true"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:unit="%"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="looknfeel__vibration_enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__vibration_enabled__label" />
|
||||
app:title="@string/pref__looknfeel__vibration_enabled__label"/>
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
<dev.patrickgold.florisboard.settings.components.DialogSeekBarPreference
|
||||
app:allowDividerAbove="false"
|
||||
android:defaultValue="-1"
|
||||
app:systemDefaultValue="-1"
|
||||
app:systemDefaultValueText="@string/settings__system_default"
|
||||
app:dependency="looknfeel__vibration_enabled"
|
||||
app:key="looknfeel__vibration_strength"
|
||||
app:min="0"
|
||||
android:max="100"
|
||||
app:max="100"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__vibration_strength__label"
|
||||
app:seekBarIncrement="1"
|
||||
app:showSeekBarValue="true"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:unit="%"/>
|
||||
|
||||
<SeekBarPreference
|
||||
<dev.patrickgold.florisboard.settings.components.DialogSeekBarPreference
|
||||
app:allowDividerAbove="false"
|
||||
android:defaultValue="300"
|
||||
app:key="looknfeel__long_press_delay"
|
||||
app:min="100"
|
||||
android:max="700"
|
||||
app:max="700"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__long_press_delay__label"
|
||||
app:seekBarIncrement="10"
|
||||
app:showSeekBarValue="true"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:unit=" ms"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Note: This project is currently in alpha stage. If you want to see a feature bei
|
||||
|
||||
Currently implemented and fully working features:
|
||||
* Latin keyboard layouts
|
||||
* QWERTY, QWERTZ, AZERTY, Spanish and Swiss. More coming in future releases
|
||||
* QWERTY, QWERTZ, AZERTY, Spanish, Norwegian, Swedish/Finnish, Danish, Icelandic and Swiss. More coming in future releases
|
||||
* Easy switching between languages/layouts by defining subtypes in the settings
|
||||
* Keyboard layouts for typing in a (phone) number
|
||||
* Special characters input
|
||||
@@ -14,4 +14,4 @@ Currently implemented and fully working features:
|
||||
* Customization of key press sound/vibration
|
||||
|
||||
Source code (Apache 2.0) and project management:
|
||||
https://github.com/florisboard/florisboard
|
||||
https://github.com/florisboard/florisboard
|
||||
|
||||
17
fastlane/metadata/android/it/full_description.txt
Normal file
17
fastlane/metadata/android/it/full_description.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
FlorisBoard è una tastiera open source mirata a fornirti un modo semplice per digitare nel rispetto della tua privacy.
|
||||
|
||||
Nota: questo progetto è attualmente in fase alpha. Se desideri vedere una funzionalità implementata o segnalare un bug, visita il repository di questo progetto (link alla fine della descrizione) su GitHub e segnala un problema. Questo aiuta a rendere FlorisBoard ancora migliore! Grazie!
|
||||
|
||||
Funzionalità attualmente implementate e completamente funzionanti:
|
||||
* Layout della tastiera
|
||||
* QWERTY, QWERTZ, AZERTY, Spagnolo, norvegese, svedese,finlandese, danese, islandese e svizzero. Altre novità in arrivo nelle versioni future
|
||||
* Facile passaggio tra lingue / layout nelle impostazioni
|
||||
* Layout di tastiera tipo tastierino telefonico per digitazione di un numero
|
||||
* Inserimento di caratteri speciali
|
||||
* Tastiera Emoji/Emoticon
|
||||
* Modalità con una sola mano per digitare più facilmente su dispositivi di grandi dimensioni
|
||||
* Tema chiaro e scuro per l'interfaccia utente e per tastiera (altri temi arriveranno nelle versioni future)
|
||||
* Personalizzazione del suono / vibrazione della pressione dei tasti
|
||||
|
||||
Codice sorgente (Apache 2.0) e gestione del progetto:
|
||||
https://github.com/florisboard/florisboard
|
||||
1
fastlane/metadata/android/it/short_description.txt
Normal file
1
fastlane/metadata/android/it/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Una tastiera open source. Attualmente in fase alfa.
|
||||
1
fastlane/metadata/android/it/title.txt
Normal file
1
fastlane/metadata/android/it/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
FlorisBoard
|
||||
Reference in New Issue
Block a user