Skip to content

Commit 90a0805

Browse files
authored
Merge branch 'main' into weblate-simple-mobile-tools-simple-keyboard
2 parents e63f08d + f4d24c7 commit 90a0805

File tree

49 files changed

+211
-103
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+211
-103
lines changed

app/src/main/kotlin/com/simplemobiletools/keyboard/activities/SettingsActivity.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class SettingsActivity : SimpleActivity() {
4444
setupKeyboardLanguage()
4545
setupKeyboardHeightMultiplier()
4646
setupShowClipboardContent()
47+
setupSentencesCapitalization()
4748
setupShowNumbersRow()
4849

4950
updateTextColors(settings_nested_scrollview)
@@ -160,6 +161,15 @@ class SettingsActivity : SimpleActivity() {
160161
config.showClipboardContent = settings_show_clipboard_content.isChecked
161162
}
162163
}
164+
165+
private fun setupSentencesCapitalization() {
166+
settings_start_sentences_capitalized.isChecked = config.enableSentencesCapitalization
167+
settings_start_sentences_capitalized_holder.setOnClickListener {
168+
settings_start_sentences_capitalized.toggle()
169+
config.enableSentencesCapitalization = settings_start_sentences_capitalized.isChecked
170+
}
171+
}
172+
163173
private fun setupShowNumbersRow() {
164174
settings_show_numbers_row.isChecked = config.showNumbersRow
165175
settings_show_numbers_row_holder.setOnClickListener {

app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/Config.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ class Config(context: Context) : BaseConfig(context) {
1717
get() = prefs.getBoolean(SHOW_POPUP_ON_KEYPRESS, true)
1818
set(showPopupOnKeypress) = prefs.edit().putBoolean(SHOW_POPUP_ON_KEYPRESS, showPopupOnKeypress).apply()
1919

20+
var enableSentencesCapitalization: Boolean
21+
get() = prefs.getBoolean(SENTENCES_CAPITALIZATION, true)
22+
set(enableCapitalization) = prefs.edit().putBoolean(SENTENCES_CAPITALIZATION, enableCapitalization).apply()
23+
2024
var showKeyBorders: Boolean
2125
get() = prefs.getBoolean(SHOW_KEY_BORDERS, false)
2226
set(showKeyBorders) = prefs.edit().putBoolean(SHOW_KEY_BORDERS, showKeyBorders).apply()

app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/Constants.kt

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,61 @@
11
package com.simplemobiletools.keyboard.helpers
22

3-
const val SHIFT_OFF = 0
4-
const val SHIFT_ON_ONE_CHAR = 1
5-
const val SHIFT_ON_PERMANENT = 2
3+
import android.content.Context
4+
import com.simplemobiletools.keyboard.extensions.config
5+
import com.simplemobiletools.keyboard.helpers.MyKeyboard.Companion.KEYCODE_SPACE
6+
7+
enum class ShiftState {
8+
OFF,
9+
ON_ONE_CHAR,
10+
ON_PERMANENT;
11+
12+
companion object {
13+
private val endOfSentenceChars: List<Char> = listOf('.', '?', '!')
14+
15+
fun getDefaultShiftState(context: Context): ShiftState {
16+
return when (context.config.enableSentencesCapitalization) {
17+
true -> ON_ONE_CHAR
18+
else -> OFF
19+
}
20+
}
21+
22+
fun getShiftStateForText(context: Context, newText: String?): ShiftState {
23+
if (!context.config.enableSentencesCapitalization) {
24+
return OFF
25+
}
26+
27+
val twoLastSymbols = newText?.takeLast(2)
28+
return when {
29+
shouldCapitalizeSentence(previousChar = twoLastSymbols?.getOrNull(0), currentChar = twoLastSymbols?.getOrNull(1)) -> {
30+
ON_ONE_CHAR
31+
}
32+
else -> {
33+
OFF
34+
}
35+
}
36+
}
37+
38+
fun getCapitalizationOnDelete(context: Context, text: CharSequence?): ShiftState {
39+
if (!context.config.enableSentencesCapitalization) {
40+
return OFF
41+
}
42+
43+
return if (text.isNullOrEmpty() || shouldCapitalizeSentence(currentChar = text.last(), previousChar = text.getOrNull(text.lastIndex - 1))) {
44+
ON_ONE_CHAR
45+
} else {
46+
OFF
47+
}
48+
}
49+
50+
private fun shouldCapitalizeSentence(previousChar: Char?, currentChar: Char?): Boolean {
51+
if (previousChar == null || currentChar == null) {
52+
return false
53+
}
54+
55+
return currentChar.code == KEYCODE_SPACE && endOfSentenceChars.contains(previousChar)
56+
}
57+
}
58+
}
659

760
// limit the count of alternative characters that show up at long pressing a key
861
const val MAX_KEYS_PER_MINI_ROW = 9
@@ -11,6 +64,7 @@ const val MAX_KEYS_PER_MINI_ROW = 9
1164
const val VIBRATE_ON_KEYPRESS = "vibrate_on_keypress"
1265
const val SHOW_POPUP_ON_KEYPRESS = "show_popup_on_keypress"
1366
const val SHOW_KEY_BORDERS = "show_key_borders"
67+
const val SENTENCES_CAPITALIZATION = "sentences_capitalization"
1468
const val LAST_EXPORTED_CLIPS_FOLDER = "last_exported_clips_folder"
1569
const val KEYBOARD_LANGUAGE = "keyboard_language"
1670
const val HEIGHT_MULTIPLIER = "height_multiplier"

app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/MyKeyboard.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class MyKeyboard {
3434
var mKeyboardHeightMultiplier: Float = 1F
3535

3636
/** Is the keyboard in the shifted state */
37-
var mShiftState = SHIFT_OFF
37+
var mShiftState = ShiftState.OFF
3838

3939
/** Total height of the keyboard, including the padding and keys */
4040
var mHeight = 0
@@ -258,13 +258,14 @@ class MyKeyboard {
258258
* @param enterKeyType determines what icon should we show on Enter key
259259
*/
260260
@JvmOverloads
261-
constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int) {
261+
constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int, shiftState: ShiftState = ShiftState.OFF) {
262262
mDisplayWidth = context.resources.displayMetrics.widthPixels
263263
mDefaultHorizontalGap = 0
264264
mDefaultWidth = mDisplayWidth / 10
265265
mDefaultHeight = mDefaultWidth
266266
mKeyboardHeightMultiplier = getKeyboardHeightMultiplier(context.config.keyboardHeightMultiplier)
267267
mKeys = ArrayList()
268+
mShiftState = shiftState
268269
mEnterKeyType = enterKeyType
269270
loadKeyboard(context, context.resources.getXml(xmlLayoutResId))
270271
}
@@ -315,12 +316,11 @@ class MyKeyboard {
315316
mRows.add(row)
316317
}
317318

318-
fun setShifted(shiftState: Int): Boolean {
319+
fun setShifted(shiftState: ShiftState): Boolean {
319320
if (this.mShiftState != shiftState) {
320321
this.mShiftState = shiftState
321322
return true
322323
}
323-
324324
return false
325325
}
326326

app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import com.simplemobiletools.keyboard.R
2020
import com.simplemobiletools.keyboard.extensions.config
2121
import com.simplemobiletools.keyboard.helpers.*
2222
import com.simplemobiletools.keyboard.views.MyKeyboardView
23-
import kotlinx.android.synthetic.main.keyboard_view_keyboard.view.*
23+
import kotlinx.android.synthetic.main.keyboard_view_keyboard.view.keyboard_holder
24+
import kotlinx.android.synthetic.main.keyboard_view_keyboard.view.keyboard_view
2425

2526
// based on https://www.androidauthority.com/lets-build-custom-keyboard-android-832362/
2627
class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionListener, SharedPreferences.OnSharedPreferenceChangeListener {
@@ -41,7 +42,6 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
4142

4243
override fun onInitializeInterface() {
4344
super.onInitializeInterface()
44-
keyboard = MyKeyboard(this, getKeyboardLayoutXML(), enterKeyType)
4545
getSharedPrefs().registerOnSharedPreferenceChangeListener(this)
4646
}
4747

@@ -65,8 +65,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
6565
super.onStartInput(attribute, restarting)
6666
inputTypeClass = attribute!!.inputType and TYPE_MASK_CLASS
6767
enterKeyType = attribute.imeOptions and (IME_MASK_ACTION or IME_FLAG_NO_ENTER_ACTION)
68-
69-
keyboard = getKeyBoard()
68+
keyboard = createNewKeyboard()
7069
keyboardView?.setKeyboard(keyboard!!)
7170
keyboardView?.setEditorInfo(attribute)
7271
updateShiftKeyState()
@@ -75,9 +74,9 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
7574
private fun updateShiftKeyState() {
7675
if (keyboardMode == KEYBOARD_LETTERS) {
7776
val editorInfo = currentInputEditorInfo
78-
if (editorInfo != null && editorInfo.inputType != InputType.TYPE_NULL && keyboard?.mShiftState != SHIFT_ON_PERMANENT) {
77+
if (editorInfo != null && editorInfo.inputType != InputType.TYPE_NULL && keyboard?.mShiftState != ShiftState.ON_PERMANENT) {
7978
if (currentInputConnection.getCursorCapsMode(editorInfo.inputType) != 0) {
80-
keyboard?.setShifted(SHIFT_ON_ONE_CHAR)
79+
keyboard?.setShifted(ShiftState.ON_ONE_CHAR)
8180
keyboardView?.invalidateAllKeys()
8281
}
8382
}
@@ -96,8 +95,10 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
9695

9796
when (code) {
9897
MyKeyboard.KEYCODE_DELETE -> {
99-
if (keyboard!!.mShiftState == SHIFT_ON_ONE_CHAR) {
100-
keyboard!!.mShiftState = SHIFT_OFF
98+
99+
if (keyboard!!.mShiftState != ShiftState.ON_PERMANENT) {
100+
val extractedText = inputConnection.getTextBeforeCursor(3, 0)?.dropLast(1)
101+
keyboard!!.setShifted(ShiftState.getCapitalizationOnDelete(context = this, text = extractedText))
101102
}
102103

103104
val selectedText = inputConnection.getSelectedText(0)
@@ -109,13 +110,14 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
109110
}
110111
keyboardView!!.invalidateAllKeys()
111112
}
113+
112114
MyKeyboard.KEYCODE_SHIFT -> {
113115
if (keyboardMode == KEYBOARD_LETTERS) {
114116
when {
115-
keyboard!!.mShiftState == SHIFT_ON_PERMANENT -> keyboard!!.mShiftState = SHIFT_OFF
116-
System.currentTimeMillis() - lastShiftPressTS < SHIFT_PERM_TOGGLE_SPEED -> keyboard!!.mShiftState = SHIFT_ON_PERMANENT
117-
keyboard!!.mShiftState == SHIFT_ON_ONE_CHAR -> keyboard!!.mShiftState = SHIFT_OFF
118-
keyboard!!.mShiftState == SHIFT_OFF -> keyboard!!.mShiftState = SHIFT_ON_ONE_CHAR
117+
keyboard!!.mShiftState == ShiftState.ON_PERMANENT -> keyboard!!.mShiftState = ShiftState.OFF
118+
System.currentTimeMillis() - lastShiftPressTS < SHIFT_PERM_TOGGLE_SPEED -> keyboard!!.mShiftState = ShiftState.ON_PERMANENT
119+
keyboard!!.mShiftState == ShiftState.ON_ONE_CHAR -> keyboard!!.mShiftState = ShiftState.OFF
120+
keyboard!!.mShiftState == ShiftState.OFF -> keyboard!!.mShiftState = ShiftState.ON_ONE_CHAR
119121
}
120122

121123
lastShiftPressTS = System.currentTimeMillis()
@@ -132,15 +134,22 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
132134
}
133135
keyboardView!!.invalidateAllKeys()
134136
}
137+
135138
MyKeyboard.KEYCODE_ENTER -> {
136139
val imeOptionsActionId = getImeOptionsActionId()
137140
if (imeOptionsActionId != IME_ACTION_NONE) {
138141
inputConnection.performEditorAction(imeOptionsActionId)
139142
} else {
140143
inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER))
141144
inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
145+
146+
if (config.enableSentencesCapitalization) {
147+
keyboard!!.setShifted(ShiftState.ON_ONE_CHAR)
148+
keyboardView!!.invalidateAllKeys()
149+
}
142150
}
143151
}
152+
144153
MyKeyboard.KEYCODE_MODE_CHANGE -> {
145154
val keyboardXml = if (keyboardMode == KEYBOARD_LETTERS) {
146155
keyboardMode = KEYBOARD_SYMBOLS
@@ -152,48 +161,58 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
152161
keyboard = MyKeyboard(this, keyboardXml, enterKeyType)
153162
keyboardView!!.setKeyboard(keyboard!!)
154163
}
164+
155165
MyKeyboard.KEYCODE_EMOJI -> {
156166
keyboardView?.openEmojiPalette()
157167
}
168+
158169
else -> {
159170
var codeChar = code.toChar()
160-
if (Character.isLetter(codeChar) && keyboard!!.mShiftState > SHIFT_OFF) {
171+
val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text ?: return
172+
173+
if (Character.isLetter(codeChar) && keyboard!!.mShiftState > ShiftState.OFF) {
161174
codeChar = Character.toUpperCase(codeChar)
162175
}
163176

164177
// If the keyboard is set to symbols and the user presses space, we usually should switch back to the letters keyboard.
165178
// However, avoid doing that in cases when the EditText for example requires numbers as the input.
166179
// We can detect that by the text not changing on pressing Space.
167-
if (keyboardMode != KEYBOARD_LETTERS && keyboardMode != KEYBOARD_PHONE && code == MyKeyboard.KEYCODE_SPACE) {
168-
val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text ?: return
180+
if (keyboardMode != KEYBOARD_LETTERS && code == MyKeyboard.KEYCODE_SPACE) {
169181
inputConnection.commitText(codeChar.toString(), 1)
170182
val newText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text
171-
switchToLetters = originalText != newText
183+
if (originalText != newText) {
184+
switchToLetters = true
185+
}
172186
} else {
173187
inputConnection.commitText(codeChar.toString(), 1)
174188
}
175189

176-
if (keyboard!!.mShiftState == SHIFT_ON_ONE_CHAR && keyboardMode == KEYBOARD_LETTERS) {
177-
keyboard!!.mShiftState = SHIFT_OFF
190+
if (keyboardMode == KEYBOARD_LETTERS && keyboard!!.mShiftState != ShiftState.ON_PERMANENT) {
191+
keyboard!!.setShifted(ShiftState.getShiftStateForText(this, newText = "$originalText$codeChar"))
178192
keyboardView!!.invalidateAllKeys()
179193
}
194+
180195
}
181196
}
182197

183-
if (code != MyKeyboard.KEYCODE_SHIFT) {
198+
if (code != MyKeyboard.KEYCODE_SHIFT && config.enableSentencesCapitalization) {
184199
updateShiftKeyState()
185200
}
186201
}
187202

188203
override fun onActionUp() {
189204
if (switchToLetters) {
205+
// TODO: Change keyboardMode to enum class
190206
keyboardMode = KEYBOARD_LETTERS
191-
keyboard = MyKeyboard(this, getKeyboardLayoutXML(), enterKeyType)
207+
val text = currentInputConnection?.getExtractedText(ExtractedTextRequest(), 0)?.text
208+
val newShiftState = ShiftState.getShiftStateForText(this, text?.toString().orEmpty())
209+
210+
keyboard = MyKeyboard(this, getKeyboardLayoutXML(), enterKeyType, shiftState = newShiftState)
192211

193212
val editorInfo = currentInputEditorInfo
194-
if (editorInfo != null && editorInfo.inputType != InputType.TYPE_NULL && keyboard?.mShiftState != SHIFT_ON_PERMANENT) {
213+
if (editorInfo != null && editorInfo.inputType != InputType.TYPE_NULL && keyboard?.mShiftState != ShiftState.ON_PERMANENT) {
195214
if (currentInputConnection.getCursorCapsMode(editorInfo.inputType) != 0) {
196-
keyboard?.setShifted(SHIFT_ON_ONE_CHAR)
215+
keyboard?.setShifted(ShiftState.ON_ONE_CHAR)
197216
}
198217
}
199218

@@ -215,32 +234,37 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
215234
}
216235

217236
override fun reloadKeyboard() {
218-
val keyboard = getKeyBoard()
237+
val keyboard = createNewKeyboard()
219238
this.keyboard = keyboard
220239
keyboardView?.setKeyboard(keyboard)
221240
}
222241

223-
private fun getKeyBoard(): MyKeyboard {
242+
private fun createNewKeyboard(): MyKeyboard {
224243
val keyboardXml = when (inputTypeClass) {
225244
TYPE_CLASS_NUMBER -> {
226245
keyboardMode = KEYBOARD_NUMBERS
227246
R.xml.keys_numbers
228247
}
248+
229249
TYPE_CLASS_PHONE -> {
230250
keyboardMode = KEYBOARD_PHONE
231251
R.xml.keys_phone
232252
}
253+
233254
TYPE_CLASS_DATETIME -> {
234255
keyboardMode = KEYBOARD_SYMBOLS
235256
R.xml.keys_symbols
236257
}
258+
237259
else -> {
238260
keyboardMode = KEYBOARD_LETTERS
239261
getKeyboardLayoutXML()
240262
}
241263
}
242264

243-
return MyKeyboard(this, keyboardXml, enterKeyType)
265+
return MyKeyboard(
266+
context = this, xmlLayoutResId = keyboardXml, enterKeyType = enterKeyType, shiftState = ShiftState.getDefaultShiftState(this)
267+
)
244268
}
245269

246270
override fun onUpdateSelection(oldSelStart: Int, oldSelEnd: Int, newSelStart: Int, newSelEnd: Int, candidatesStart: Int, candidatesEnd: Int) {

app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut
455455
* @param shifted whether or not to enable the state of the shift key
456456
* @return true if the shift key state changed, false if there was no change
457457
*/
458-
private fun setShifted(shiftState: Int) {
458+
private fun setShifted(shiftState: ShiftState) {
459459
if (mKeyboard?.setShifted(shiftState) == true) {
460460
invalidateAllKeys()
461461
}
@@ -466,7 +466,7 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut
466466
* @return true if the shift is in a pressed state, false otherwise
467467
*/
468468
private fun isShifted(): Boolean {
469-
return (mKeyboard?.mShiftState ?: SHIFT_OFF) > SHIFT_OFF
469+
return (mKeyboard?.mShiftState ?: ShiftState.OFF) > ShiftState.OFF
470470
}
471471

472472
private fun setPopupOffset(x: Int, y: Int) {
@@ -479,7 +479,7 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut
479479

480480
private fun adjustCase(label: CharSequence): CharSequence? {
481481
var newLabel: CharSequence? = label
482-
if (newLabel != null && newLabel.isNotEmpty() && mKeyboard!!.mShiftState > SHIFT_OFF && newLabel.length < 3 && Character.isLowerCase(newLabel[0])) {
482+
if (newLabel != null && newLabel.isNotEmpty() && mKeyboard!!.mShiftState != ShiftState.OFF && newLabel.length < 3 && Character.isLowerCase(newLabel[0])) {
483483
newLabel = newLabel.toString().uppercase(Locale.getDefault())
484484
}
485485
return newLabel
@@ -604,8 +604,8 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut
604604
} else if (key.icon != null && mKeyboard != null) {
605605
if (code == KEYCODE_SHIFT) {
606606
val drawableId = when (mKeyboard!!.mShiftState) {
607-
SHIFT_OFF -> R.drawable.ic_caps_outline_vector
608-
SHIFT_ON_ONE_CHAR -> R.drawable.ic_caps_vector
607+
ShiftState.OFF -> R.drawable.ic_caps_outline_vector
608+
ShiftState.ON_ONE_CHAR -> R.drawable.ic_caps_vector
609609
else -> R.drawable.ic_caps_underlined_vector
610610
}
611611
key.icon = resources.getDrawable(drawableId)
@@ -1131,7 +1131,7 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut
11311131
mMiniKeyboardSelectedKeyIndex = selectedKeyIndex
11321132
mMiniKeyboard!!.invalidateAllKeys()
11331133

1134-
val miniShiftStatus = if (isShifted()) SHIFT_ON_PERMANENT else SHIFT_OFF
1134+
val miniShiftStatus = if (isShifted()) ShiftState.ON_PERMANENT else ShiftState.OFF
11351135
mMiniKeyboard!!.setShifted(miniShiftStatus)
11361136
mPopupKeyboard.contentView = mMiniKeyboardContainer
11371137
mPopupKeyboard.width = mMiniKeyboardContainer!!.measuredWidth

0 commit comments

Comments
 (0)