Skip to content

Commit f68396d

Browse files
authored
Merge branch 'main' into scandinavian_language_layouts
2 parents a85d924 + c9218bc commit f68396d

File tree

50 files changed

+245
-75
lines changed

Some content is hidden

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

50 files changed

+245
-75
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: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,61 @@
11
package com.simplemobiletools.keyboard.helpers
22

33
import android.content.Context
4-
import androidx.annotation.StringRes
5-
import com.simplemobiletools.keyboard.R
4+
import com.simplemobiletools.keyboard.extensions.config
5+
import com.simplemobiletools.keyboard.helpers.MyKeyboard.Companion.KEYCODE_SPACE
66

7-
const val SHIFT_OFF = 0
8-
const val SHIFT_ON_ONE_CHAR = 1
9-
const val SHIFT_ON_PERMANENT = 2
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+
}
1059

1160
// limit the count of alternative characters that show up at long pressing a key
1261
const val MAX_KEYS_PER_MINI_ROW = 9
@@ -15,6 +64,7 @@ const val MAX_KEYS_PER_MINI_ROW = 9
1564
const val VIBRATE_ON_KEYPRESS = "vibrate_on_keypress"
1665
const val SHOW_POPUP_ON_KEYPRESS = "show_popup_on_keypress"
1766
const val SHOW_KEY_BORDERS = "show_key_borders"
67+
const val SENTENCES_CAPITALIZATION = "sentences_capitalization"
1868
const val LAST_EXPORTED_CLIPS_FOLDER = "last_exported_clips_folder"
1969
const val KEYBOARD_LANGUAGE = "keyboard_language"
2070
const val HEIGHT_MULTIPLIER = "height_multiplier"

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

Lines changed: 11 additions & 8 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
@@ -201,8 +201,14 @@ class MyKeyboard {
201201

202202
a.recycle()
203203
a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.MyKeyboard_Key)
204+
205+
label = a.getText(R.styleable.MyKeyboard_Key_keyLabel) ?: ""
204206
code = a.getInt(R.styleable.MyKeyboard_Key_code, 0)
205207

208+
if (label.isNotEmpty() && code == 0) {
209+
code = label[0].code
210+
}
211+
206212
popupCharacters = a.getText(R.styleable.MyKeyboard_Key_popupCharacters)
207213
popupResId = a.getResourceId(R.styleable.MyKeyboard_Key_popupKeyboard, 0)
208214
repeatable = a.getBoolean(R.styleable.MyKeyboard_Key_isRepeatable, false)
@@ -213,12 +219,9 @@ class MyKeyboard {
213219
secondaryIcon = a.getDrawable(R.styleable.MyKeyboard_Key_secondaryKeyIcon)
214220
secondaryIcon?.setBounds(0, 0, secondaryIcon!!.intrinsicWidth, secondaryIcon!!.intrinsicHeight)
215221

216-
label = a.getText(R.styleable.MyKeyboard_Key_keyLabel) ?: ""
217222
topSmallNumber = a.getString(R.styleable.MyKeyboard_Key_topSmallNumber) ?: ""
218223

219-
if (label.isNotEmpty() && code != KEYCODE_MODE_CHANGE && code != KEYCODE_SHIFT) {
220-
code = label[0].code
221-
}
224+
222225
a.recycle()
223226
}
224227

@@ -255,13 +258,14 @@ class MyKeyboard {
255258
* @param enterKeyType determines what icon should we show on Enter key
256259
*/
257260
@JvmOverloads
258-
constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int) {
261+
constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int, shiftState: ShiftState = ShiftState.OFF) {
259262
mDisplayWidth = context.resources.displayMetrics.widthPixels
260263
mDefaultHorizontalGap = 0
261264
mDefaultWidth = mDisplayWidth / 10
262265
mDefaultHeight = mDefaultWidth
263266
mKeyboardHeightMultiplier = getKeyboardHeightMultiplier(context.config.keyboardHeightMultiplier)
264267
mKeys = ArrayList()
268+
mShiftState = shiftState
265269
mEnterKeyType = enterKeyType
266270
loadKeyboard(context, context.resources.getXml(xmlLayoutResId))
267271
}
@@ -312,12 +316,11 @@ class MyKeyboard {
312316
mRows.add(row)
313317
}
314318

315-
fun setShifted(shiftState: Int): Boolean {
319+
fun setShifted(shiftState: ShiftState): Boolean {
316320
if (this.mShiftState != shiftState) {
317321
this.mShiftState = shiftState
318322
return true
319323
}
320-
321324
return false
322325
}
323326

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

Lines changed: 48 additions & 24 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.
167180
if (keyboardMode != KEYBOARD_LETTERS && code == MyKeyboard.KEYCODE_SPACE) {
168-
val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text ?: return
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) {

0 commit comments

Comments
 (0)