Compare commits

...

29 Commits

Author SHA1 Message Date
Patrick Goldinger
0eb5ca318b Release v0.1.2 2020-08-16 22:41:06 +02:00
Patrick Goldinger
dfa9df6cd6 Merge pull request #13 from florisboard/feat-clipboard-cursor-tools
Add clipboard cursor related tools
2020-08-16 22:13:19 +02:00
Patrick Goldinger
3f5dfbc852 Add number row and clipboard cursor tools to Smartbar (#9, #3)
- Smartbar now supports showing a number row or a clipboard/cursor toolbar
  if candidate suggestions are disabled. What toolbar will be shown is
  controlled by prefs.suggestion.showInstead
- Smartbar now shows a back button when the active keyboard mode is
  KeyboardMode.EDITING
- Improvements in backend of Smartbar
2020-08-16 17:07:52 +02:00
Patrick Goldinger
59caafbf19 Implement cursor movement and clipboard functionality
- Add backend handling for editing layout.
- Improve FlorisBoard event listener implementation, allow different
  objects than Text-/MediaInputManager to receive events.
- Add function to send a key event to the system (allows to write this
  in a single line, which is more readable)
2020-08-15 20:26:06 +02:00
Patrick Goldinger
037a452baf Add SmartbarQuickAction to access editing layout 2020-08-14 19:17:13 +02:00
Patrick Goldinger
ffa405f289 Add clipboard and cursor editing layout (UI) 2020-08-14 18:54:35 +02:00
Patrick Goldinger
5d7091582f Modify prefs_looknfeel.xml to use new seek bar pref 2020-08-11 23:12:15 +02:00
Patrick Goldinger
b4096f2cfb Add DialogSeekBarPreference in .settings.components
- This SeekBar implementation allows for better control of min/max/step.
- The current value of the preference is shown in the summary, to change it
  the user has to click on it, where a dialog window with a SeekBar opens.
2020-08-11 23:06:51 +02:00
Patrick Goldinger
81c62f3e91 Improve list item layout
- List item now uses Android's predefined ids for title and summary
- Needed for custom preference implementation
- Layout font size in list_item.xml is now the same as the other list items
2020-08-11 22:58:49 +02:00
Patrick Goldinger
5c7db2b344 Simplify feature_request.md issue template 2020-08-10 23:26:17 +02:00
Patrick Goldinger
30bca99092 Fix extended key popup not aligning correctly
when FlorisBoard initializes with prefs.popup.enabled=false. This was
due to the fact that the show() method of KeyPopupManager did some required
calculations which were not done if the popups were disabled.
2020-08-10 22:59:06 +02:00
Patrick Goldinger
9a9445dab1 Merge pull request #8 from hamedsj/master
Add Disable Checkbox for "Preview Popup" feature
2020-08-10 21:54:45 +02:00
hamedsj
1fbfc32429 Add checkbox for disable "Preview PopUp" in keyboard settings 2020-08-10 23:45:17 +04:30
hamedsj
645b682451 Add Keshida character to untranslatable strings/keys 2020-08-10 22:56:42 +04:30
Patrick Goldinger
63ed46ccf4 Release v0.1.1 2020-08-09 20:43:01 +02:00
Patrick Goldinger
236f682622 Fix LayoutManager incorrectly merging main and mod layout
- Related to issue mentioned in #6
2020-08-09 20:23:27 +02:00
Patrick Goldinger
bef69b3187 Merge pull request #6 from PHELAT/master
Add support for persian keyboard layout
2020-08-09 20:14:03 +02:00
Mahdi Nouri
235224aefd Move the delete key back to the modifier layout 2020-08-09 16:54:09 +04:30
qw123wh
2489872589 Add Italian PlayStore translation by Qw123wh (#5)
- Update full_description.txt
- Add files via upload
- Update full_description.txt
2020-08-09 09:35:53 +02:00
Mahdi Nouri
33a9504707 Add support for persian layout 2020-08-08 18:46:45 +04:30
Mahdi Nouri
0fb7bbb034 Add support for half-space key with an appropriate icon 2020-08-08 17:53:37 +04:30
Mahdi Nouri
beef54941f Add the ability to load a customized layout modifier for each layout 2020-08-08 16:58:49 +04:30
Patrick Goldinger
634ff4972d Show number row in Smartbar if composing word suggestions is disabled 2020-08-08 11:27:22 +02:00
Patrick Goldinger
52f3477e24 Merge branch 'master' of https://github.com/florisboard/florisboard 2020-08-07 21:24:29 +02:00
Patrick Goldinger
1086464b09 Add string resources for preferences items / Fix apostrophe issues 2020-08-07 21:24:10 +02:00
Patrick Goldinger
2e4267aad4 Update full_description.txt
- Add supported keyboard layouts to the list
2020-08-07 19:07:48 +02:00
Patrick Goldinger
b14fe8ad03 Merge pull request #4 from qw123wh/qw123wh--italian-translate
Add Italian translation
2020-08-07 17:42:00 +02:00
qw123wh
82d6141fb2 Italian translation
Add Italian translation
2020-08-07 14:48:41 +02:00
Patrick Goldinger
e0e7bcc08a Split strings.xml into translatable and non-translatable file
- Done to easily identify which strings should be localized and which not
- Add translating guide in CONTRIBUTING.md
2020-08-06 23:08:27 +02:00
52 changed files with 1866 additions and 407 deletions

View File

@@ -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!

View File

@@ -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

View File

@@ -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 (?)

View File

@@ -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'

View File

@@ -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
}
]
}

View File

@@ -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"}
]
}

View 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" }
] }
]
]
}

View 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": "چ" }
]
]
}

View File

@@ -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,

View File

@@ -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.
*/

View File

@@ -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)
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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)
}
}
}

View File

@@ -18,6 +18,7 @@ package dev.patrickgold.florisboard.ime.text.keyboard
enum class KeyboardMode {
CHARACTERS,
EDITING,
SYMBOLS,
SYMBOLS2,
NUMERIC,

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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
}
}
}

View File

@@ -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.
*/

View File

@@ -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)
}

View File

@@ -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
}
}

View File

@@ -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>

View File

@@ -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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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>

View 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 &amp; Emotions</string>
<string name="emoji__category__people_body">Persone &amp; Corpo</string>
<string name="emoji__category__animals_nature">Animali &amp; Natura</string>
<string name="emoji__category__food_drink">Cibo &amp; Bevande</string>
<string name="emoji__category__travel_places">Viaggi &amp; 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 &amp; feedback</string>
<string name="settings__navigation__home">Home</string>
<string name="settings__navigation__keyboard">Tastiera</string>
<string name="settings__navigation__looknfeel">Aspetto &amp; 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 &amp; 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 &amp; 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 &amp; Input</i>impostazioni, quindi assicurati di selezionare\'<i>FlorisBoard</i>\'.</string>
<string name="setup__enable_ime__text_button_language_and_input">Apri lingue &amp; 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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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">=\\&lt;</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 &amp; 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 &amp; 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 &amp; 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 -->

View 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">=\\&lt;</string>
<string name="key__view_half_space" translatable="false">&#8626;</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>

View File

@@ -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>

View File

@@ -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 -->

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -0,0 +1,17 @@
FlorisBoard &egrave una tastiera open source mirata a fornirti un modo semplice per digitare nel rispetto della tua privacy.
Nota: questo progetto &egrave 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&agrave; 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&agrave; 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

View File

@@ -0,0 +1 @@
Una tastiera open source. Attualmente in fase alfa.

View File

@@ -0,0 +1 @@
FlorisBoard