Compare commits

...

14 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
43 changed files with 1439 additions and 332 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

@@ -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 10
versionName "0.1.1"
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

@@ -44,7 +44,8 @@
{ "code": 1577, "label": "ة" }
],
"ک": [
{ "code": 1603, "label": "ك" }
{ "code": 1603, "label": "ك" },
{ "code": 1706, "label": "ڪ"}
],
"ز": [
{ "code": 1688, "label": "ژ" }

View File

@@ -21,6 +21,7 @@
] },
{ "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" },

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()
@@ -248,14 +263,19 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
/**
* 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.isQuickActionsVisible = false
isManualSelectionMode = false
isManualSelectionModeLeft = false
isManualSelectionModeRight = false
}
override fun onSubtypeChanged(newSubtype: Subtype) {
@@ -272,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
@@ -300,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)
}
/**
@@ -400,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.
*/
@@ -407,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()
}
@@ -425,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,
@@ -441,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()
@@ -499,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
@@ -510,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

@@ -70,5 +70,6 @@ object KeyCode {
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.code != KeyCode.HALF_SPACE || data.type == KeyType.NUMERIC
&& data.code != KeyCode.HALF_SPACE && data.code != KeyCode.KESHIDA || data.type == KeyType.NUMERIC
) {
label = getComputedLetter()
} else {
@@ -516,6 +520,9 @@ class KeyView(
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

@@ -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,17 +12,16 @@ 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
@@ -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,18 +51,37 @@ 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 ->
isQuickActionsVisible = false
when (v.id) {
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 {
isQuickActionsVisible = !isQuickActionsVisible
@@ -89,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) {
@@ -99,13 +114,23 @@ 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() {
@@ -119,40 +144,14 @@ 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.PHONE, KeyboardMode.PHONE2 -> {
smartbarView?.visibility = View.GONE
smartbarView?.setActiveVariant(null)
}
else -> {
smartbarView?.visibility = View.VISIBLE
smartbarView?.setActiveVariant(R.id.smartbar_variant_default)
isQuickActionsVisible = false
}
}
@@ -162,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) {
//
}
@@ -202,39 +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
if (isQuickActionsVisible) {
smartbarView.candidatesView?.visibility = View.GONE
smartbarView.numberRowView?.visibility = View.GONE
smartbarView.quickActionsView?.visibility = View.VISIBLE
smartbarView.quickActionToggle?.rotation = -180.0f
smartbarView.setActiveContainer(R.id.quick_actions)
smartbarView.findViewById<View>(R.id.quick_action_toggle)?.rotation = -180.0f
} else {
if (florisboard.prefs.suggestion.enabled) {
smartbarView.candidatesView?.visibility = View.VISIBLE
smartbarView.numberRowView?.visibility = View.GONE
smartbarView.quickActionsView?.visibility = View.GONE
if (isComposingEnabled) {
smartbarView.setActiveContainer(R.id.candidates)
} else if (textInputManager.getActiveKeyboardMode() == KeyboardMode.CHARACTERS) {
smartbarView.candidatesView?.visibility = View.GONE
smartbarView.numberRowView?.visibility = View.VISIBLE
smartbarView.quickActionsView?.visibility = View.GONE
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.candidatesView?.visibility = View.GONE
smartbarView.numberRowView?.visibility = View.GONE
smartbarView.quickActionsView?.visibility = View.GONE
smartbarView.setActiveContainer(null)
}
smartbarView.quickActionToggle?.rotation = 0.0f
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

@@ -58,8 +58,11 @@
<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>

View File

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

@@ -21,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 -->
@@ -35,6 +37,7 @@
<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>
@@ -56,11 +59,17 @@
<string name="pref__suggestion__title">Suggestions</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>

View File

@@ -11,6 +11,7 @@
<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>

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>