Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a25501d63c | ||
|
|
e9a5f2161c | ||
|
|
6f12f22937 | ||
|
|
25054ef679 | ||
|
|
3af17f99fe | ||
|
|
06664ff521 | ||
|
|
fde0749a3b | ||
|
|
c061e15263 | ||
|
|
7256c597c2 | ||
|
|
2f9d32027b | ||
|
|
538912edc2 | ||
|
|
ee5ff81ee8 | ||
|
|
d873dc54c5 | ||
|
|
1e967463de | ||
|
|
5c084a10dc | ||
|
|
f158a9deb3 | ||
|
|
66d328293c | ||
|
|
e33b652bb3 | ||
|
|
65d8c02b95 | ||
|
|
bd090132eb | ||
|
|
0eb5ca318b | ||
|
|
dfa9df6cd6 | ||
|
|
3f5dfbc852 | ||
|
|
59caafbf19 | ||
|
|
037a452baf | ||
|
|
ffa405f289 | ||
|
|
5d7091582f | ||
|
|
b4096f2cfb | ||
|
|
81c62f3e91 | ||
|
|
5c7db2b344 | ||
|
|
30bca99092 | ||
|
|
9a9445dab1 | ||
|
|
1fbfc32429 | ||
|
|
645b682451 |
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,15 +7,5 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Short description of your idea
|
||||
A short but clear and concise description of your idea.
|
||||
|
||||
#### Detailed description of your idea
|
||||
A clear and concise description of what you want to be added or changed. If you also have
|
||||
an idea how to implement it, please describe it here.
|
||||
|
||||
#### Alternatives to your idea
|
||||
If you have considered an alternative solution for your idea, describe it here.
|
||||
|
||||
#### Additional context
|
||||
Add any other context or screenshots about the feature request here.
|
||||
Describe your idea in a short but concise way. If you have multiple ideas which are not directly connected to each other, file an issue per idea. This makes it easy to implement one feature proposal at a time. If you have any examples, e.g. screenshots or other keyboards which have the proposed feature implemented, link them here.
|
||||
Thank you for your help in making FlorisBoard better!
|
||||
|
||||
14
README.md
14
README.md
@@ -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
|
||||
@@ -69,7 +69,7 @@ timeline for this, but I aim for the 0.2.0 or 0.3.0 release.
|
||||
* [x] Preferences screen
|
||||
* [x] Customize look and behaviour of keyboard (currently only
|
||||
light/dark theme)
|
||||
* [ ] Theme customization
|
||||
* [x] Theme customization
|
||||
* [ ] Theme import/export (?)
|
||||
* [x] Subtype selection (language/layout)
|
||||
* [x] Keyboard behaviour preferences
|
||||
@@ -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 (?)
|
||||
@@ -97,13 +97,15 @@ timeline for this, but I aim for the 0.2.0 or 0.3.0 release.
|
||||
|
||||
Note: (?) = not sure if it will be implemented
|
||||
|
||||
## Used libraries and icons
|
||||
## Used libraries, components and icons
|
||||
* [Google Flexbox Layout for Android](https://github.com/google/flexbox-layout)
|
||||
by [google](https://github.com/google)
|
||||
* [Google Material icons](https://github.com/google/material-design-icons) by
|
||||
[google](https://github.com/google)
|
||||
* [Moshi JSON library](https://github.com/square/moshi) by
|
||||
[square](https://github.com/square)
|
||||
* [ColorPicker preference](https://github.com/jaredrummler/ColorPicker) by
|
||||
[Jared Rummler](https://github.com/jaredrummler)
|
||||
|
||||
## License
|
||||
```
|
||||
|
||||
@@ -10,8 +10,8 @@ android {
|
||||
applicationId "dev.patrickgold.florisboard"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 10
|
||||
versionName "0.1.1"
|
||||
versionCode 12
|
||||
versionName "0.2.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -31,9 +31,11 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.1'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'androidx.test:core:1.2.0'
|
||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||
@@ -42,7 +44,8 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
implementation 'com.google.android:flexbox:2.0.1'
|
||||
implementation "com.squareup.moshi:moshi-kotlin:1.9.2"
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.2.0'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"
|
||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
||||
}
|
||||
|
||||
@@ -74,6 +74,14 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/SettingsTheme"/>
|
||||
|
||||
<!-- Advanced Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.settings.AdvancedActivity"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/settings__advanced__title"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/SettingsTheme"/>
|
||||
|
||||
<!-- Setup Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.setup.SetupActivity"
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
{ "code": 1577, "label": "ة" }
|
||||
],
|
||||
"ک": [
|
||||
{ "code": 1603, "label": "ك" }
|
||||
{ "code": 1603, "label": "ك" },
|
||||
{ "code": 1706, "label": "ڪ"}
|
||||
],
|
||||
"ز": [
|
||||
{ "code": 1688, "label": "ژ" }
|
||||
|
||||
@@ -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" },
|
||||
|
||||
60
app/src/main/assets/ime/theme/floris_day.json
Normal file
60
app/src/main/assets/ime/theme/floris_day.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "floris_day",
|
||||
"displayName": "Floris Day",
|
||||
"author": "patrickgold",
|
||||
"isNightTheme": false,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#4CAF50",
|
||||
"colorPrimaryDark": "#388E3C",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/bgColor",
|
||||
"navigationBarLight": "true",
|
||||
"semiTransparentColor": "#20000000",
|
||||
"textColor": "#000000"
|
||||
},
|
||||
"keyboard": {
|
||||
"bgColor": "#E0E0E0"
|
||||
},
|
||||
"key": {
|
||||
"bgColor": "#FFFFFF",
|
||||
"bgColorPressed": "#F5F5F5",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyEnter": {
|
||||
"bgColor": "@window/colorPrimary",
|
||||
"bgColorPressed": "@window/colorPrimaryDark",
|
||||
"fgColor": "#FFFFFF"
|
||||
},
|
||||
"keyPopup": {
|
||||
"bgColor": "#EEEEEE",
|
||||
"bgColorActive": "#BDBDBD",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyShift": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"bgColorPressed": "@key/bgColorPressed",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorCapsLock": "@window/colorAccent"
|
||||
},
|
||||
"media": {
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#757575"
|
||||
},
|
||||
"oneHanded": {
|
||||
"bgColor": "#E8F5E9"
|
||||
},
|
||||
"oneHandedButton": {
|
||||
"fgColor": "#424242"
|
||||
},
|
||||
"smartbar": {
|
||||
"bgColor": "transparent",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#8A8A8A"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"fgColor": "@key/fgColor"
|
||||
}
|
||||
}
|
||||
}
|
||||
60
app/src/main/assets/ime/theme/floris_night.json
Normal file
60
app/src/main/assets/ime/theme/floris_night.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "floris_night",
|
||||
"displayName": "Floris Night",
|
||||
"author": "patrickgold",
|
||||
"isNightTheme": true,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#4CAF50",
|
||||
"colorPrimaryDark": "#388E3C",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/bgColor",
|
||||
"navigationBarLight": "false",
|
||||
"semiTransparentColor": "#20FFFFFF",
|
||||
"textColor": "#FFFFFF"
|
||||
},
|
||||
"keyboard": {
|
||||
"bgColor": "#212121"
|
||||
},
|
||||
"key": {
|
||||
"bgColor": "#424242",
|
||||
"bgColorPressed": "#616161",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyEnter": {
|
||||
"bgColor": "@window/colorPrimary",
|
||||
"bgColorPressed": "@window/colorPrimaryDark",
|
||||
"fgColor": "#FFFFFF"
|
||||
},
|
||||
"keyPopup": {
|
||||
"bgColor": "#757575",
|
||||
"bgColorActive": "#BDBDBD",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyShift": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"bgColorPressed": "@key/bgColorPressed",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorCapsLock": "@window/colorAccent"
|
||||
},
|
||||
"media": {
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#BDBDBD"
|
||||
},
|
||||
"oneHanded": {
|
||||
"bgColor": "#1B5E20"
|
||||
},
|
||||
"oneHandedButton": {
|
||||
"fgColor": "#EEEEEE"
|
||||
},
|
||||
"smartbar": {
|
||||
"bgColor": "transparent",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#73FFFFFF"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"fgColor": "@key/fgColor"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,24 @@ 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.
|
||||
</pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>ColorPicker preference</h3>
|
||||
<span>Copyright 2016 Jared Rummler / Copyright 2015 Daniel Nilsson</span>
|
||||
<pre>
|
||||
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.
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
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.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.inputmethodservice.InputMethodService
|
||||
import android.media.AudioManager
|
||||
@@ -58,15 +60,20 @@ class FlorisBoard : InputMethodService() {
|
||||
private set
|
||||
|
||||
val context: Context
|
||||
get() = inputView?.context ?: this
|
||||
private var inputView: InputView? = null
|
||||
get() = inputWindowView?.context ?: this
|
||||
var inputView: InputView? = null
|
||||
private set
|
||||
private var inputWindowView: InputWindowView? = 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()
|
||||
|
||||
lateinit var subtypeManager: SubtypeManager
|
||||
lateinit var activeSubtype: Subtype
|
||||
private var currentThemeIsNight: Boolean = false
|
||||
private var currentThemeResId: Int = 0
|
||||
|
||||
val textInputManager: TextInputManager
|
||||
@@ -104,6 +111,18 @@ class FlorisBoard : InputMethodService() {
|
||||
fun getInstance(): FlorisBoard {
|
||||
return florisboardInstance!!
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun getInstanceOrNull(): FlorisBoard? {
|
||||
return florisboardInstance
|
||||
}
|
||||
|
||||
fun getDayNightBaseThemeId(isNightTheme: Boolean): Int {
|
||||
return when (isNightTheme) {
|
||||
true -> R.style.KeyboardThemeBase_Night
|
||||
else -> R.style.KeyboardThemeBase_Day
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
@@ -128,21 +147,23 @@ 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 = PrefHelper.getDefaultInstance(this)
|
||||
prefs.initDefaultPreferences()
|
||||
prefs.sync()
|
||||
subtypeManager = SubtypeManager(this, prefs)
|
||||
activeSubtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
|
||||
|
||||
currentThemeResId = prefs.theme.getSelectedThemeResId()
|
||||
currentThemeIsNight = prefs.internal.themeCurrentIsNight
|
||||
currentThemeResId = getDayNightBaseThemeId(currentThemeIsNight)
|
||||
setTheme(currentThemeResId)
|
||||
updateTheme()
|
||||
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
|
||||
super.onCreate()
|
||||
textInputManager.onCreate()
|
||||
mediaInputManager.onCreate()
|
||||
eventListeners.toList().forEach { it.onCreate() }
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@@ -151,12 +172,11 @@ class FlorisBoard : InputMethodService() {
|
||||
|
||||
baseContext.setTheme(currentThemeResId)
|
||||
|
||||
inputView = layoutInflater.inflate(R.layout.florisboard, null) as InputView
|
||||
inputWindowView = layoutInflater.inflate(R.layout.florisboard, null) as InputWindowView
|
||||
|
||||
textInputManager.onCreateInputView()
|
||||
mediaInputManager.onCreateInputView()
|
||||
eventListeners.toList().forEach { it.onCreateInputView() }
|
||||
|
||||
return inputView
|
||||
return inputWindowView
|
||||
}
|
||||
|
||||
fun registerInputView(inputView: InputView) {
|
||||
@@ -164,11 +184,11 @@ class FlorisBoard : InputMethodService() {
|
||||
|
||||
this.inputView = inputView
|
||||
initializeOneHandedEnvironment()
|
||||
updateTheme()
|
||||
updateSoftInputWindowLayoutParameters()
|
||||
updateOneHandedPanelVisibility()
|
||||
|
||||
textInputManager.onRegisterInputView(inputView)
|
||||
mediaInputManager.onRegisterInputView(inputView)
|
||||
eventListeners.toList().forEach { it.onRegisterInputView(inputView) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -177,48 +197,44 @@ class FlorisBoard : InputMethodService() {
|
||||
osHandler.removeCallbacksAndMessages(null)
|
||||
florisboardInstance = null
|
||||
|
||||
eventListeners.toList().forEach { it.onDestroy() }
|
||||
eventListeners.clear()
|
||||
super.onDestroy()
|
||||
textInputManager.onDestroy()
|
||||
mediaInputManager.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.toList().forEach { it.onStartInputView(info, restarting) }
|
||||
}
|
||||
|
||||
override fun onFinishInputView(finishingInput: Boolean) {
|
||||
currentInputConnection?.requestCursorUpdates(0)
|
||||
|
||||
super.onFinishInputView(finishingInput)
|
||||
textInputManager.onFinishInputView(finishingInput)
|
||||
mediaInputManager.onFinishInputView(finishingInput)
|
||||
eventListeners.toList().forEach { it.onFinishInputView(finishingInput) }
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onWindowShown()")
|
||||
|
||||
prefs.sync()
|
||||
updateThemeIfNecessary()
|
||||
updateTheme()
|
||||
updateOneHandedPanelVisibility()
|
||||
activeSubtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
|
||||
onSubtypeChanged(activeSubtype)
|
||||
setActiveInput(R.id.text_input)
|
||||
|
||||
super.onWindowShown()
|
||||
textInputManager.onWindowShown()
|
||||
mediaInputManager.onWindowShown()
|
||||
eventListeners.toList().forEach { it.onWindowShown() }
|
||||
}
|
||||
|
||||
override fun onWindowHidden() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onWindowHidden()")
|
||||
|
||||
super.onWindowHidden()
|
||||
textInputManager.onWindowHidden()
|
||||
mediaInputManager.onWindowHidden()
|
||||
eventListeners.toList().forEach { it.onWindowHidden() }
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
@@ -227,14 +243,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.toList().forEach { it.onUpdateCursorAnchorInfo(cursorAnchorInfo) }
|
||||
}
|
||||
|
||||
override fun onUpdateSelection(
|
||||
@@ -253,59 +266,65 @@ class FlorisBoard : InputMethodService() {
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
textInputManager.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
newSelStart,
|
||||
newSelEnd,
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
mediaInputManager.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
newSelStart,
|
||||
newSelEnd,
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
eventListeners.toList().forEach {
|
||||
it.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
newSelStart,
|
||||
newSelEnd,
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the preferences if the selected theme res id has changed and updates the theme only
|
||||
* then by rebuilding the UI and setting the navigation bar theme manually.
|
||||
* Reapplies the supplies colors and settings from prefs to navigation bar.
|
||||
*/
|
||||
private fun updateThemeIfNecessary() {
|
||||
val newThemeResId = prefs.theme.getSelectedThemeResId()
|
||||
if (newThemeResId != currentThemeResId) {
|
||||
currentThemeResId = newThemeResId
|
||||
private fun updateTheme() {
|
||||
val newThemeIsNightMode = prefs.internal.themeCurrentIsNight
|
||||
if (currentThemeIsNight != newThemeIsNightMode) {
|
||||
currentThemeResId = getDayNightBaseThemeId(newThemeIsNightMode)
|
||||
currentThemeIsNight = newThemeIsNightMode
|
||||
setInputView(onCreateInputView())
|
||||
val w = window?.window ?: return
|
||||
w.navigationBarColor = getColorFromAttr(baseContext, android.R.attr.navigationBarColor)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
var flags = w.decorView.systemUiVisibility
|
||||
flags = if (getBooleanFromAttr(baseContext, android.R.attr.windowLightNavigationBar)) {
|
||||
flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
} else {
|
||||
flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
|
||||
}
|
||||
w.decorView.systemUiVisibility = flags
|
||||
}
|
||||
return
|
||||
}
|
||||
val w = window?.window ?: return
|
||||
inputView?.setBackgroundColor(prefs.theme.keyboardBgColor)
|
||||
w.navigationBarColor = prefs.theme.navBarColor
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
var flags = w.decorView.systemUiVisibility
|
||||
flags = if (prefs.theme.navBarIsLight) {
|
||||
flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
} else {
|
||||
flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
|
||||
}
|
||||
w.decorView.systemUiVisibility = flags
|
||||
}
|
||||
inputView?.oneHandedCtrlPanelStart?.setBackgroundColor(prefs.theme.oneHandedBgColor)
|
||||
inputView?.oneHandedCtrlPanelEnd?.setBackgroundColor(prefs.theme.oneHandedBgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_move_start)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_move_end)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_close_start)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_close_end)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
eventListeners.toList().forEach { it.onApplyThemeAttributes() }
|
||||
}
|
||||
|
||||
override fun onComputeInsets(outInsets: Insets?) {
|
||||
super.onComputeInsets(outInsets)
|
||||
val inputView = this.inputView ?: return
|
||||
val inputWindowView = this.inputWindowView ?: return
|
||||
// TODO: Check also if the keyboard is currently suppressed by a hardware keyboard
|
||||
if (!isInputViewShown) {
|
||||
outInsets?.contentTopInsets = inputView.height
|
||||
outInsets?.visibleTopInsets = inputView.height
|
||||
outInsets?.contentTopInsets = inputWindowView.height
|
||||
outInsets?.visibleTopInsets = inputWindowView.height
|
||||
return
|
||||
}
|
||||
val innerInputViewContainer =
|
||||
inputView.findViewById<LinearLayout>(R.id.inner_input_view_container) ?: return
|
||||
val visibleTopY = inputView.height - innerInputViewContainer.measuredHeight
|
||||
val visibleTopY = inputWindowView.height - inputView.measuredHeight
|
||||
outInsets?.contentTopInsets = visibleTopY
|
||||
outInsets?.visibleTopInsets = visibleTopY
|
||||
}
|
||||
@@ -321,8 +340,8 @@ class FlorisBoard : InputMethodService() {
|
||||
private fun updateSoftInputWindowLayoutParameters() {
|
||||
val w = window?.window ?: return
|
||||
ViewLayoutUtils.updateLayoutHeightOf(w, WindowManager.LayoutParams.MATCH_PARENT)
|
||||
val inputView = this.inputView
|
||||
if (inputView != null) {
|
||||
val inputWindowView = this.inputWindowView
|
||||
if (inputWindowView != null) {
|
||||
val layoutHeight = if (isFullscreenMode) {
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
} else {
|
||||
@@ -331,7 +350,7 @@ class FlorisBoard : InputMethodService() {
|
||||
val inputArea = w.findViewById<View>(android.R.id.inputArea)
|
||||
ViewLayoutUtils.updateLayoutHeightOf(inputArea, layoutHeight)
|
||||
ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM)
|
||||
ViewLayoutUtils.updateLayoutHeightOf(inputView, layoutHeight)
|
||||
ViewLayoutUtils.updateLayoutHeightOf(inputWindowView, layoutHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,9 +358,9 @@ class FlorisBoard : InputMethodService() {
|
||||
* Makes a key press vibration if the user has this feature enabled in the preferences.
|
||||
*/
|
||||
fun keyPressVibrate() {
|
||||
if (prefs.looknfeel.vibrationEnabled) {
|
||||
var vibrationStrength = prefs.looknfeel.vibrationStrength
|
||||
if (vibrationStrength == 0 && prefs.looknfeel.vibrationEnabledSystem) {
|
||||
if (prefs.keyboard.vibrationEnabled) {
|
||||
var vibrationStrength = prefs.keyboard.vibrationStrength
|
||||
if (vibrationStrength == -1 && prefs.keyboard.vibrationEnabledSystem) {
|
||||
vibrationStrength = 36
|
||||
}
|
||||
if (vibrationStrength > 0) {
|
||||
@@ -363,15 +382,15 @@ class FlorisBoard : InputMethodService() {
|
||||
* Makes a key press sound if the user has this feature enabled in the preferences.
|
||||
*/
|
||||
fun keyPressSound(keyData: KeyData? = null) {
|
||||
if (prefs.looknfeel.soundEnabled) {
|
||||
val soundVolume = prefs.looknfeel.soundVolume
|
||||
if (prefs.keyboard.soundEnabled) {
|
||||
val soundVolume = prefs.keyboard.soundVolume
|
||||
val effect = when (keyData?.code) {
|
||||
KeyCode.SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR
|
||||
KeyCode.DELETE -> AudioManager.FX_KEYPRESS_DELETE
|
||||
KeyCode.ENTER -> AudioManager.FX_KEYPRESS_RETURN
|
||||
else -> AudioManager.FX_KEYPRESS_STANDARD
|
||||
}
|
||||
if (soundVolume == 0 && prefs.looknfeel.soundEnabledSystem) {
|
||||
if (soundVolume == -1 && prefs.keyboard.soundEnabledSystem) {
|
||||
audioManager!!.playSoundEffect(effect)
|
||||
} else if (soundVolume > 0) {
|
||||
audioManager!!.playSoundEffect(effect, soundVolume / 100f)
|
||||
@@ -435,26 +454,26 @@ class FlorisBoard : InputMethodService() {
|
||||
private fun onOneHandedPanelButtonClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.one_handed_ctrl_move_start -> {
|
||||
prefs.looknfeel.oneHandedMode = "start"
|
||||
prefs.keyboard.oneHandedMode = "start"
|
||||
}
|
||||
R.id.one_handed_ctrl_move_end -> {
|
||||
prefs.looknfeel.oneHandedMode = "end"
|
||||
prefs.keyboard.oneHandedMode = "end"
|
||||
}
|
||||
R.id.one_handed_ctrl_close_start,
|
||||
R.id.one_handed_ctrl_close_end -> {
|
||||
prefs.looknfeel.oneHandedMode = "off"
|
||||
prefs.keyboard.oneHandedMode = "off"
|
||||
}
|
||||
}
|
||||
updateOneHandedPanelVisibility()
|
||||
}
|
||||
|
||||
fun toggleOneHandedMode() {
|
||||
when (prefs.looknfeel.oneHandedMode) {
|
||||
when (prefs.keyboard.oneHandedMode) {
|
||||
"off" -> {
|
||||
prefs.looknfeel.oneHandedMode = "end"
|
||||
prefs.keyboard.oneHandedMode = "end"
|
||||
}
|
||||
else -> {
|
||||
prefs.looknfeel.oneHandedMode = "off"
|
||||
prefs.keyboard.oneHandedMode = "off"
|
||||
}
|
||||
}
|
||||
updateOneHandedPanelVisibility()
|
||||
@@ -465,7 +484,7 @@ class FlorisBoard : InputMethodService() {
|
||||
inputView?.oneHandedCtrlPanelStart?.visibility = View.GONE
|
||||
inputView?.oneHandedCtrlPanelEnd?.visibility = View.GONE
|
||||
} else {
|
||||
when (prefs.looknfeel.oneHandedMode) {
|
||||
when (prefs.keyboard.oneHandedMode) {
|
||||
"off" -> {
|
||||
inputView?.oneHandedCtrlPanelStart?.visibility = View.GONE
|
||||
inputView?.oneHandedCtrlPanelEnd?.visibility = View.GONE
|
||||
@@ -486,6 +505,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 +538,6 @@ class FlorisBoard : InputMethodService() {
|
||||
fun onWindowShown() {}
|
||||
fun onWindowHidden() {}
|
||||
|
||||
fun onConfigurationChanged(newConfig: Configuration) {}
|
||||
|
||||
fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {}
|
||||
fun onUpdateSelection(
|
||||
oldSelStart: Int,
|
||||
@@ -510,6 +548,7 @@ class FlorisBoard : InputMethodService() {
|
||||
candidatesEnd: Int
|
||||
) {}
|
||||
|
||||
fun onApplyThemeAttributes() {}
|
||||
fun onSubtypeChanged(newSubtype: Subtype) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ViewFlipper
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
/**
|
||||
* Custom ViewFlipper class used to prevent an unnecessary exception to be thrown when it is
|
||||
* detached from a window.
|
||||
*
|
||||
* Based on the solution of this SO answer: https://stackoverflow.com/a/8208874/6801193
|
||||
*/
|
||||
class FlorisViewFlipper : ViewFlipper {
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
try {
|
||||
super.onDetachedFromWindow()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
stopFlipping()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,20 +17,30 @@
|
||||
package dev.patrickgold.florisboard.ime.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ViewFlipper
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Root view of the keyboard. Notifies [FlorisBoard] when it has been attached to a window.
|
||||
*/
|
||||
class InputView : FrameLayout {
|
||||
|
||||
class InputView : LinearLayout {
|
||||
private var florisboard: FlorisBoard = FlorisBoard.getInstance()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
var desiredInputViewHeight: Int = resources.getDimension(R.dimen.inputView_baseHeight).roundToInt()
|
||||
private set
|
||||
var desiredSmartbarHeight: Int = resources.getDimension(R.dimen.smartbar_baseHeight).roundToInt()
|
||||
private set
|
||||
var desiredTextKeyboardViewHeight: Int = resources.getDimension(R.dimen.textKeyboardView_baseHeight).roundToInt()
|
||||
private set
|
||||
var desiredMediaKeyboardViewHeight: Int = resources.getDimension(R.dimen.mediaKeyboardView_baseHeight).roundToInt()
|
||||
private set
|
||||
|
||||
var mainViewFlipper: ViewFlipper? = null
|
||||
private set
|
||||
@@ -54,4 +64,31 @@ class InputView : FrameLayout {
|
||||
|
||||
florisboard.registerInputView(this)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val heightFactor = when (resources.configuration.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> 0.85f
|
||||
else -> if (prefs.keyboard.oneHandedMode != "off") {
|
||||
0.9f
|
||||
} else {
|
||||
1.0f
|
||||
}
|
||||
} * when (prefs.keyboard.heightFactor) {
|
||||
"extra_short" -> 0.85f
|
||||
"short" -> 0.90f
|
||||
"mid_short" -> 0.95f
|
||||
"normal" -> 1.00f
|
||||
"mid_tall" -> 1.05f
|
||||
"tall" -> 1.10f
|
||||
"extra_tall" -> 1.15f
|
||||
else -> 1.00f
|
||||
}
|
||||
val height = (resources.getDimension(R.dimen.inputView_baseHeight) * heightFactor).roundToInt()
|
||||
desiredInputViewHeight = height
|
||||
desiredSmartbarHeight = (0.16129 * height).roundToInt()
|
||||
desiredTextKeyboardViewHeight = height - desiredSmartbarHeight
|
||||
desiredMediaKeyboardViewHeight = height
|
||||
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ViewFlipper
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
/**
|
||||
* Root view of the keyboard.
|
||||
*/
|
||||
class InputWindowView : FrameLayout {
|
||||
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)
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class PrefHelper(
|
||||
val correction = Correction(this)
|
||||
val internal = Internal(this)
|
||||
val keyboard = Keyboard(this)
|
||||
val looknfeel = Looknfeel(this)
|
||||
val localization = Localization(this)
|
||||
val suggestion = Suggestion(this)
|
||||
val theme = Theme(this)
|
||||
|
||||
@@ -110,15 +110,28 @@ class PrefHelper(
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var defaultInstance: PrefHelper? = null
|
||||
|
||||
@Synchronized
|
||||
fun getDefaultInstance(context: Context): PrefHelper {
|
||||
if (defaultInstance == null) {
|
||||
defaultInstance = PrefHelper(context)
|
||||
}
|
||||
return defaultInstance!!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the [PreferenceManager] to set the defined preferences to their default values, if
|
||||
* they have not been initialized yet.
|
||||
*/
|
||||
fun initDefaultPreferences() {
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_advanced, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_gestures, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_keyboard, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_looknfeel, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_theme, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_typing, true)
|
||||
//setPref(Keyboard.SUBTYPES, "")
|
||||
//setPref(Internal.IS_IME_SET_UP, false)
|
||||
}
|
||||
@@ -128,10 +141,10 @@ class PrefHelper(
|
||||
*/
|
||||
fun sync() {
|
||||
val contentResolver = context.contentResolver
|
||||
looknfeel.soundEnabledSystem = Settings.System.getInt(
|
||||
keyboard.soundEnabledSystem = Settings.System.getInt(
|
||||
contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 0
|
||||
) != 0
|
||||
looknfeel.vibrationEnabledSystem = Settings.System.getInt(
|
||||
keyboard.vibrationEnabledSystem = Settings.System.getInt(
|
||||
contentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, 0
|
||||
) != 0
|
||||
|
||||
@@ -171,28 +184,41 @@ class PrefHelper(
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for internal preferences.
|
||||
* Wrapper class for internal preferences. A preference qualifies as an internal pref if the
|
||||
* user has no ability to control this preference's value directly (via a UI pref view).
|
||||
*/
|
||||
class Internal(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val IS_IME_SET_UP = "internal__is_ime_set_up"
|
||||
const val VERSION_ON_INSTALL = "internal__version_on_install"
|
||||
const val VERSION_LAST_USE = "internal__version_last_use"
|
||||
const val VERSION_LAST_CHANGELOG = "internal__version_last_changelog"
|
||||
const val IS_IME_SET_UP = "internal__is_ime_set_up"
|
||||
const val THEME_CURRENT_BASED_ON = "internal__theme_current_based_on"
|
||||
const val THEME_CURRENT_IS_MODIFIED = "internal__theme_current_is_modified"
|
||||
const val THEME_CURRENT_IS_NIGHT = "internal__theme_current_is_night"
|
||||
const val VERSION_ON_INSTALL = "internal__version_on_install"
|
||||
const val VERSION_LAST_USE = "internal__version_last_use"
|
||||
const val VERSION_LAST_CHANGELOG = "internal__version_last_changelog"
|
||||
}
|
||||
|
||||
var isImeSetUp: Boolean
|
||||
get() = prefHelper.getPref(IS_IME_SET_UP, false)
|
||||
set(value) = prefHelper.setPref(IS_IME_SET_UP, value)
|
||||
get() = prefHelper.getPref(IS_IME_SET_UP, false)
|
||||
set(v) = prefHelper.setPref(IS_IME_SET_UP, v)
|
||||
var themeCurrentBasedOn: String
|
||||
get() = prefHelper.getPref(THEME_CURRENT_BASED_ON, "undefined")
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_BASED_ON, v)
|
||||
var themeCurrentIsModified: Boolean
|
||||
get() = prefHelper.getPref(THEME_CURRENT_IS_MODIFIED, false)
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_IS_MODIFIED, v)
|
||||
var themeCurrentIsNight: Boolean
|
||||
get() = prefHelper.getPref(THEME_CURRENT_IS_NIGHT, false)
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_IS_NIGHT, v)
|
||||
var versionOnInstall: String
|
||||
get() = prefHelper.getPref(VERSION_ON_INSTALL, VersionName.DEFAULT_RAW)
|
||||
set(value) = prefHelper.setPref(VERSION_ON_INSTALL, value)
|
||||
get() = prefHelper.getPref(VERSION_ON_INSTALL, VersionName.DEFAULT_RAW)
|
||||
set(v) = prefHelper.setPref(VERSION_ON_INSTALL, v)
|
||||
var versionLastUse: String
|
||||
get() = prefHelper.getPref(VERSION_LAST_USE, VersionName.DEFAULT_RAW)
|
||||
set(value) = prefHelper.setPref(VERSION_LAST_USE, value)
|
||||
get() = prefHelper.getPref(VERSION_LAST_USE, VersionName.DEFAULT_RAW)
|
||||
set(v) = prefHelper.setPref(VERSION_LAST_USE, v)
|
||||
var versionLastChangelog: String
|
||||
get() = prefHelper.getPref(VERSION_LAST_CHANGELOG, VersionName.DEFAULT_RAW)
|
||||
set(value) = prefHelper.setPref(VERSION_LAST_CHANGELOG, value)
|
||||
get() = prefHelper.getPref(VERSION_LAST_CHANGELOG, VersionName.DEFAULT_RAW)
|
||||
set(v) = prefHelper.setPref(VERSION_LAST_CHANGELOG, v)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,30 +226,14 @@ class PrefHelper(
|
||||
*/
|
||||
class Keyboard(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ACTIVE_SUBTYPE_ID = "keyboard__active_subtype_id"
|
||||
const val SUBTYPES = "keyboard__subtypes"
|
||||
}
|
||||
|
||||
var activeSubtypeId: Int
|
||||
get() = prefHelper.getPref(ACTIVE_SUBTYPE_ID, -1)
|
||||
set(v) = prefHelper.setPref(ACTIVE_SUBTYPE_ID, v)
|
||||
var subtypes: String
|
||||
get() = prefHelper.getPref(SUBTYPES, "")
|
||||
set(v) = prefHelper.setPref(SUBTYPES, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for looknfeel preferences.
|
||||
*/
|
||||
class Looknfeel(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val HEIGHT_FACTOR = "looknfeel__height_factor"
|
||||
const val LONG_PRESS_DELAY = "looknfeel__long_press_delay"
|
||||
const val ONE_HANDED_MODE = "looknfeel__one_handed_mode"
|
||||
const val SOUND_ENABLED = "looknfeel__sound_enabled"
|
||||
const val SOUND_VOLUME = "looknfeel__sound_volume"
|
||||
const val VIBRATION_ENABLED = "looknfeel__vibration_enabled"
|
||||
const val VIBRATION_STRENGTH = "looknfeel__vibration_strength"
|
||||
const val HEIGHT_FACTOR = "keyboard__height_factor"
|
||||
const val LONG_PRESS_DELAY = "keyboard__long_press_delay"
|
||||
const val ONE_HANDED_MODE = "keyboard__one_handed_mode"
|
||||
const val POPUP_ENABLED = "keyboard__popup_enabled"
|
||||
const val SOUND_ENABLED = "keyboard__sound_enabled"
|
||||
const val SOUND_VOLUME = "keyboard__sound_volume"
|
||||
const val VIBRATION_ENABLED = "keyboard__vibration_enabled"
|
||||
const val VIBRATION_STRENGTH = "keyboard__vibration_strength"
|
||||
}
|
||||
|
||||
var heightFactor: String = ""
|
||||
@@ -235,34 +245,58 @@ class PrefHelper(
|
||||
var oneHandedMode: String
|
||||
get() = prefHelper.getPref(ONE_HANDED_MODE, "off")
|
||||
set(value) = prefHelper.setPref(ONE_HANDED_MODE, value)
|
||||
var popupEnabled: Boolean = false
|
||||
get() = prefHelper.getPref(POPUP_ENABLED, true)
|
||||
private set
|
||||
var soundEnabled: Boolean = false
|
||||
get() = prefHelper.getPref(SOUND_ENABLED, true)
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for localization preferences.
|
||||
*/
|
||||
class Localization(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ACTIVE_SUBTYPE_ID = "localization__active_subtype_id"
|
||||
const val SUBTYPES = "localization__subtypes"
|
||||
}
|
||||
|
||||
var activeSubtypeId: Int
|
||||
get() = prefHelper.getPref(ACTIVE_SUBTYPE_ID, Subtype.DEFAULT.id)
|
||||
set(v) = prefHelper.setPref(ACTIVE_SUBTYPE_ID, v)
|
||||
var subtypes: String
|
||||
get() = prefHelper.getPref(SUBTYPES, "")
|
||||
set(v) = prefHelper.setPref(SUBTYPES, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for suggestion preferences.
|
||||
*/
|
||||
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
|
||||
@@ -273,18 +307,119 @@ class PrefHelper(
|
||||
*/
|
||||
class Theme(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val NAME = "theme__name"
|
||||
const val COLOR_PRIMARY = "theme__colorPrimary"
|
||||
const val COLOR_PRIMARY_DARK = "theme__colorPrimaryDark"
|
||||
const val COLOR_ACCENT = "theme__colorAccent"
|
||||
const val NAV_BAR_COLOR = "theme__navBarColor"
|
||||
const val NAV_BAR_IS_LIGHT = "theme__navBarIsLight"
|
||||
const val KEYBOARD_BG_COLOR = "theme__keyboard_bgColor"
|
||||
const val KEY_BG_COLOR = "theme__key_bgColor"
|
||||
const val KEY_BG_COLOR_PRESSED = "theme__key_bgColorPressed"
|
||||
const val KEY_FG_COLOR = "theme__key_fgColor"
|
||||
const val KEY_ENTER_BG_COLOR = "theme__keyEnter_bgColor"
|
||||
const val KEY_ENTER_BG_COLOR_PRESSED = "theme__keyEnter_bgColorPressed"
|
||||
const val KEY_ENTER_FG_COLOR = "theme__keyEnter_fgColor"
|
||||
const val KEY_SHIFT_BG_COLOR = "theme__keyShift_bgColor"
|
||||
const val KEY_SHIFT_BG_COLOR_PRESSED = "theme__keyShift_bgColorPressed"
|
||||
const val KEY_SHIFT_FG_COLOR = "theme__keyShift_fgColor"
|
||||
const val KEY_SHIFT_FG_COLOR_CAPSLOCK = "theme__keyShift_fgColorCapsLock"
|
||||
const val KEY_POPUP_BG_COLOR = "theme__keyPopup_bgColor"
|
||||
const val KEY_POPUP_BG_COLOR_ACTIVE = "theme__keyPopup_bgColorActive"
|
||||
const val KEY_POPUP_FG_COLOR = "theme__keyPopup_fgColor"
|
||||
const val MEDIA_FG_COLOR = "theme__media_fgColor"
|
||||
const val MEDIA_FG_COLOR_ALT = "theme__media_fgColorAlt"
|
||||
const val ONE_HANDED_BG_COLOR = "theme__oneHanded_bgColor"
|
||||
const val ONE_HANDED_BUTTON_FG_COLOR = "theme__oneHandedButton_fgColor"
|
||||
const val SMARTBAR_BG_COLOR = "theme__smartbar_bgColor"
|
||||
const val SMARTBAR_FG_COLOR = "theme__smartbar_fgColor"
|
||||
const val SMARTBAR_FG_COLOR_ALT = "theme__smartbar_fgColorAlt"
|
||||
const val SMARTBAR_BUTTON_BG_COLOR = "theme__smartbarButton_bgColor"
|
||||
const val SMARTBAR_BUTTON_FG_COLOR = "theme__smartbarButton_fgColor"
|
||||
}
|
||||
|
||||
var name: String = ""
|
||||
get() = prefHelper.getPref(NAME, "floris_light")
|
||||
private set
|
||||
fun getSelectedThemeResId(): Int {
|
||||
return when (name) {
|
||||
"floris_light" -> R.style.KeyboardTheme_FlorisLight
|
||||
"floris_dark" -> R.style.KeyboardTheme_FlorisDark
|
||||
else -> R.style.KeyboardTheme_FlorisLight
|
||||
}
|
||||
}
|
||||
var colorPrimary: Int
|
||||
get() = prefHelper.getPref(COLOR_PRIMARY, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_PRIMARY, v)
|
||||
var colorPrimaryDark: Int
|
||||
get() = prefHelper.getPref(COLOR_PRIMARY_DARK, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_PRIMARY_DARK, v)
|
||||
var colorAccent: Int
|
||||
get() = prefHelper.getPref(COLOR_ACCENT, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_ACCENT, v)
|
||||
var navBarColor: Int
|
||||
get() = prefHelper.getPref(NAV_BAR_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(NAV_BAR_COLOR, v)
|
||||
var navBarIsLight: Boolean
|
||||
get() = prefHelper.getPref(NAV_BAR_IS_LIGHT, false)
|
||||
set(v) = prefHelper.setPref(NAV_BAR_IS_LIGHT, v)
|
||||
var keyboardBgColor: Int
|
||||
get() = prefHelper.getPref(KEYBOARD_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEYBOARD_BG_COLOR, v)
|
||||
var keyBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_BG_COLOR, v)
|
||||
var keyBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_BG_COLOR_PRESSED, v)
|
||||
var keyFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_FG_COLOR, v)
|
||||
var keyEnterBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_BG_COLOR, v)
|
||||
var keyEnterBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_BG_COLOR_PRESSED, v)
|
||||
var keyEnterFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_FG_COLOR, v)
|
||||
var keyShiftBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_BG_COLOR, v)
|
||||
var keyShiftBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_BG_COLOR_PRESSED, v)
|
||||
var keyShiftFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_FG_COLOR, v)
|
||||
var keyShiftFgColorCapsLock: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_FG_COLOR_CAPSLOCK, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_FG_COLOR_CAPSLOCK, v)
|
||||
var keyPopupBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_BG_COLOR, v)
|
||||
var keyPopupBgColorActive: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_BG_COLOR_ACTIVE, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_BG_COLOR_ACTIVE, v)
|
||||
var keyPopupFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_FG_COLOR, v)
|
||||
var mediaFgColor: Int
|
||||
get() = prefHelper.getPref(MEDIA_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(MEDIA_FG_COLOR, v)
|
||||
var mediaFgColorAlt: Int
|
||||
get() = prefHelper.getPref(MEDIA_FG_COLOR_ALT, 0)
|
||||
set(v) = prefHelper.setPref(MEDIA_FG_COLOR_ALT, v)
|
||||
var oneHandedBgColor: Int
|
||||
get() = prefHelper.getPref(ONE_HANDED_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(ONE_HANDED_BG_COLOR, v)
|
||||
var oneHandedButtonFgColor: Int
|
||||
get() = prefHelper.getPref(ONE_HANDED_BUTTON_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(ONE_HANDED_BUTTON_FG_COLOR, v)
|
||||
var smartbarBgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BG_COLOR, v)
|
||||
var smartbarFgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_FG_COLOR, v)
|
||||
var smartbarFgColorAlt: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_FG_COLOR_ALT, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_FG_COLOR_ALT, v)
|
||||
var smartbarButtonBgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BUTTON_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BUTTON_BG_COLOR, v)
|
||||
var smartbarButtonFgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BUTTON_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BUTTON_FG_COLOR, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class SubtypeManager(
|
||||
var imeConfig: FlorisBoard.ImeConfig = FlorisBoard.ImeConfig(context.packageName)
|
||||
var subtypes: List<Subtype>
|
||||
get() {
|
||||
val listRaw = prefs.keyboard.subtypes
|
||||
val listRaw = prefs.localization.subtypes
|
||||
return if (listRaw.isBlank()) {
|
||||
listOf()
|
||||
} else {
|
||||
@@ -58,7 +58,7 @@ class SubtypeManager(
|
||||
}
|
||||
}
|
||||
set(v) {
|
||||
prefs.keyboard.subtypes = v.joinToString(SUBTYPE_LIST_STR_DELIMITER)
|
||||
prefs.localization.subtypes = v.joinToString(SUBTYPE_LIST_STR_DELIMITER)
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -134,16 +134,16 @@ class SubtypeManager(
|
||||
*/
|
||||
fun getActiveSubtype(): Subtype? {
|
||||
for (subtype in subtypes) {
|
||||
if (subtype.id == prefs.keyboard.activeSubtypeId) {
|
||||
if (subtype.id == prefs.localization.activeSubtypeId) {
|
||||
return subtype
|
||||
}
|
||||
}
|
||||
val subtypeList = subtypes
|
||||
return if (subtypeList.isNotEmpty()) {
|
||||
prefs.keyboard.activeSubtypeId = subtypeList[0].id
|
||||
prefs.localization.activeSubtypeId = subtypeList[0].id
|
||||
subtypeList[0]
|
||||
} else {
|
||||
prefs.keyboard.activeSubtypeId = -1
|
||||
prefs.localization.activeSubtypeId = Subtype.DEFAULT.id
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ class SubtypeManager(
|
||||
}
|
||||
}
|
||||
subtypes = subtypeList
|
||||
if (subtypeToRemove.id == prefs.keyboard.activeSubtypeId) {
|
||||
if (subtypeToRemove.id == prefs.localization.activeSubtypeId) {
|
||||
getActiveSubtype()
|
||||
}
|
||||
}
|
||||
@@ -238,7 +238,7 @@ class SubtypeManager(
|
||||
if (triggerNextSubtype) {
|
||||
newActiveSubtype = subtypeList[0]
|
||||
}
|
||||
prefs.keyboard.activeSubtypeId = when (newActiveSubtype) {
|
||||
prefs.localization.activeSubtypeId = when (newActiveSubtype) {
|
||||
null -> -1
|
||||
else -> newActiveSubtype.id
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.media
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
|
||||
class MediaInputView : LinearLayout, FlorisBoard.EventListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
var tabLayout: TabLayout? = null
|
||||
private set
|
||||
var switchToTextInputButton: Button? = null
|
||||
private set
|
||||
var backspaceButton: ImageButton? = null
|
||||
private set
|
||||
|
||||
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()
|
||||
tabLayout = findViewById(R.id.media_input_tabs)
|
||||
switchToTextInputButton = findViewById(R.id.media_input_switch_to_text_input_button)
|
||||
backspaceButton = findViewById(R.id.media_input_backspace_button)
|
||||
onApplyThemeAttributes()
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
tabLayout?.setTabTextColors(prefs.theme.mediaFgColor, prefs.theme.mediaFgColor)
|
||||
tabLayout?.tabIconTint = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
tabLayout?.setSelectedTabIndicatorColor(prefs.theme.colorPrimary)
|
||||
switchToTextInputButton?.setTextColor(prefs.theme.mediaFgColor)
|
||||
backspaceButton?.imageTintList = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val height = florisboard?.inputView?.desiredInputViewHeight ?: 0
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
}
|
||||
@@ -18,17 +18,20 @@ package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Handler
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.widget.HorizontalScrollView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.util.getColorFromAttr
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
|
||||
/**
|
||||
* View class for managing the rendering and the events of a single emoji keyboard key.
|
||||
@@ -40,10 +43,12 @@ import dev.patrickgold.florisboard.util.getColorFromAttr
|
||||
*/
|
||||
@SuppressLint("ViewConstructor")
|
||||
class EmojiKeyView(
|
||||
private val florisboard: FlorisBoard,
|
||||
private val emojiKeyboardView: EmojiKeyboardView,
|
||||
val data: EmojiKeyData
|
||||
) : androidx.appcompat.widget.AppCompatTextView(florisboard.context) {
|
||||
) : androidx.appcompat.widget.AppCompatTextView(emojiKeyboardView.context),
|
||||
FlorisBoard.EventListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
private var isCancelled: Boolean = false
|
||||
private var osHandler: Handler? = null
|
||||
@@ -55,14 +60,16 @@ class EmojiKeyView(
|
||||
setPadding(0, 0, 0, 0)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.emoji_key_textSize))
|
||||
|
||||
triangleDrawable = resources.getDrawable(
|
||||
R.drawable.triangle_bottom_right, context.theme
|
||||
)
|
||||
triangleDrawable?.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
getColorFromAttr(context, R.attr.emoji_key_fgColor), BlendModeCompat.SRC_ATOP
|
||||
)
|
||||
triangleDrawable = ContextCompat.getDrawable(context, R.drawable.triangle_bottom_right)
|
||||
|
||||
text = data.getCodePointsAsString()
|
||||
|
||||
florisboard?.addEventListener(this)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
onApplyThemeAttributes()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +86,7 @@ class EmojiKeyView(
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
isCancelled = false
|
||||
val delayMillis = florisboard.prefs.looknfeel.longPressDelay
|
||||
val delayMillis = prefs.keyboard.longPressDelay
|
||||
if (osHandler == null) {
|
||||
osHandler = Handler()
|
||||
}
|
||||
@@ -89,8 +96,8 @@ class EmojiKeyView(
|
||||
emojiKeyboardView.isScrollBlocked = true
|
||||
emojiKeyboardView.popupManager.show(this)
|
||||
emojiKeyboardView.popupManager.extend(this)
|
||||
florisboard.keyPressVibrate()
|
||||
florisboard.keyPressSound()
|
||||
florisboard?.keyPressVibrate()
|
||||
florisboard?.keyPressSound()
|
||||
}, delayMillis.toLong())
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
@@ -117,10 +124,10 @@ class EmojiKeyView(
|
||||
if (event.actionMasked != MotionEvent.ACTION_CANCEL &&
|
||||
retData != null && !isCancelled) {
|
||||
if (!emojiKeyboardView.isScrollBlocked) {
|
||||
florisboard.keyPressVibrate()
|
||||
florisboard.keyPressSound()
|
||||
florisboard?.keyPressVibrate()
|
||||
florisboard?.keyPressSound()
|
||||
}
|
||||
florisboard.mediaInputManager.sendEmojiKeyPress(retData)
|
||||
florisboard?.mediaInputManager?.sendEmojiKeyPress(retData)
|
||||
performClick()
|
||||
}
|
||||
if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
|
||||
@@ -131,18 +138,29 @@ class EmojiKeyView(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
triangleDrawable?.setBounds(
|
||||
(measuredWidth * 0.75f).toInt(),
|
||||
(measuredHeight * 0.75f).toInt(),
|
||||
(measuredWidth * 0.85f).toInt(),
|
||||
(measuredHeight * 0.85f).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
triangleDrawable?.colorFilter =
|
||||
BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
prefs.theme.mediaFgColorAlt, BlendModeCompat.SRC_ATOP
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
canvas ?: return
|
||||
|
||||
if (data.popup.isNotEmpty()) {
|
||||
triangleDrawable?.setBounds(
|
||||
(measuredWidth * 0.75f).toInt(),
|
||||
(measuredHeight * 0.75f).toInt(),
|
||||
(measuredWidth * 0.85f).toInt(),
|
||||
(measuredHeight * 0.85f).toInt()
|
||||
)
|
||||
triangleDrawable?.draw(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
@@ -31,6 +32,7 @@ import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.popup.KeyPopupManager
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
@@ -42,15 +44,17 @@ import java.util.*
|
||||
*
|
||||
* @property florisboard Reference to instance of core class [FlorisBoard].
|
||||
*/
|
||||
class EmojiKeyboardView : LinearLayout {
|
||||
class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
private var activeCategory: EmojiCategory = EmojiCategory.SMILEYS_EMOTION
|
||||
private var emojiViewFlipper: ViewFlipper
|
||||
private val emojiKeyWidth = resources.getDimension(R.dimen.emoji_key_width).toInt()
|
||||
private val emojiKeyHeight = resources.getDimension(R.dimen.emoji_key_height).toInt()
|
||||
private val florisboard: FlorisBoard = FlorisBoard.getInstance()
|
||||
private var layouts: Deferred<EmojiLayoutDataMap>
|
||||
private val mainScope = MainScope()
|
||||
private val tabLayout: TabLayout
|
||||
private val uiLayouts = EnumMap<EmojiCategory, HorizontalScrollView>(EmojiCategory::class.java)
|
||||
|
||||
var isScrollBlocked: Boolean = false
|
||||
@@ -71,9 +75,9 @@ class EmojiKeyboardView : LinearLayout {
|
||||
emojiViewFlipper.measureAllChildren = false
|
||||
addView(emojiViewFlipper)
|
||||
|
||||
val tabs =
|
||||
tabLayout =
|
||||
ViewGroup.inflate(context, R.layout.media_input_emoji_tabs, null) as TabLayout
|
||||
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) {
|
||||
setActiveCategory(when (tab?.position) {
|
||||
0 -> EmojiCategory.SMILEYS_EMOTION
|
||||
@@ -92,7 +96,8 @@ class EmojiKeyboardView : LinearLayout {
|
||||
override fun onTabReselected(tab: TabLayout.Tab?) {}
|
||||
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||
})
|
||||
addView(tabs)
|
||||
addView(tabLayout)
|
||||
florisboard?.addEventListener(this)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
@@ -102,11 +107,7 @@ class EmojiKeyboardView : LinearLayout {
|
||||
buildLayout()
|
||||
setActiveCategory(EmojiCategory.SMILEYS_EMOTION)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
mainScope.cancel()
|
||||
onApplyThemeAttributes()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +145,7 @@ class EmojiKeyboardView : LinearLayout {
|
||||
flexboxLayout.flexWrap = FlexWrap.WRAP
|
||||
for (emojiKeyData in layouts.await()[category].orEmpty()) {
|
||||
val emojiKeyView =
|
||||
EmojiKeyView(florisboard, this@EmojiKeyboardView, emojiKeyData)
|
||||
EmojiKeyView(this@EmojiKeyboardView, emojiKeyData)
|
||||
emojiKeyView.layoutParams = FlexboxLayout.LayoutParams(
|
||||
emojiKeyWidth, emojiKeyHeight
|
||||
)
|
||||
@@ -192,4 +193,9 @@ class EmojiKeyboardView : LinearLayout {
|
||||
))
|
||||
isScrollBlocked = true
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
tabLayout.tabIconTint = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
tabLayout.setSelectedTabIndicatorColor(prefs.theme.colorAccent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,13 @@ package dev.patrickgold.florisboard.ime.popup
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.ContextCompat.getDrawable
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
@@ -32,7 +34,7 @@ class KeyPopupExtendedSingleView(
|
||||
) : androidx.appcompat.widget.AppCompatTextView(
|
||||
context, null, 0
|
||||
) {
|
||||
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
var iconDrawable: Drawable? = null
|
||||
|
||||
init {
|
||||
@@ -40,15 +42,16 @@ class KeyPopupExtendedSingleView(
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, when {
|
||||
isActive -> prefs.theme.keyPopupBgColorActive
|
||||
else -> Color.TRANSPARENT
|
||||
})
|
||||
setTextColor(prefs.theme.keyPopupFgColor)
|
||||
|
||||
super.onDraw(canvas)
|
||||
|
||||
canvas ?: return
|
||||
|
||||
setBackgroundTintColor(this, when {
|
||||
isActive -> R.attr.key_popup_extended_bgColorActive
|
||||
else -> R.attr.key_popup_extended_bgColor
|
||||
})
|
||||
|
||||
val drawable = iconDrawable
|
||||
val drawablePadding = (0.2f * measuredHeight).toInt()
|
||||
if (drawable != null) {
|
||||
@@ -65,7 +68,7 @@ class KeyPopupExtendedSingleView(
|
||||
measuredWidth - marginH - drawablePadding,
|
||||
measuredHeight - marginV - drawablePadding)
|
||||
drawable.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
getColorFromAttr(context, R.attr.key_popup_fgColor),
|
||||
prefs.theme.keyPopupFgColor,
|
||||
BlendModeCompat.SRC_ATOP
|
||||
)
|
||||
drawable.draw(canvas)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.popup
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
|
||||
class KeyPopupExtendedView : FlexboxLayout {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
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)
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, prefs.theme.keyPopupBgColor)
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyView
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
import dev.patrickgold.florisboard.util.setTextTintColor
|
||||
|
||||
/**
|
||||
* Manages the creation and dismissal of key popups as well as the checks if the pointer moved
|
||||
@@ -107,10 +106,6 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
lp.isWrapBefore = isWrapBefore
|
||||
textView.layoutParams = lp
|
||||
textView.gravity = Gravity.CENTER
|
||||
setTextTintColor(
|
||||
textView,
|
||||
R.attr.key_popup_fgColor
|
||||
)
|
||||
val textSize = keyboardView.resources.getDimension(R.dimen.key_popup_textSize)
|
||||
if (keyView is KeyView) {
|
||||
when (keyView.data.popup[k].code) {
|
||||
@@ -169,20 +164,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 +184,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 +251,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
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.popup
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
|
||||
class KeyPopupView : LinearLayout {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private lateinit var text: TextView
|
||||
private lateinit var threedots: ImageView
|
||||
|
||||
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)
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
text = findViewById(R.id.key_popup_text)
|
||||
threedots = findViewById(R.id.key_popup_threedots)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, prefs.theme.keyPopupBgColor)
|
||||
text.setTextColor(prefs.theme.keyPopupFgColor)
|
||||
setImageTintColor2(threedots, prefs.theme.keyPopupFgColor)
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
||||
@@ -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.text.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).
|
||||
@@ -124,8 +137,6 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
|
||||
private suspend fun addKeyboardView(mode: KeyboardMode) {
|
||||
val keyboardView = KeyboardView(florisboard.context)
|
||||
keyboardView.florisboard = florisboard
|
||||
keyboardView.prefs = florisboard.prefs
|
||||
keyboardView.computedLayout = layoutManager.fetchComputedLayoutAsync(mode, florisboard.activeSubtype).await()
|
||||
keyboardViews[mode] = keyboardView
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -142,6 +153,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)
|
||||
@@ -216,7 +228,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 +260,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 +289,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 +326,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 +431,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 +468,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()
|
||||
}
|
||||
|
||||
@@ -421,37 +480,24 @@ class TextInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
*/
|
||||
private fun handleEnter() {
|
||||
val ic = florisboard.currentInputConnection
|
||||
ic?.beginBatchEdit()
|
||||
resetComposingText()
|
||||
val action = florisboard.currentInputEditorInfo?.imeOptions ?: 0
|
||||
val actionMasked = action and EditorInfo.IME_MASK_ACTION
|
||||
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) {
|
||||
when (actionMasked) {
|
||||
EditorInfo.IME_ACTION_DONE,
|
||||
EditorInfo.IME_ACTION_GO,
|
||||
EditorInfo.IME_ACTION_NEXT,
|
||||
EditorInfo.IME_ACTION_PREVIOUS,
|
||||
EditorInfo.IME_ACTION_SEARCH,
|
||||
EditorInfo.IME_ACTION_SEND -> {
|
||||
ic?.performEditorAction(action)
|
||||
}
|
||||
else -> {
|
||||
ic?.sendKeyEvent(
|
||||
KeyEvent(
|
||||
KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_ENTER
|
||||
)
|
||||
)
|
||||
ic?.performEditorAction(actionMasked)
|
||||
}
|
||||
else -> sendSystemKeyEvent(ic, KeyEvent.KEYCODE_ENTER)
|
||||
}
|
||||
}
|
||||
ic?.endBatchEdit()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,6 +545,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 +744,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()
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.text.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.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* View class for managing and rendering an editing key.
|
||||
*/
|
||||
class EditingKeyView : AppCompatImageButton {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
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(when {
|
||||
isEnabled -> prefs.theme.smartbarFgColor
|
||||
else -> prefs.theme.smartbarFgColorAlt
|
||||
})
|
||||
|
||||
// Draw label
|
||||
val label = label
|
||||
if (label != null) {
|
||||
labelPaint.color = if (isHighlighted && isEnabled) {
|
||||
prefs.theme.colorPrimary
|
||||
} else if (!isEnabled) {
|
||||
prefs.theme.smartbarFgColorAlt
|
||||
} else {
|
||||
prefs.theme.smartbarFgColor
|
||||
}
|
||||
val isPortrait =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
if (!isPortrait) {
|
||||
labelPaint.textSize *= 0.9f
|
||||
}
|
||||
val centerX = measuredWidth / 2.0f
|
||||
val centerY = measuredHeight / 2.0f + (labelPaint.textSize - labelPaint.descent()) / 2
|
||||
if (label.contains("\n")) {
|
||||
// Even if more lines may be existing only the first 2 are shown
|
||||
val labelLines = label.split("\n")
|
||||
canvas.drawText(labelLines[0], centerX, centerY * 0.70f, labelPaint)
|
||||
canvas.drawText(labelLines[1], centerX, centerY * 1.30f, labelPaint)
|
||||
} else {
|
||||
canvas.drawText(label, centerX, centerY, labelPaint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.text.editing
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
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
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor2
|
||||
|
||||
/**
|
||||
* 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 val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
|
||||
val height = when (heightMode) {
|
||||
MeasureSpec.EXACTLY -> {
|
||||
// Must be this size
|
||||
heightSize
|
||||
}
|
||||
MeasureSpec.AT_MOST -> {
|
||||
// Can't be bigger than...
|
||||
(florisboard?.inputView?.desiredTextKeyboardViewHeight ?: 0).coerceAtMost(heightSize)
|
||||
}
|
||||
else -> {
|
||||
// Be whatever you want
|
||||
florisboard?.inputView?.desiredTextKeyboardViewHeight ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
setBackgroundTintColor2(this, prefs.theme.smartbarBgColor)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ import androidx.core.view.children
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
import dev.patrickgold.florisboard.util.getColorFromAttr
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor2
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -60,6 +60,7 @@ class KeyView(
|
||||
}
|
||||
private var osHandler: Handler? = null
|
||||
private var osTimer: Timer? = null
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private var shouldBlockNextKeyCode: Boolean = false
|
||||
|
||||
private var drawable: Drawable? = null
|
||||
@@ -174,7 +175,11 @@ class KeyView(
|
||||
event ?: return false
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
keyboardView.popupManager.show(this)
|
||||
florisboard?.prefs?.keyboard?.let {
|
||||
if (it.popupEnabled){
|
||||
keyboardView.popupManager.show(this)
|
||||
}
|
||||
}
|
||||
isKeyPressed = true
|
||||
florisboard?.keyPressVibrate()
|
||||
florisboard?.keyPressSound(data)
|
||||
@@ -190,7 +195,7 @@ class KeyView(
|
||||
}
|
||||
}, 500, 50)
|
||||
}
|
||||
val delayMillis = keyboardView.prefs.looknfeel.longPressDelay
|
||||
val delayMillis = prefs.keyboard.longPressDelay
|
||||
if (osHandler == null) {
|
||||
osHandler = Handler()
|
||||
}
|
||||
@@ -333,20 +338,31 @@ class KeyView(
|
||||
* Updates the background depending on [isKeyPressed] and [data].
|
||||
*/
|
||||
private fun updateKeyPressedBackground() {
|
||||
if (data.code == KeyCode.ENTER) {
|
||||
setBackgroundTintColor(
|
||||
this, when {
|
||||
isKeyPressed -> R.attr.colorPrimaryDark
|
||||
else -> R.attr.colorPrimary
|
||||
}
|
||||
)
|
||||
} else {
|
||||
setBackgroundTintColor(
|
||||
this, when {
|
||||
isKeyPressed -> R.attr.key_bgColorPressed
|
||||
else -> R.attr.key_bgColor
|
||||
}
|
||||
)
|
||||
when (data.code) {
|
||||
KeyCode.ENTER -> {
|
||||
setBackgroundTintColor2(
|
||||
this, when {
|
||||
isKeyPressed -> prefs.theme.keyEnterBgColorPressed
|
||||
else -> prefs.theme.keyEnterBgColor
|
||||
}
|
||||
)
|
||||
}
|
||||
KeyCode.SHIFT -> {
|
||||
setBackgroundTintColor2(
|
||||
this, when {
|
||||
isKeyPressed -> prefs.theme.keyShiftBgColorPressed
|
||||
else -> prefs.theme.keyShiftBgColor
|
||||
}
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
setBackgroundTintColor2(
|
||||
this, when {
|
||||
isKeyPressed -> prefs.theme.keyBgColorPressed
|
||||
else -> prefs.theme.keyBgColor
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,14 +442,14 @@ 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 {
|
||||
when (data.code) {
|
||||
KeyCode.DELETE -> {
|
||||
drawable = getDrawable(context, R.drawable.ic_backspace)
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyFgColor
|
||||
}
|
||||
KeyCode.ENTER -> {
|
||||
val action = florisboard?.currentInputEditorInfo?.imeOptions ?: 0
|
||||
@@ -447,29 +463,29 @@ class KeyView(
|
||||
EditorInfo.IME_ACTION_SEND -> R.drawable.ic_send
|
||||
else -> R.drawable.ic_arrow_right_alt
|
||||
})
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_enter_fgColor)
|
||||
drawableColor = prefs.theme.keyEnterFgColor
|
||||
if (action and EditorInfo.IME_FLAG_NO_ENTER_ACTION > 0) {
|
||||
drawable = getDrawable(context, R.drawable.ic_keyboard_return)
|
||||
}
|
||||
}
|
||||
KeyCode.LANGUAGE_SWITCH -> {
|
||||
drawable = getDrawable(context, R.drawable.ic_language)
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyFgColor
|
||||
}
|
||||
KeyCode.PHONE_PAUSE -> label = resources.getString(R.string.key__phone_pause)
|
||||
KeyCode.PHONE_WAIT -> label = resources.getString(R.string.key__phone_wait)
|
||||
KeyCode.SHIFT -> {
|
||||
drawable = getDrawable(context, when {
|
||||
florisboard?.textInputManager?.caps ?: false && florisboard?.textInputManager?.capsLock ?: false -> {
|
||||
drawableColor = getColorFromAttr(context, R.attr.colorAccent)
|
||||
drawableColor = prefs.theme.keyShiftFgColorCapsLock
|
||||
R.drawable.ic_keyboard_capslock
|
||||
}
|
||||
florisboard?.textInputManager?.caps ?: false && !(florisboard?.textInputManager?.capsLock ?: false) -> {
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyShiftFgColor
|
||||
R.drawable.ic_keyboard_capslock
|
||||
}
|
||||
else -> {
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyShiftFgColor
|
||||
R.drawable.ic_keyboard_arrow_up
|
||||
}
|
||||
})
|
||||
@@ -481,7 +497,7 @@ class KeyView(
|
||||
KeyboardMode.PHONE,
|
||||
KeyboardMode.PHONE2 -> {
|
||||
drawable = getDrawable(context, R.drawable.ic_space_bar)
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyFgColor
|
||||
}
|
||||
KeyboardMode.CHARACTERS -> {
|
||||
label = florisboard?.activeSubtype?.locale?.displayName
|
||||
@@ -491,7 +507,7 @@ class KeyView(
|
||||
}
|
||||
KeyCode.SWITCH_TO_MEDIA_CONTEXT -> {
|
||||
drawable = getDrawable(context, R.drawable.ic_sentiment_satisfied)
|
||||
drawableColor = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
drawableColor = prefs.theme.keyFgColor
|
||||
}
|
||||
KeyCode.SWITCH_TO_TEXT_CONTEXT,
|
||||
KeyCode.VIEW_CHARACTERS -> {
|
||||
@@ -516,6 +532,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,12 +569,12 @@ class KeyView(
|
||||
} else {
|
||||
labelPaint.textSize = resources.getDimension(R.dimen.key_textSize)
|
||||
}
|
||||
labelPaint.color = getColorFromAttr(context, R.attr.key_fgColor)
|
||||
labelPaint.color = prefs.theme.keyFgColor
|
||||
labelPaint.alpha = if (keyboardView.computedLayout?.mode == KeyboardMode.CHARACTERS &&
|
||||
data.code == KeyCode.SPACE) { 120 } else { 255 }
|
||||
val isPortrait =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
if (keyboardView.prefs.looknfeel.oneHandedMode != "off" && isPortrait) {
|
||||
if (prefs.keyboard.oneHandedMode != "off" && isPortrait) {
|
||||
labelPaint.textSize *= 0.9f
|
||||
}
|
||||
val centerX = measuredWidth / 2.0f
|
||||
|
||||
@@ -18,6 +18,7 @@ package dev.patrickgold.florisboard.ime.text.keyboard
|
||||
|
||||
enum class KeyboardMode {
|
||||
CHARACTERS,
|
||||
EDITING,
|
||||
SYMBOLS,
|
||||
SYMBOLS2,
|
||||
NUMERIC,
|
||||
|
||||
@@ -18,9 +18,6 @@ package dev.patrickgold.florisboard.ime.text.keyboard
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
@@ -34,23 +31,24 @@ import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.popup.KeyPopupManager
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyView
|
||||
import dev.patrickgold.florisboard.ime.text.layout.ComputedLayoutData
|
||||
import dev.patrickgold.florisboard.util.getColorFromAttr
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Manages the layout of the keyboard, key measurement, key selection and all touch events.
|
||||
* Supports multi touch events.
|
||||
* Supports multi touch events. Note that the keyboard's background is transparent. The 'real'
|
||||
* background of this keyboard is the background of the underlying mainViewFlipper. This prevents
|
||||
* rendering issues when a keyboard is being loaded for the first time.
|
||||
*
|
||||
* TODO: Implement swipe gesture support
|
||||
*
|
||||
* @property florisboard Reference to instance of core class [FlorisBoard].
|
||||
*/
|
||||
class KeyboardView : LinearLayout {
|
||||
class KeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
private var activeKeyView: KeyView? = null
|
||||
private var activePointerId: Int? = null
|
||||
private var activeX: Float = 0.0f
|
||||
private var activeY: Float = 0.0f
|
||||
|
||||
private var colorDrawable: ColorDrawable
|
||||
var computedLayout: ComputedLayoutData? = null
|
||||
set(v) {
|
||||
field = v
|
||||
@@ -58,21 +56,20 @@ class KeyboardView : LinearLayout {
|
||||
}
|
||||
var desiredKeyWidth: Int = resources.getDimension(R.dimen.key_width).toInt()
|
||||
var desiredKeyHeight: Int = resources.getDimension(R.dimen.key_height).toInt()
|
||||
var florisboard: FlorisBoard? = null
|
||||
var florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
var isPreviewMode: Boolean = false
|
||||
var popupManager = KeyPopupManager<KeyboardView, KeyView>(this)
|
||||
lateinit var prefs: PrefHelper
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
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) {
|
||||
colorDrawable = ColorDrawable(getColorFromAttr(context, R.attr.keyboard_bgColor))
|
||||
background = colorDrawable
|
||||
orientation = VERTICAL
|
||||
layoutParams = layoutParams ?: FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
florisboard?.addEventListener(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,32 +234,21 @@ class KeyboardView : LinearLayout {
|
||||
val keyMarginH = resources.getDimension((R.dimen.key_marginH)).toInt()
|
||||
desiredKeyWidth = (widthSize / 10) - (2 * keyMarginH)
|
||||
|
||||
val factor = prefs.looknfeel.heightFactor
|
||||
val keyHeightFactor = when (resources.configuration.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> 0.85f
|
||||
else -> if (prefs.looknfeel.oneHandedMode == "start" ||
|
||||
prefs.looknfeel.oneHandedMode == "end") {
|
||||
0.9f
|
||||
} else {
|
||||
1.0f
|
||||
}
|
||||
} * when (factor) {
|
||||
"extra_short" -> 0.85f
|
||||
"short" -> 0.90f
|
||||
"mid_short" -> 0.95f
|
||||
"normal" -> 1.00f
|
||||
"mid_tall" -> 1.05f
|
||||
"tall" -> 1.10f
|
||||
"extra_tall" -> 1.15f
|
||||
else -> 1.00f
|
||||
} * when (isPreviewMode) {
|
||||
val keyMarginV = resources.getDimension((R.dimen.key_marginV)).toInt()
|
||||
val keyHeightFactor = when (isPreviewMode) {
|
||||
true -> 0.90f
|
||||
else -> 1.00f
|
||||
}
|
||||
desiredKeyHeight = (resources.getDimension(R.dimen.key_height) * keyHeightFactor).toInt()
|
||||
florisboard?.textInputManager?.smartbarManager?.smartbarView?.setHeightFactor(keyHeightFactor)
|
||||
val desiredHeight = keyHeightFactor * (florisboard?.inputView?.desiredTextKeyboardViewHeight ?: resources.getDimension(R.dimen.textKeyboardView_baseHeight).toInt())
|
||||
desiredKeyHeight = (desiredHeight / 4 - 2 * keyMarginV).roundToInt()
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(desiredHeight.roundToInt(), MeasureSpec.EXACTLY))
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
if (isPreviewMode) {
|
||||
setBackgroundColor(prefs.theme.keyboardBgColor)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,10 +295,4 @@ class KeyboardView : LinearLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
colorDrawable.color = getColorFromAttr(context, R.attr.keyboard_bgColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,17 @@
|
||||
package dev.patrickgold.florisboard.ime.text.smartbar
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.setBackgroundTintColor2
|
||||
|
||||
/**
|
||||
* Basically the same as an ImageButton.
|
||||
* @see [onMeasure] why this view class exists.
|
||||
*/
|
||||
class SmartbarQuickActionButton : androidx.appcompat.widget.AppCompatImageButton {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
@@ -37,4 +41,10 @@ class SmartbarQuickActionButton : androidx.appcompat.widget.AppCompatImageButton
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(heightMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
setBackgroundTintColor2(this, prefs.theme.smartbarButtonBgColor)
|
||||
setColorFilter(prefs.theme.smartbarButtonFgColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,22 @@
|
||||
package dev.patrickgold.florisboard.ime.text.smartbar
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
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 androidx.core.view.children
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.setImageTintColor2
|
||||
import kotlinx.android.synthetic.main.florisboard.view.*
|
||||
|
||||
/**
|
||||
* View class which keeps the references to important children and informs [SmartbarManager] that
|
||||
@@ -31,19 +40,15 @@ import dev.patrickgold.florisboard.R
|
||||
* a theme change).
|
||||
*/
|
||||
class SmartbarView : LinearLayout {
|
||||
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
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,24 +59,106 @@ 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies the default smartbar height with the given [factor] and sets it.
|
||||
* 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 setHeightFactor(factor: Float) {
|
||||
val baseSize = resources.getDimension(R.dimen.smartbar_height)
|
||||
val size = (baseSize * factor).toInt()
|
||||
layoutParams?.height = size
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
|
||||
val height = when (heightMode) {
|
||||
MeasureSpec.EXACTLY -> {
|
||||
// Must be this size
|
||||
heightSize
|
||||
}
|
||||
MeasureSpec.AT_MOST -> {
|
||||
// Can't be bigger than...
|
||||
(florisboard?.inputView?.desiredSmartbarHeight ?: 0).coerceAtMost(heightSize)
|
||||
}
|
||||
else -> {
|
||||
// Be whatever you want
|
||||
florisboard?.inputView?.desiredSmartbarHeight ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
setBackgroundColor(prefs.theme.smartbarBgColor)
|
||||
for (container in containers) {
|
||||
when (container.id) {
|
||||
R.id.number_row -> {
|
||||
for (button in container.children) {
|
||||
if (button is Button) {
|
||||
button.setTextColor(prefs.theme.smartbarFgColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.clipboard_cursor_row -> {
|
||||
for (button in container.children) {
|
||||
if (button is ImageButton) {
|
||||
if (button.isEnabled) {
|
||||
setImageTintColor2(button, prefs.theme.smartbarFgColor)
|
||||
} else {
|
||||
setImageTintColor2(button, prefs.theme.smartbarFgColorAlt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.candidates -> {
|
||||
for (view in container.children) {
|
||||
if (view is Button) {
|
||||
view.setTextColor(prefs.theme.smartbarFgColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
310
app/src/main/java/dev/patrickgold/florisboard/ime/theme/Theme.kt
Normal file
310
app/src/main/java/dev/patrickgold/florisboard/ime/theme/Theme.kt
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 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.theme
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
|
||||
/**
|
||||
* Data class which holds a parsed theme json file. Used for loading a theme
|
||||
* preset in Settings.
|
||||
* Note: this implementation is generic and allows for any group/attr names.
|
||||
* FlorisBoard itself expects certain groups and attrs to be able to
|
||||
* color the controls accordingly. See 'ime/themes/floris_day.json'
|
||||
* for a good example of which attributes FlorisBoard needs!
|
||||
*
|
||||
* @property name A unique id/name for this theme. Must only contain certain
|
||||
* characters: upper/lower case letters, numbers (not at the beginning!) or
|
||||
* an underline (_).
|
||||
* @property displayName The name of this theme when shown to the user. Can
|
||||
* contain any valid Unicode character.
|
||||
* @property author The name of the author of this theme. Should be your
|
||||
* username on GitHub/GitLab/BitBucket/... or your full name.
|
||||
* @property isNightTheme If this theme is meant for display at day (false)
|
||||
* or night (true). This property is only used to auto-assign this theme to
|
||||
* either the day or night theme list in Settings, which is used when the
|
||||
* user wants to auto-set his theme based on the current time.
|
||||
* @property rawAttrs Map which holds the raw attributes of this theme. Note
|
||||
* that the name of this property is 'attributes' within the json file!
|
||||
* Attributes are always grouped together. This ensures a better structure
|
||||
* and easier storage. The group- as well as the attr-name has the same
|
||||
* limitations as the theme [name].
|
||||
* Attribute values can be of different format:
|
||||
* 1. A color
|
||||
* Either #RRGGBB or #AARRGGBB (case-insensitive) -> e.g. #A034FF23
|
||||
* 2. A static word
|
||||
* - transparent (=0x00000000)
|
||||
* - true (=0x1)
|
||||
* - false (=0x0)
|
||||
* 3. A reference to another attribute within the SAME theme, as follows:
|
||||
* @group/attrName -> e.g. @window/textColor
|
||||
* Note that referencing attributes has its limitations:
|
||||
* a. Recursive references will cause an exception.
|
||||
* b. Referencing an previously defined attribute is fine.
|
||||
* c. Referencing an attribute not-yet defined is also ok, as long as
|
||||
* the reference can be resolved at the next iteration.
|
||||
* d. If the next iteration cannot resolve a value, an exception is
|
||||
* thrown.
|
||||
* 4. If the value is of any other format, an exception will be thrown.
|
||||
*
|
||||
* @throws IllegalArgumentException either at an invalid value or when a
|
||||
* reference cannot be resolved.
|
||||
*/
|
||||
data class Theme(
|
||||
val name: String,
|
||||
val displayName: String,
|
||||
val author: String,
|
||||
val isNightTheme: Boolean = false,
|
||||
@Json(name = "attributes")
|
||||
private val rawAttrs: Map<String, Map<String, String>>
|
||||
) {
|
||||
/**
|
||||
* Holds the parsed attributes after init.
|
||||
*/
|
||||
val parsedAttrs: MutableMap<String, MutableMap<String, Int>> = mutableMapOf()
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Loads a theme from the specified [path].
|
||||
*
|
||||
* @param context A reference to the current [Context]. Used to request
|
||||
* asset file.
|
||||
* @param path The path to the json theme file in the asset folder.
|
||||
* @returns A parsed [Theme] or null. A null value may indicate that
|
||||
* the file does not exist or that an error during the reading
|
||||
* of the file occurred.
|
||||
*/
|
||||
fun fromJsonFile(context: Context, path: String): Theme? {
|
||||
val rawJsonData: String = try {
|
||||
context.assets.open(path).bufferedReader().use { it.readText() }
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
} ?: return null
|
||||
return fromJsonString(rawJsonData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a theme from the given [rawData].
|
||||
*
|
||||
* @param rawData The raw json theme file as a string.
|
||||
* @returns A parsed [Theme] or null. A null value may indicate that an error
|
||||
* during the reading of the [rawData] occurred.
|
||||
*/
|
||||
fun fromJsonString(rawData: String): Theme? {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val layoutAdapter = moshi.adapter(Theme::class.java)
|
||||
return layoutAdapter.fromJson(rawData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a given [theme] to the [prefs]. The default color values are based off the
|
||||
* Floris Day theme and are not intended to be modified. Instead, themes should be defined
|
||||
* in assets/ime/theme/<theme_id>.json
|
||||
*
|
||||
* @param theme The theme data.
|
||||
* @param prefs The preference object to write the theme to.
|
||||
*/
|
||||
fun writeThemeToPrefs(prefs: PrefHelper, theme: Theme) {
|
||||
// Internal prefs part I
|
||||
prefs.internal.themeCurrentBasedOn = theme.name
|
||||
prefs.internal.themeCurrentIsNight = theme.isNightTheme
|
||||
|
||||
// Theme attributes
|
||||
prefs.theme.colorPrimary = theme.getAttr("window/colorPrimary", "#4CAF50")
|
||||
prefs.theme.colorPrimaryDark = theme.getAttr("window/colorPrimaryDark", "#388E3C")
|
||||
prefs.theme.colorAccent = theme.getAttr("window/colorAccent", "#FF9800")
|
||||
prefs.theme.navBarColor = theme.getAttr("window/navigationBarColor", "#E0E0E0")
|
||||
prefs.theme.navBarIsLight = (theme.getAttrOrNull("window/navigationBarLight") ?: 0) > 0
|
||||
|
||||
prefs.theme.keyboardBgColor = theme.getAttr("keyboard/bgColor", "#E0E0E0")
|
||||
|
||||
prefs.theme.keyBgColor = theme.getAttr("key/bgColor", "#FFFFFF")
|
||||
prefs.theme.keyBgColorPressed = theme.getAttr("key/bgColorPressed", "#F5F5F5")
|
||||
prefs.theme.keyFgColor = theme.getAttr("key/fgColor", "#000000")
|
||||
|
||||
prefs.theme.keyEnterBgColor = theme.getAttr("keyEnter/bgColor", "#4CAF50")
|
||||
prefs.theme.keyEnterBgColorPressed = theme.getAttr("keyEnter/bgColorPressed", "#388E3C")
|
||||
prefs.theme.keyEnterFgColor = theme.getAttr("keyEnter/fgColor", "#FFFFFF")
|
||||
|
||||
prefs.theme.keyPopupBgColor = theme.getAttr("keyPopup/bgColor", "#EEEEEE")
|
||||
prefs.theme.keyPopupBgColorActive = theme.getAttr("keyPopup/bgColorActive", "#BDBDBD")
|
||||
prefs.theme.keyPopupFgColor = theme.getAttr("keyPopup/fgColor", "#000000")
|
||||
|
||||
prefs.theme.keyShiftBgColor = theme.getAttr("keyShift/bgColor", "#FFFFFF")
|
||||
prefs.theme.keyShiftBgColorPressed = theme.getAttr("keyShift/bgColorPressed", "#F5F5F5")
|
||||
prefs.theme.keyShiftFgColor = theme.getAttr("keyShift/fgColor", "#000000")
|
||||
prefs.theme.keyShiftFgColorCapsLock = theme.getAttr("keyShift/fgColorCapsLock", "#FF9800")
|
||||
|
||||
prefs.theme.mediaFgColor = theme.getAttr("media/fgColor", "#000000")
|
||||
prefs.theme.mediaFgColorAlt = theme.getAttr("media/fgColorAlt", "#757575")
|
||||
|
||||
prefs.theme.oneHandedBgColor = theme.getAttr("oneHanded/bgColor", "#E8F5E9")
|
||||
|
||||
prefs.theme.oneHandedButtonFgColor = theme.getAttr("oneHandedButton/fgColor", "#424242")
|
||||
|
||||
prefs.theme.smartbarBgColor = theme.getAttr("smartbar/bgColor", "#E0E0E0")
|
||||
prefs.theme.smartbarFgColor = theme.getAttr("smartbar/fgColor", "#000000")
|
||||
prefs.theme.smartbarFgColorAlt = theme.getAttr("smartbar/fgColorAlt", "#4A000000")
|
||||
|
||||
prefs.theme.smartbarButtonBgColor = theme.getAttr("smartbarButton/bgColor", "#FFFFFF")
|
||||
prefs.theme.smartbarButtonFgColor = theme.getAttr("smartbarButton/fgColor", "#000000")
|
||||
|
||||
// Internal prefs part II (must be written at the end!!)
|
||||
prefs.internal.themeCurrentIsModified = false
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val listOfAttrsToReevaluate = mutableListOf<Triple<String, String, String>>()
|
||||
for (group in rawAttrs) {
|
||||
val groupMap = mutableMapOf<String, Int>()
|
||||
parsedAttrs[group.key] = groupMap
|
||||
for (attr in group.value) {
|
||||
val colorRegex = """[#]([0-9a-fA-F]{8}|[0-9a-fA-F]{6})""".toRegex()
|
||||
val refRegex = """[@]([a-zA-Z_][a-zA-Z0-9_]*)[/]([a-zA-Z_][a-zA-Z0-9_]*)""".toRegex()
|
||||
when {
|
||||
attr.value.matches(colorRegex) -> {
|
||||
groupMap[attr.key] = Color.parseColor(attr.value)
|
||||
}
|
||||
attr.value == "transparent" -> {
|
||||
groupMap[attr.key] = Color.TRANSPARENT
|
||||
}
|
||||
attr.value == "true" -> {
|
||||
groupMap[attr.key] = 0x1
|
||||
}
|
||||
attr.value == "false" -> {
|
||||
groupMap[attr.key] = 0x0
|
||||
}
|
||||
attr.value.matches(refRegex) -> {
|
||||
val attrValue = getAttrOrNull(attr.value.substring(1))
|
||||
if (attrValue != null) {
|
||||
groupMap[attr.key] = attrValue
|
||||
} else {
|
||||
listOfAttrsToReevaluate.add(Triple(group.key, attr.key, attr.value))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("The specified attr '${attr.key}' = '${attr.value}' is not valid!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (attrToReevaluate in listOfAttrsToReevaluate) {
|
||||
val attrValue = getAttrOrNull(attrToReevaluate.third.substring(1))
|
||||
if (attrValue != null) {
|
||||
parsedAttrs[attrToReevaluate.first]?.put(attrToReevaluate.second, attrValue)
|
||||
} else {
|
||||
throw IllegalArgumentException("The specified attr '${attrToReevaluate.second}' = '${attrToReevaluate.third}' is not valid!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAttr(key: String, defaultColor: String): Int {
|
||||
return getAttrOrNull(key) ?: Color.parseColor(defaultColor)
|
||||
}
|
||||
fun getAttr(group: String, attr: String, defaultColor: String): Int {
|
||||
return getAttrOrNull(group, attr) ?: Color.parseColor(defaultColor)
|
||||
}
|
||||
|
||||
fun getAttrOrNull(key: String): Int? {
|
||||
val regex = """([a-zA-Z_][a-zA-Z0-9_]*)[/]([a-zA-Z_][a-zA-Z0-9_]*)""".toRegex()
|
||||
return if (key.matches(regex)) {
|
||||
val split = key.split("/")
|
||||
getAttrOrNull(split[0], split[1])
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
fun getAttrOrNull(group: String, attr: String): Int? {
|
||||
return parsedAttrs[group]?.get(attr)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class which is used to quickly parse only the relevant meta data to
|
||||
* display a theme in a selection list.
|
||||
*
|
||||
* @see [Theme] for details regarding the attributes and the theme structure.
|
||||
*/
|
||||
data class ThemeMetaOnly(
|
||||
val name: String,
|
||||
val displayName: String,
|
||||
val author: String,
|
||||
val isNightTheme: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Loads the theme meta data from the specified [path].
|
||||
*
|
||||
* @param context A reference to the current [Context]. Used to request
|
||||
* asset file.
|
||||
* @param path The path to the json theme file in the asset folder.
|
||||
* @returns [ThemeMetaOnly] or null. A null value may indicate that
|
||||
* the file does not exist or that an error during the reading
|
||||
* of the file occurred.
|
||||
*/
|
||||
fun loadFromJsonFile(context: Context, path: String): ThemeMetaOnly? {
|
||||
val rawJsonData: String = try {
|
||||
context.assets.open(path).bufferedReader().use { it.readText() }
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
} ?: return null
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val layoutAdapter = moshi.adapter(ThemeMetaOnly::class.java)
|
||||
return layoutAdapter.fromJson(rawJsonData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all theme meta data from the specified [path].
|
||||
*
|
||||
* @param context A reference to the current [Context]. Used to request
|
||||
* asset file.
|
||||
* @param path The path to the dir in the asset folder.
|
||||
* @returns [ThemeMetaOnly] or null. A null value may indicate that
|
||||
* the file does not exist or that an error during the reading
|
||||
* of the file occurred.
|
||||
*/
|
||||
fun loadAllFromDir(context: Context, path: String): List<ThemeMetaOnly> {
|
||||
val ret = mutableListOf<ThemeMetaOnly>()
|
||||
try {
|
||||
val list = context.assets.list(path)
|
||||
if (list != null && list.isNotEmpty()) {
|
||||
// Is a folder
|
||||
for (file in list) {
|
||||
val subList = context.assets.list("$path/$file")
|
||||
if (subList?.isEmpty() == true) {
|
||||
// Is file
|
||||
val metaData = loadFromJsonFile(context, "$path/$file")
|
||||
if (metaData != null) {
|
||||
ret.add(metaData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: java.lang.Exception) {}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.AdvancedActivityBinding
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.PackageManagerUtils
|
||||
|
||||
class AdvancedActivity : AppCompatActivity(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private lateinit var binding: AdvancedActivityBinding
|
||||
private lateinit var prefs: PrefHelper
|
||||
|
||||
companion object {
|
||||
const val RESULT_APPLY_THEME = 0x322D
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
prefs = PrefHelper.getDefaultInstance(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = AdvancedActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
supportActionBar?.setTitle(R.string.settings__advanced__title)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
|
||||
prefs.sync()
|
||||
if (key == PrefHelper.Advanced.SETTINGS_THEME) {
|
||||
setResult(RESULT_APPLY_THEME)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
prefs.shared.registerOnSharedPreferenceChangeListener(this)
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
prefs.shared.unregisterOnSharedPreferenceChangeListener(this)
|
||||
updateLauncherIconStatus()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private fun updateLauncherIconStatus() {
|
||||
// Set LauncherAlias enabled/disabled state just before destroying/pausing this activity
|
||||
if (prefs.advanced.showAppIcon) {
|
||||
PackageManagerUtils.showAppIcon(this)
|
||||
} else {
|
||||
PackageManagerUtils.hideAppIcon(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentAdvancedBinding
|
||||
|
||||
class AdvancedFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private lateinit var binding: SettingsFragmentAdvancedBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = SettingsFragmentAdvancedBinding.inflate(inflater, container, false)
|
||||
|
||||
val transaction = childFragmentManager.beginTransaction()
|
||||
transaction.replace(
|
||||
binding.prefsAdvancedFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_advanced)
|
||||
)
|
||||
transaction.commit()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentLooknfeelBinding
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
import dev.patrickgold.florisboard.ime.text.layout.LayoutManager
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class LooknfeelFragment : SettingsMainActivity.SettingsFragment(), CoroutineScope by MainScope() {
|
||||
private lateinit var binding: SettingsFragmentLooknfeelBinding
|
||||
private lateinit var keyboardView: KeyboardView
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = SettingsFragmentLooknfeelBinding.inflate(inflater, container, false)
|
||||
|
||||
launch(Dispatchers.Default) {
|
||||
val themeContext = ContextThemeWrapper(context, prefs.theme.getSelectedThemeResId())
|
||||
val layoutManager = LayoutManager(themeContext)
|
||||
keyboardView = KeyboardView(themeContext)
|
||||
keyboardView.prefs = prefs
|
||||
keyboardView.isPreviewMode = true
|
||||
keyboardView.computedLayout = layoutManager.fetchComputedLayoutAsync(KeyboardMode.CHARACTERS, Subtype.DEFAULT).await()
|
||||
keyboardView.updateVisibility()
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.themeLinearLayout.addView(keyboardView, 0)
|
||||
}
|
||||
}
|
||||
|
||||
val transaction = childFragmentManager.beginTransaction()
|
||||
transaction.replace(
|
||||
binding.prefsLooknfeelFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_looknfeel)
|
||||
)
|
||||
transaction.replace(
|
||||
binding.prefsThemeFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_theme)
|
||||
)
|
||||
transaction.commit()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -28,32 +28,33 @@ import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsActivityBinding
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.core.SubtypeManager
|
||||
import dev.patrickgold.florisboard.settings.fragments.*
|
||||
import dev.patrickgold.florisboard.util.AppVersionUtils
|
||||
import dev.patrickgold.florisboard.util.PackageManagerUtils
|
||||
|
||||
private const val FRAGMENT_TAG = "FRAGMENT_TAG"
|
||||
internal const val FRAGMENT_TAG = "FRAGMENT_TAG"
|
||||
private const val PREF_RES_ID = "PREF_RES_ID"
|
||||
private const val SELECTED_ITEM_ID = "SELECTED_ITEM_ID"
|
||||
private const val ADVANCED_REQ_CODE = 0x145F
|
||||
|
||||
class SettingsMainActivity : AppCompatActivity(),
|
||||
BottomNavigationView.OnNavigationItemSelectedListener,
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
lateinit var binding: SettingsActivityBinding
|
||||
lateinit var prefs: PrefHelper
|
||||
private lateinit var prefs: PrefHelper
|
||||
lateinit var subtypeManager: SubtypeManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
prefs = PrefHelper(this, PreferenceManager.getDefaultSharedPreferences(this))
|
||||
prefs = PrefHelper.getDefaultInstance(this)
|
||||
prefs.initDefaultPreferences()
|
||||
subtypeManager =
|
||||
SubtypeManager(this, prefs)
|
||||
prefs.sync()
|
||||
subtypeManager = SubtypeManager(this, prefs)
|
||||
|
||||
val mode = when (prefs.advanced.settingsTheme) {
|
||||
"light" -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
@@ -99,9 +100,14 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
loadFragment(KeyboardFragment())
|
||||
true
|
||||
}
|
||||
R.id.settings__navigation__looknfeel -> {
|
||||
supportActionBar?.setTitle(R.string.settings__looknfeel__title)
|
||||
loadFragment(LooknfeelFragment())
|
||||
R.id.settings__navigation__typing -> {
|
||||
supportActionBar?.setTitle(R.string.settings__typing__title)
|
||||
loadFragment(TypingFragment())
|
||||
true
|
||||
}
|
||||
R.id.settings__navigation__theme -> {
|
||||
supportActionBar?.setTitle(R.string.settings__theme__title)
|
||||
loadFragment(ThemeFragment())
|
||||
true
|
||||
}
|
||||
R.id.settings__navigation__gestures -> {
|
||||
@@ -109,20 +115,15 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
loadFragment(GesturesFragment())
|
||||
true
|
||||
}
|
||||
R.id.settings__navigation__advanced -> {
|
||||
supportActionBar?.setTitle(R.string.settings__advanced__title)
|
||||
loadFragment(AdvancedFragment())
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFragment(fragment: Fragment) {
|
||||
val transaction = supportFragmentManager.beginTransaction()
|
||||
transaction.replace(binding.pageFrame.id, fragment, FRAGMENT_TAG)
|
||||
//transaction.addToBackStack(null)
|
||||
transaction.commit()
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(binding.pageFrame.id, fragment, FRAGMENT_TAG)
|
||||
.commit()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
@@ -130,6 +131,14 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (binding.bottomNavigation.selectedItemId != R.id.settings__navigation__home) {
|
||||
binding.bottomNavigation.selectedItemId = R.id.settings__navigation__home
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
@@ -144,6 +153,10 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
startActivity(browserIntent)
|
||||
true
|
||||
}
|
||||
R.id.settings__menu_advanced -> {
|
||||
startActivityForResult(Intent(this, AdvancedActivity::class.java), ADVANCED_REQ_CODE)
|
||||
true
|
||||
}
|
||||
R.id.settings__menu_about -> {
|
||||
startActivity(Intent(this, AboutActivity::class.java))
|
||||
true
|
||||
@@ -152,22 +165,18 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
|
||||
if (key == PrefHelper.Advanced.SETTINGS_THEME) {
|
||||
recreate()
|
||||
}
|
||||
val fragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG)
|
||||
if (fragment != null && fragment.isVisible) {
|
||||
if (fragment is LooknfeelFragment) {
|
||||
if (key == PrefHelper.Theme.NAME) {
|
||||
// TODO: recreate() is only a lazy solution, better would be to only recreate
|
||||
// the keyboard view
|
||||
recreate()
|
||||
}
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == ADVANCED_REQ_CODE) {
|
||||
if (resultCode == AdvancedActivity.RESULT_APPLY_THEME) {
|
||||
recreate()
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {}
|
||||
|
||||
private fun updateLauncherIconStatus() {
|
||||
// Set LauncherAlias enabled/disabled state just before destroying/pausing this activity
|
||||
if (prefs.advanced.showAppIcon) {
|
||||
@@ -195,7 +204,6 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
}
|
||||
|
||||
abstract class SettingsFragment : Fragment() {
|
||||
protected lateinit var prefs: PrefHelper
|
||||
protected lateinit var settingsMainActivity: SettingsMainActivity
|
||||
protected lateinit var subtypeManager: SubtypeManager
|
||||
|
||||
@@ -203,7 +211,6 @@ class SettingsMainActivity : AppCompatActivity(),
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
settingsMainActivity = activity as SettingsMainActivity
|
||||
prefs = settingsMainActivity.prefs
|
||||
subtypeManager = settingsMainActivity.subtypeManager
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.settings.components
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.SeekBar
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SeekBarDialogBinding
|
||||
|
||||
/**
|
||||
* Custom preference which represents a seek bar which shows the current value in the summary. The
|
||||
* value can be changed by clicking on the preference, which brings up a dialog which a seek bar.
|
||||
* This implementation also allows for a min / max step value, while being backwards compatible.
|
||||
*
|
||||
* @see R.styleable.DialogSeekBarPreferenceAttrs for which xml attributes this preference accepts
|
||||
* besides the default Preference attributes.
|
||||
*
|
||||
* @property defaultValue The default value of this preference.
|
||||
* @property systemDefaultValue At this exact value [systemDefaultValueText] should be shown instead
|
||||
* of the actual value.
|
||||
* @property systemDefaultValueText The text to show if this preference's value or seek bar is
|
||||
* [systemDefaultValue]. Set to null to disable the system default text feature.
|
||||
* @property min The minimum value of the seek bar. Must not be greater or equal than [max].
|
||||
* @property max The maximum value of the seek bar. Must not be lesser or equal than [min].
|
||||
* @property step The step in which the seek bar increases per move. If the provided value is less
|
||||
* than 1, 1 will be used as step. Note that the xml attribute's name for this property is
|
||||
* [R.styleable.DialogSeekBarPreferenceAttrs_seekBarIncrement].
|
||||
* @property unit The unit to show after the value. Set to an empty string to disable this feature.
|
||||
*/
|
||||
class DialogSeekBarPreference : Preference {
|
||||
private var defaultValue: Int = 0
|
||||
private var systemDefaultValue: Int = -1
|
||||
private var systemDefaultValueText: String? = null
|
||||
private var min: Int = 0
|
||||
private var max: Int = 100
|
||||
private var step: Int = 1
|
||||
private var unit: String = ""
|
||||
|
||||
@Suppress("unused")
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
layoutResource = R.layout.list_item
|
||||
context.obtainStyledAttributes(attrs, R.styleable.DialogSeekBarPreferenceAttrs).apply {
|
||||
min = getInt(R.styleable.DialogSeekBarPreferenceAttrs_min, min)
|
||||
max = getInt(R.styleable.DialogSeekBarPreferenceAttrs_max, max)
|
||||
step = getInt(R.styleable.DialogSeekBarPreferenceAttrs_seekBarIncrement, step)
|
||||
if (step < 1) {
|
||||
step = 1
|
||||
}
|
||||
defaultValue = getInt(R.styleable.DialogSeekBarPreferenceAttrs_android_defaultValue, defaultValue)
|
||||
systemDefaultValue = getInt(R.styleable.DialogSeekBarPreferenceAttrs_systemDefaultValue, min - 1)
|
||||
systemDefaultValueText = getString(R.styleable.DialogSeekBarPreferenceAttrs_systemDefaultValueText)
|
||||
unit = getString(R.styleable.DialogSeekBarPreferenceAttrs_unit) ?: unit
|
||||
recycle()
|
||||
}
|
||||
onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue ->
|
||||
summary = getTextForValue(newValue.toString())
|
||||
true
|
||||
}
|
||||
onPreferenceClickListener = OnPreferenceClickListener {
|
||||
showSeekBarDialog()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToHierarchy(preferenceManager: PreferenceManager?) {
|
||||
super.onAttachedToHierarchy(preferenceManager)
|
||||
summary = getTextForValue(sharedPreferences.getInt(key, defaultValue))
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the text for the given [value] and adds the defined [unit] at the end.
|
||||
* If [systemDefaultValueText] is not null this method tries to match the given [value] with
|
||||
* [systemDefaultValue] and returns [systemDefaultValueText] upon matching.
|
||||
*/
|
||||
private fun getTextForValue(value: Any): String {
|
||||
if (value !is Int) {
|
||||
return "??$unit"
|
||||
}
|
||||
val systemDefValText = systemDefaultValueText
|
||||
return if (value == systemDefaultValue && systemDefValText != null) {
|
||||
systemDefValText
|
||||
} else {
|
||||
value.toString() + unit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the seek bar dialog.
|
||||
*/
|
||||
private fun showSeekBarDialog() {
|
||||
val inflater =
|
||||
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
val dialogView = SeekBarDialogBinding.inflate(inflater)
|
||||
val initValue = sharedPreferences.getInt(key, defaultValue)
|
||||
dialogView.seekBar.max = actualValueToSeekBarProgress(max)
|
||||
dialogView.seekBar.progress = actualValueToSeekBarProgress(initValue)
|
||||
dialogView.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
dialogView.seekBarValue.text = getTextForValue(seekBarProgressToActualValue(progress))
|
||||
}
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
dialogView.seekBarValue.text = getTextForValue(initValue)
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(this@DialogSeekBarPreference.title)
|
||||
setCancelable(true)
|
||||
setView(dialogView.root)
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val actualValue = seekBarProgressToActualValue(dialogView.seekBar.progress)
|
||||
sharedPreferences.edit().putInt(key, actualValue).apply()
|
||||
}
|
||||
setNeutralButton(R.string.settings__default) { _, _ ->
|
||||
sharedPreferences.edit().putInt(key, defaultValue).apply()
|
||||
}
|
||||
setNegativeButton(android.R.string.cancel, null)
|
||||
setOnDismissListener { summary = getTextForValue(sharedPreferences.getInt(key, defaultValue)) }
|
||||
create()
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the actual value to a progress value which the Android SeekBar implementation can
|
||||
* handle. (Android's SeekBar step is fixed at 1 and min at 0)
|
||||
*
|
||||
* @param actual The actual value.
|
||||
* @returns the internal value which is used to allow different min and step values.
|
||||
*/
|
||||
private fun actualValueToSeekBarProgress(actual: Int): Int {
|
||||
return (actual - min) / step
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the Android SeekBar value to the actual value.
|
||||
*
|
||||
* @param progress The progress value of the SeekBar.
|
||||
* @returns the actual value which is ready to use.
|
||||
*/
|
||||
private fun seekBarProgressToActualValue(progress: Int): Int {
|
||||
return (progress * step) + min
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.content.SharedPreferences
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.ThemeSelectorDialogBinding
|
||||
import dev.patrickgold.florisboard.databinding.ThemeSelectorListItemBinding
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMetaOnly
|
||||
|
||||
/**
|
||||
* Custom preference which handles the theme preset selection dialog and shows a summary in the
|
||||
* list.
|
||||
*/
|
||||
class ThemePresetSelectorPreference : Preference, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private var dialog: AlertDialog? = null
|
||||
private val metaDataCache: MutableMap<String, ThemeMetaOnly> = mutableMapOf()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
@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
|
||||
onPreferenceClickListener = OnPreferenceClickListener {
|
||||
showThemeSelectorDialog()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToHierarchy(preferenceManager: PreferenceManager?) {
|
||||
super.onAttachedToHierarchy(preferenceManager)
|
||||
summary = generateSummaryText()
|
||||
prefs.shared.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onDetached() {
|
||||
if (dialog?.isShowing == true) {
|
||||
dialog?.dismiss()
|
||||
}
|
||||
prefs.shared.unregisterOnSharedPreferenceChangeListener(this)
|
||||
super.onDetached()
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
|
||||
if (key == PrefHelper.Internal.THEME_CURRENT_IS_MODIFIED) {
|
||||
summary = generateSummaryText()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the summary text to display and returns it. Based on the prefs.internal.theme*
|
||||
* values and the theme meta cache.
|
||||
*/
|
||||
private fun generateSummaryText(): String {
|
||||
val themeKey = prefs.internal.themeCurrentBasedOn
|
||||
val isModified = prefs.internal.themeCurrentIsModified
|
||||
var metaOnly: ThemeMetaOnly? = metaDataCache[themeKey]
|
||||
if (metaOnly == null) {
|
||||
try {
|
||||
metaOnly = ThemeMetaOnly.loadFromJsonFile(context, "ime/theme/$themeKey.json")
|
||||
} catch (e: Exception) {
|
||||
return context.resources.getString(R.string.settings__theme__undefined)
|
||||
}
|
||||
}
|
||||
metaOnly ?: return context.resources.getString(R.string.settings__theme__undefined)
|
||||
return if (isModified) {
|
||||
String.format(context.resources.getString(R.string.settings__theme__preset_summary), metaOnly.displayName)
|
||||
} else {
|
||||
metaOnly.displayName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the theme selector dialog.
|
||||
*/
|
||||
private fun showThemeSelectorDialog() {
|
||||
val inflater =
|
||||
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
val dialogView = ThemeSelectorDialogBinding.inflate(inflater)
|
||||
val selectedThemeView = ThemeSelectorListItemBinding.inflate(inflater)
|
||||
selectedThemeView.title.text = generateSummaryText()
|
||||
dialogView.content.addView(selectedThemeView.root, 1)
|
||||
metaDataCache.clear()
|
||||
ThemeMetaOnly.loadAllFromDir(context, "ime/theme").forEach { metaData ->
|
||||
metaDataCache[metaData.name] = metaData
|
||||
}
|
||||
for ((themeKey, metaData) in metaDataCache) {
|
||||
if (themeKey == prefs.internal.themeCurrentBasedOn && !prefs.internal.themeCurrentIsModified) {
|
||||
continue
|
||||
}
|
||||
val availableThemeView = ThemeSelectorListItemBinding.inflate(inflater)
|
||||
availableThemeView.title.text = metaData.displayName
|
||||
availableThemeView.root.setOnClickListener {
|
||||
applyThemePreset(metaData.name)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
dialogView.content.addView(availableThemeView.root)
|
||||
}
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(this@ThemePresetSelectorPreference.title)
|
||||
setCancelable(true)
|
||||
setView(dialogView.root)
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
//
|
||||
}
|
||||
setNeutralButton(R.string.settings__default) { _, _ ->
|
||||
//
|
||||
}
|
||||
setNegativeButton(android.R.string.cancel, null)
|
||||
setOnDismissListener { summary = generateSummaryText() }
|
||||
create()
|
||||
dialog = show()
|
||||
dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the Theme for given [themeKey] to the preferences. Overrides any custom user-defined
|
||||
* theme in the shared prefs, if existent.
|
||||
*
|
||||
* @param themeKey The key of the Theme preset to be applied.
|
||||
*/
|
||||
private fun applyThemePreset(themeKey: String) {
|
||||
val theme = Theme.fromJsonFile(context, "ime/theme/$themeKey.json") ?: return
|
||||
Theme.writeThemeToPrefs(prefs, theme)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
class AdvancedFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.prefs_advanced)
|
||||
}
|
||||
}
|
||||
@@ -14,25 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.settings
|
||||
package dev.patrickgold.florisboard.settings.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentGesturesBinding
|
||||
|
||||
class GesturesFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private lateinit var binding: SettingsFragmentGesturesBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = SettingsFragmentGesturesBinding.inflate(inflater, container, false)
|
||||
|
||||
return binding.root
|
||||
class GesturesFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.prefs_gestures)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.settings
|
||||
package dev.patrickgold.florisboard.settings.fragments
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
@@ -25,6 +25,7 @@ import android.view.ViewGroup
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentHomeBinding
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.settings.SettingsMainActivity
|
||||
import dev.patrickgold.florisboard.setup.SetupActivity
|
||||
|
||||
class HomeFragment : SettingsMainActivity.SettingsFragment() {
|
||||
@@ -56,6 +57,12 @@ class HomeFragment : SettingsMainActivity.SettingsFragment() {
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
binding.localizationCard.setOnClickListener {
|
||||
settingsMainActivity.binding.bottomNavigation.selectedItemId = R.id.settings__navigation__typing
|
||||
}
|
||||
binding.themeCard.setOnClickListener {
|
||||
settingsMainActivity.binding.bottomNavigation.selectedItemId = R.id.settings__navigation__theme
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
class KeyboardFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.prefs_keyboard)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
|
||||
class ThemeCustomizeFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.prefs_theme)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.fragments
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentThemeBinding
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
import dev.patrickgold.florisboard.ime.text.layout.LayoutManager
|
||||
import dev.patrickgold.florisboard.settings.SettingsMainActivity
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class ThemeFragment : SettingsMainActivity.SettingsFragment(), CoroutineScope by MainScope(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private lateinit var binding: SettingsFragmentThemeBinding
|
||||
private lateinit var keyboardView: KeyboardView
|
||||
private lateinit var prefs: PrefHelper
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
prefs = PrefHelper.getDefaultInstance(requireContext())
|
||||
binding = SettingsFragmentThemeBinding.inflate(inflater, container, false)
|
||||
|
||||
launch(Dispatchers.Default) {
|
||||
val themeContext = ContextThemeWrapper(context, FlorisBoard.getDayNightBaseThemeId(prefs.internal.themeCurrentIsNight))
|
||||
val layoutManager = LayoutManager(themeContext)
|
||||
keyboardView = KeyboardView(themeContext)
|
||||
keyboardView.layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
val m = resources.getDimension(R.dimen.keyboard_preview_margin).toInt()
|
||||
setMargins(m, m, m, m)
|
||||
}
|
||||
prefs.sync()
|
||||
keyboardView.isPreviewMode = true
|
||||
val subtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
|
||||
keyboardView.computedLayout = layoutManager.fetchComputedLayoutAsync(KeyboardMode.CHARACTERS, subtype).await()
|
||||
keyboardView.updateVisibility()
|
||||
keyboardView.onApplyThemeAttributes()
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.root.addView(keyboardView, 0)
|
||||
}
|
||||
}
|
||||
|
||||
loadThemePrefFragment()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun loadThemePrefFragment() {
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
binding.prefsFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_theme)
|
||||
)
|
||||
.commit()
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
|
||||
prefs.sync()
|
||||
key ?: return
|
||||
if (key == PrefHelper.Internal.THEME_CURRENT_BASED_ON ||
|
||||
key == PrefHelper.Internal.THEME_CURRENT_IS_MODIFIED && !prefs.internal.themeCurrentIsModified) {
|
||||
loadThemePrefFragment()
|
||||
}
|
||||
if (key.startsWith("theme__")) {
|
||||
prefs.internal.themeCurrentIsModified = true
|
||||
keyboardView.onApplyThemeAttributes()
|
||||
keyboardView.invalidate()
|
||||
keyboardView.invalidateAllKeys()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
prefs.shared.registerOnSharedPreferenceChangeListener(this)
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
prefs.shared.unregisterOnSharedPreferenceChangeListener(this)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.settings
|
||||
package dev.patrickgold.florisboard.settings.fragments
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
@@ -23,16 +23,15 @@ 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.SettingsFragmentKeyboardBinding
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentKeyboardSubtypeDialogBinding
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentKeyboardSubtypeListItemBinding
|
||||
import dev.patrickgold.florisboard.databinding.ListItemBinding
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentTypingBinding
|
||||
import dev.patrickgold.florisboard.databinding.SettingsFragmentTypingSubtypeDialogBinding
|
||||
import dev.patrickgold.florisboard.settings.SettingsMainActivity
|
||||
import dev.patrickgold.florisboard.util.LocaleUtils
|
||||
|
||||
class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private lateinit var binding: SettingsFragmentKeyboardBinding
|
||||
class TypingFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private lateinit var binding: SettingsFragmentTypingBinding
|
||||
/**
|
||||
* Must always have a reference to the open AlertDialog to dismiss the AlertDialog in the event
|
||||
* of onDestroy(), if this is not done a memory leak will most likely happen!
|
||||
@@ -44,17 +43,18 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = SettingsFragmentKeyboardBinding.inflate(inflater, container, false)
|
||||
binding = SettingsFragmentTypingBinding.inflate(inflater, container, false)
|
||||
binding.subtypeAddBtn.setOnClickListener { showAddSubtypeDialog() }
|
||||
|
||||
updateSubtypeListView()
|
||||
|
||||
val transaction = childFragmentManager.beginTransaction()
|
||||
transaction.replace(
|
||||
binding.prefsKeyboardFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_keyboard)
|
||||
)
|
||||
transaction.commit()
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
binding.prefsFrame.id,
|
||||
SettingsMainActivity.PrefFragment.createFromResource(R.xml.prefs_typing)
|
||||
)
|
||||
.commit()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
@@ -66,7 +66,7 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
|
||||
private fun showAddSubtypeDialog() {
|
||||
val dialogView =
|
||||
SettingsFragmentKeyboardSubtypeDialogBinding.inflate(layoutInflater)
|
||||
SettingsFragmentTypingSubtypeDialogBinding.inflate(layoutInflater)
|
||||
val languageAdapter: ArrayAdapter<String> = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
@@ -95,11 +95,11 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
)
|
||||
dialogView.layoutSpinner.adapter = layoutAdapter
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.settings__keyboard__subtype_add_title)
|
||||
setTitle(R.string.settings__localization__subtype_add_title)
|
||||
setCancelable(true)
|
||||
setView(dialogView.root)
|
||||
setPositiveButton(R.string.settings__keyboard__subtype_add, null)
|
||||
setNegativeButton(R.string.settings__keyboard__subtype_cancel) { _, _ -> }
|
||||
setPositiveButton(R.string.settings__localization__subtype_add, null)
|
||||
setNegativeButton(R.string.settings__localization__subtype_cancel) { _, _ -> }
|
||||
setOnDismissListener { activeDialogWindow = null }
|
||||
create()
|
||||
activeDialogWindow = show()
|
||||
@@ -110,7 +110,7 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
val layoutName = subtypeManager.imeConfig.characterLayouts.keys.toList()[dialogView.layoutSpinner.selectedItemPosition]
|
||||
val success = subtypeManager.addSubtype(LocaleUtils.stringToLocale(languageCode), layoutName)
|
||||
if (!success) {
|
||||
dialogView.errorBox.setText(R.string.settings__keyboard__subtype_error_already_exists)
|
||||
dialogView.errorBox.setText(R.string.settings__localization__subtype_error_already_exists)
|
||||
dialogView.errorBox.visibility = View.VISIBLE
|
||||
} else {
|
||||
updateSubtypeListView()
|
||||
@@ -123,7 +123,7 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
private fun showEditSubtypeDialog(id: Int) {
|
||||
val subtype = subtypeManager.getSubtypeById(id) ?: return
|
||||
val dialogView =
|
||||
SettingsFragmentKeyboardSubtypeDialogBinding.inflate(layoutInflater)
|
||||
SettingsFragmentTypingSubtypeDialogBinding.inflate(layoutInflater)
|
||||
val languageAdapter: ArrayAdapter<String> = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
@@ -143,10 +143,10 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
subtypeManager.imeConfig.characterLayouts.keys.toList().indexOf(subtype.layout)
|
||||
)
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.settings__keyboard__subtype_edit_title)
|
||||
setTitle(R.string.settings__localization__subtype_edit_title)
|
||||
setCancelable(true)
|
||||
setView(dialogView.root)
|
||||
setPositiveButton(R.string.settings__keyboard__subtype_apply) { _, _ ->
|
||||
setPositiveButton(R.string.settings__localization__subtype_apply) { _, _ ->
|
||||
val languageCode = subtypeManager.imeConfig.defaultSubtypesLanguageCodes[dialogView.languageSpinner.selectedItemPosition]
|
||||
val layoutName = subtypeManager.imeConfig.characterLayouts.keys.toList()[dialogView.layoutSpinner.selectedItemPosition]
|
||||
subtype.locale = LocaleUtils.stringToLocale(languageCode)
|
||||
@@ -154,11 +154,11 @@ class KeyboardFragment : SettingsMainActivity.SettingsFragment() {
|
||||
subtypeManager.modifySubtypeWithSameId(subtype)
|
||||
updateSubtypeListView()
|
||||
}
|
||||
setNeutralButton(R.string.settings__keyboard__subtype_delete) { _, _ ->
|
||||
setNeutralButton(R.string.settings__localization__subtype_delete) { _, _ ->
|
||||
subtypeManager.removeSubtype(subtype)
|
||||
updateSubtypeListView()
|
||||
}
|
||||
setNegativeButton(R.string.settings__keyboard__subtype_cancel) { _, _ -> }
|
||||
setNegativeButton(R.string.settings__localization__subtype_cancel) { _, _ -> }
|
||||
setOnDismissListener { activeDialogWindow = null }
|
||||
create()
|
||||
activeDialogWindow = show()
|
||||
@@ -174,9 +174,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)
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import dev.patrickgold.florisboard.databinding.SetupFragmentFinishBinding
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
|
||||
class FinishFragment : Fragment() {
|
||||
private lateinit var binding: SetupFragmentFinishBinding
|
||||
@@ -33,6 +34,12 @@ class FinishFragment : Fragment() {
|
||||
): View? {
|
||||
binding = SetupFragmentFinishBinding.inflate(inflater, container, false)
|
||||
|
||||
// Set theme to floris_day
|
||||
Theme.writeThemeToPrefs(
|
||||
(activity as SetupActivity).prefs,
|
||||
Theme.fromJsonFile(requireContext(), "ime/theme/floris_day.json")!!
|
||||
)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package dev.patrickgold.florisboard.setup
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -26,6 +27,7 @@ import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
|
||||
class MakeDefaultFragment : Fragment(), SetupActivity.EventListener {
|
||||
private lateinit var binding: SetupFragmentMakeDefaultBinding
|
||||
private var osHandler: Handler? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@@ -60,6 +62,11 @@ class MakeDefaultFragment : Fragment(), SetupActivity.EventListener {
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
if (hasFocus && context != null) {
|
||||
updateState()
|
||||
if (osHandler == null) {
|
||||
osHandler = Handler()
|
||||
}
|
||||
osHandler?.postDelayed({ updateState() }, 250)
|
||||
osHandler?.postDelayed({ updateState() }, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class SetupActivity : AppCompatActivity() {
|
||||
private lateinit var adapter: ViewPagerAdapter
|
||||
private lateinit var binding: SetupActivityBinding
|
||||
lateinit var imm: InputMethodManager
|
||||
private lateinit var prefs: PrefHelper
|
||||
lateinit var prefs: PrefHelper
|
||||
private var shouldFinish: Boolean = false
|
||||
private var shouldLaunchSettings: Boolean = true
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import androidx.core.view.children
|
||||
|
||||
fun getColorFromAttr(
|
||||
@@ -33,16 +34,28 @@ fun setBackgroundTintColor(view: View, colorId: Int) {
|
||||
getColorFromAttr(view.context, colorId)
|
||||
)
|
||||
}
|
||||
fun setBackgroundTintColor2(view: View, colorInt: Int) {
|
||||
view.backgroundTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
fun setDrawableTintColor(view: Button, colorId: Int) {
|
||||
view.compoundDrawableTintList = ColorStateList.valueOf(
|
||||
getColorFromAttr(view.context, colorId)
|
||||
)
|
||||
}
|
||||
fun setDrawableTintColor2(view: Button, colorInt: Int) {
|
||||
view.compoundDrawableTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
fun setImageTintColor2(view: ImageView, colorInt: Int) {
|
||||
view.imageTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
fun setTextTintColor(view: View, colorId: Int) {
|
||||
view.foregroundTintList = ColorStateList.valueOf(
|
||||
getColorFromAttr(view.context, colorId)
|
||||
)
|
||||
}
|
||||
fun setTextTintColor2(view: View, colorInt: Int) {
|
||||
view.foregroundTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
|
||||
fun refreshLayoutOf(view: View?) {
|
||||
if (view is ViewGroup) {
|
||||
|
||||
@@ -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="#FFFFFF"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="?semiTransparentColor"/>
|
||||
<stroke android:width="0.5dp" android:color="?semiTransparentColor"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_focused="true">
|
||||
<shape>
|
||||
<solid android:color="?semiTransparentColor"/>
|
||||
<stroke android:width="0.5dp" android:color="?semiTransparentColor"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<stroke android:width="0.5dp" android:color="?semiTransparentColor"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
5
app/src/main/res/drawable/ic_arrow_back.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_back.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_content_copy.xml
Normal file
5
app/src/main/res/drawable/ic_content_copy.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_content_cut.xml
Normal file
5
app/src/main/res/drawable/ic_content_cut.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M9.64,7.64c0.23,-0.5 0.36,-1.05 0.36,-1.64 0,-2.21 -1.79,-4 -4,-4S2,3.79 2,6s1.79,4 4,4c0.59,0 1.14,-0.13 1.64,-0.36L10,12l-2.36,2.36C7.14,14.13 6.59,14 6,14c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4c0,-0.59 -0.13,-1.14 -0.36,-1.64L12,14l7,7h3v-1L9.64,7.64zM6,8c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zM6,20c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zM12,12.5c-0.28,0 -0.5,-0.22 -0.5,-0.5s0.22,-0.5 0.5,-0.5 0.5,0.22 0.5,0.5 -0.22,0.5 -0.5,0.5zM19,3l-6,6 2,2 7,-7L22,3z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_content_paste.xml
Normal file
5
app/src/main/res/drawable/ic_content_paste.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_first_page.xml
Normal file
5
app/src/main/res/drawable/ic_first_page.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M18.41,16.59L13.82,12l4.59,-4.59L17,6l-6,6 6,6zM6,6h2v12H6z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_format_italic.xml
Normal file
5
app/src/main/res/drawable/ic_format_italic.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M10,4v3h2.21l-3.42,8H6v3h8v-3h-2.21l3.42,-8H18V4z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_last_page.xml
Normal file
5
app/src/main/res/drawable/ic_last_page.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M5.59,7.41L10.18,12l-4.59,4.59L7,18l6,-6 -6,-6zM16,6h2v12h-2z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_select_all.xml
Normal file
5
app/src/main/res/drawable/ic_select_all.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_spellcheck.xml
Normal file
5
app/src/main/res/drawable/ic_spellcheck.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12.45,16h2.09L9.43,3L7.57,3L2.46,16h2.09l1.12,-3h5.64l1.14,3zM6.43,11L8.5,5.48 10.57,11L6.43,11zM21.59,11.59l-8.09,8.09L9.83,16l-1.41,1.41 5.09,5.09L23,13l-1.41,-1.41z"/>
|
||||
</vector>
|
||||
@@ -2,16 +2,16 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape>
|
||||
<padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
|
||||
<solid android:color="?key_popup_extended_shadowColor" />
|
||||
<corners android:radius="@dimen/key_borderRadius" />
|
||||
<padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp"/>
|
||||
<solid android:color="#CDAFAFAF"/>
|
||||
<corners android:radius="@dimen/key_borderRadius"/>
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?key_popup_bgColor" />
|
||||
<corners android:radius="@dimen/key_borderRadius" />
|
||||
<solid android:color="@android:color/white"/>
|
||||
<corners android:radius="@dimen/key_borderRadius"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
||||
16
app/src/main/res/layout/advanced_activity.xml
Normal file
16
app/src/main/res/layout/advanced_activity.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/toolbar"/>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/advanced_fragment_frame"
|
||||
android:name="dev.patrickgold.florisboard.settings.fragments.AdvancedFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
138
app/src/main/res/layout/editing_layout.xml
Normal file
138
app/src/main/res/layout/editing_layout.xml
Normal file
@@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<dev.patrickgold.florisboard.ime.text.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"
|
||||
android:background="@android:color/white"
|
||||
android:backgroundTintMode="multiply">
|
||||
|
||||
<dev.patrickgold.florisboard.ime.text.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.text.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.text.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.text.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.text.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.text.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.text.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.text.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.text.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.text.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.text.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.text.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.text.editing.EditingKeyboardView>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<dev.patrickgold.florisboard.ime.core.InputView
|
||||
<dev.patrickgold.florisboard.ime.core.InputWindowView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/florisboard"
|
||||
android:layout_width="match_parent"
|
||||
@@ -8,13 +8,13 @@
|
||||
android:gravity="bottom"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<dev.patrickgold.florisboard.ime.core.InputView
|
||||
android:id="@+id/inner_input_view_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="horizontal"
|
||||
android:background="?keyboard_bgColor"
|
||||
android:background="?inputView_bgColorFallback"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<LinearLayout
|
||||
@@ -25,32 +25,34 @@
|
||||
<ImageButton
|
||||
android:id="@+id/one_handed_ctrl_close_start"
|
||||
style="@style/OneHandedPanelButton"
|
||||
android:src="@drawable/ic_zoom_out_map"/>
|
||||
android:src="@drawable/ic_zoom_out_map"
|
||||
android:contentDescription="@string/one_handed__close_btn_content_description"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/one_handed_button_height"
|
||||
android:visibility="invisible" />
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/one_handed_ctrl_move_start"
|
||||
style="@style/OneHandedPanelButton"
|
||||
android:src="@drawable/ic_keyboard_arrow_left"/>
|
||||
android:src="@drawable/ic_keyboard_arrow_left"
|
||||
android:contentDescription="@string/one_handed__move_start_btn_content_description"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ViewFlipper
|
||||
<dev.patrickgold.florisboard.ime.core.FlorisViewFlipper
|
||||
android:id="@+id/main_view_flipper"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:measureAllChildren="false">
|
||||
|
||||
<include layout="@layout/text_input_layout" />
|
||||
<include layout="@layout/text_input_layout"/>
|
||||
|
||||
<include layout="@layout/media_input_layout" />
|
||||
<include layout="@layout/media_input_layout"/>
|
||||
|
||||
</ViewFlipper>
|
||||
</dev.patrickgold.florisboard.ime.core.FlorisViewFlipper>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/one_handed_ctrl_panel_end"
|
||||
@@ -60,20 +62,22 @@
|
||||
<ImageButton
|
||||
android:id="@+id/one_handed_ctrl_close_end"
|
||||
style="@style/OneHandedPanelButton"
|
||||
android:src="@drawable/ic_zoom_out_map"/>
|
||||
android:src="@drawable/ic_zoom_out_map"
|
||||
android:contentDescription="@string/one_handed__close_btn_content_description"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/one_handed_button_height"
|
||||
android:visibility="invisible" />
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/one_handed_ctrl_move_end"
|
||||
style="@style/OneHandedPanelButton"
|
||||
android:src="@drawable/ic_keyboard_arrow_right"/>
|
||||
android:src="@drawable/ic_keyboard_arrow_right"
|
||||
android:contentDescription="@string/one_handed__move_end_btn_content_description"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</dev.patrickgold.florisboard.ime.core.InputView>
|
||||
|
||||
</dev.patrickgold.florisboard.ime.core.InputView>
|
||||
</dev.patrickgold.florisboard.ime.core.InputWindowView>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<dev.patrickgold.florisboard.ime.popup.KeyPopupView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/key_popup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/key_popup_bgshape">
|
||||
android:background="@drawable/key_popup_bgshape"
|
||||
android:backgroundTintMode="multiply">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/key_popup_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/key_height"
|
||||
android:gravity="center"
|
||||
android:textColor="?attr/key_popup_fgColor"
|
||||
android:textSize="@dimen/key_popup_textSize"/>
|
||||
|
||||
<ImageView
|
||||
@@ -23,7 +24,6 @@
|
||||
android:layout_gravity="end"
|
||||
android:padding="0dp"
|
||||
android:src="@drawable/ic_more_horiz"
|
||||
android:tint="?attr/key_popup_fgColor"
|
||||
android:visibility="visible" />
|
||||
android:visibility="visible"/>
|
||||
|
||||
</LinearLayout>
|
||||
</dev.patrickgold.florisboard.ime.popup.KeyPopupView>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
<dev.patrickgold.florisboard.ime.popup.KeyPopupExtendedView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/key_popup_extended"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/key_popup_bgshape"
|
||||
android:backgroundTintMode="multiply"
|
||||
android:padding="0dp"
|
||||
android:textSize="@dimen/key_popup_textSize"
|
||||
app:flexDirection="row"
|
||||
app:flexWrap="wrap"
|
||||
app:showDivider="none" />
|
||||
app:showDivider="none"/>
|
||||
|
||||
37
app/src/main/res/layout/list_item.xml
Normal file
37
app/src/main/res/layout/list_item.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<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:minHeight="?android:listPreferredItemHeightSmall"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
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="@android:id/summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:maxLines="4"
|
||||
tools:text="Summary"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,20 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<dev.patrickgold.florisboard.ime.media.MediaInputView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/media_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ViewFlipper
|
||||
<dev.patrickgold.florisboard.ime.core.FlorisViewFlipper
|
||||
android:id="@+id/media_input_view_flipper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:measureAllChildren="true"/>
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
android:measureAllChildren="false"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.0"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
@@ -87,4 +90,4 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</dev.patrickgold.florisboard.ime.media.MediaInputView>
|
||||
|
||||
22
app/src/main/res/layout/seek_bar_dialog.xml
Normal file
22
app/src/main/res/layout/seek_bar_dialog.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/seek_bar_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
tools:text="0%"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSeekBar
|
||||
android:id="@+id/seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="GESTURES | not yet implemented..."/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -50,4 +50,28 @@
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/localization_card"
|
||||
style="@style/SettingsCardView.Clickable">
|
||||
|
||||
<!-- TODO: create lang preview and make nice feature graphic for this card -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Localization prefs -->"/>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/theme_card"
|
||||
style="@style/SettingsCardView.Clickable">
|
||||
|
||||
<!-- TODO: create theme preview and make nice feature graphic for this card -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Theme prefs -->"/>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<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="64dp"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/caption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="normal"
|
||||
tools:text="Caption"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
style="@style/SettingsCardView"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/themeLinearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- KeyboardView preview will be inserted here programmatically -->
|
||||
|
||||
<!-- Must be RelativeLayout, because it blocks RecyclerView to create a second scrollbar -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/prefs_theme_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Must be RelativeLayout, because it blocks RecyclerView to create a second scrollbar -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/prefs_looknfeel_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -4,9 +4,11 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- KeyboardView preview will be inserted here programmatically -->
|
||||
|
||||
<!-- Must be RelativeLayout, because it blocks RecyclerView to create a second scrollbar -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/prefs_advanced_frame"
|
||||
android:id="@+id/prefs_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
@@ -12,20 +12,31 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:theme="@style/PreferenceThemeOverlay">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtype_heading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/settings__localization__title"
|
||||
android:textAppearance="?preferenceCategoryTitleTextAppearance"
|
||||
android:textColor="?colorAccent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtype_not_conf_warning"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_margin="8dp"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/shape_rect_rounded"
|
||||
android:backgroundTint="?colorWarning"
|
||||
android:text="@string/settings__keyboard__subtype_no_subtypes_configured_warning"
|
||||
android:text="@string/settings__localization__subtype_no_subtypes_configured_warning"
|
||||
android:textColor="?textColorWarning"/>
|
||||
|
||||
<LinearLayout
|
||||
@@ -40,7 +51,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textAllCaps="false"
|
||||
android:text="@string/settings__keyboard__subtype_add_title"
|
||||
android:text="@string/settings__localization__subtype_add_title"
|
||||
android:drawableStart="@drawable/ic_add"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableTint="?colorAccent"/>
|
||||
@@ -51,7 +62,7 @@
|
||||
|
||||
<!-- Must be RelativeLayout, because it blocks RecyclerView to create a second scrollbar -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/prefs_keyboard_frame"
|
||||
android:id="@+id/prefs_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorError"
|
||||
tools:text="@string/settings__keyboard__subtype_error_already_exists"
|
||||
tools:text="@string/settings__localization__subtype_error_already_exists"
|
||||
android:textColor="@color/textColorError"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:padding="8dp"
|
||||
@@ -27,7 +27,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings__keyboard__subtype_locale"
|
||||
android:text="@string/settings__localization__subtype_locale"
|
||||
android:paddingHorizontal="8dp"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
@@ -46,7 +46,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings__keyboard__subtype_layout"
|
||||
android:text="@string/settings__localization__subtype_layout"
|
||||
android:paddingHorizontal="8dp"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
@@ -4,129 +4,205 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/smartbar"
|
||||
android:layout_width="match_parent"
|
||||
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" />
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<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>
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/smartbar" />
|
||||
<include layout="@layout/smartbar"/>
|
||||
|
||||
<!-- KeyboardViews will be inserted in ViewFlipper below dynamically -->
|
||||
<ViewFlipper
|
||||
<dev.patrickgold.florisboard.ime.core.FlorisViewFlipper
|
||||
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>
|
||||
|
||||
</ViewFlipper>
|
||||
<include layout="@layout/editing_layout"/>
|
||||
|
||||
</dev.patrickgold.florisboard.ime.core.FlorisViewFlipper>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
29
app/src/main/res/layout/theme_selector_dialog.xml
Normal file
29
app/src/main/res/layout/theme_selector_dialog.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/settings__theme__preset_dialog_selected_theme"
|
||||
android:textColor="?android:textColorPrimary"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/settings__theme__preset_dialog_available_themes"
|
||||
android:textColor="?android:textColorPrimary"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
36
app/src/main/res/layout/theme_selector_list_item.xml
Normal file
36
app/src/main/res/layout/theme_selector_list_item.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
tools:text="Theme Name"
|
||||
android:textColor="?android:textColorSecondary"/>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:src="@drawable/ic_keyboard_arrow_right"
|
||||
android:contentDescription="@string/settings__theme__preset_dialog_alt_arrow_right"
|
||||
app:tint="?android:textColorSecondary"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -6,17 +6,24 @@
|
||||
android:icon="@drawable/ic_more_vert"
|
||||
android:title="@string/settings__menu"
|
||||
app:showAsAction="always">
|
||||
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/settings__menu_help"
|
||||
android:orderInCategory="1"
|
||||
android:title="@string/settings__menu_help" />
|
||||
android:title="@string/settings__menu_help"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__menu_advanced"
|
||||
android:orderInCategory="2"
|
||||
android:title="@string/settings__menu_advanced"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__menu_about"
|
||||
android:orderInCategory="2"
|
||||
android:title="@string/settings__menu_about" />
|
||||
android:orderInCategory="3"
|
||||
android:title="@string/settings__menu_about"/>
|
||||
</menu>
|
||||
|
||||
</item>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -4,26 +4,26 @@
|
||||
<item
|
||||
android:id="@+id/settings__navigation__home"
|
||||
android:icon="@drawable/ic_home"
|
||||
android:title="@string/settings__navigation__home" />
|
||||
android:title="@string/settings__navigation__home"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__keyboard"
|
||||
android:icon="@drawable/ic_keyboard"
|
||||
android:title="@string/settings__navigation__keyboard" />
|
||||
android:title="@string/settings__navigation__keyboard"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__looknfeel"
|
||||
android:id="@+id/settings__navigation__typing"
|
||||
android:icon="@drawable/ic_spellcheck"
|
||||
android:title="@string/settings__navigation__typing"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__theme"
|
||||
android:icon="@drawable/ic_palette"
|
||||
android:title="@string/settings__navigation__looknfeel" />
|
||||
android:title="@string/settings__navigation__theme"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__gestures"
|
||||
android:icon="@drawable/ic_gesture"
|
||||
android:title="@string/settings__navigation__gestures" />
|
||||
|
||||
<item
|
||||
android:id="@+id/settings__navigation__advanced"
|
||||
android:icon="@drawable/ic_more_vert"
|
||||
android:title="@string/settings__navigation__advanced" />
|
||||
android:title="@string/settings__navigation__gestures"/>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -32,26 +32,44 @@
|
||||
<string name="settings__menu_help">Aiuto & feedback</string>
|
||||
<string name="settings__navigation__home">Home</string>
|
||||
<string name="settings__navigation__keyboard">Tastiera</string>
|
||||
<string name="settings__navigation__looknfeel">Aspetto & funzionalità</string>
|
||||
<string name="settings__navigation__typing">Digitazione</string>
|
||||
<string name="settings__navigation__theme">Tema</string>
|
||||
<string name="settings__navigation__gestures">Gesti</string>
|
||||
<string name="settings__navigation__advanced">Avanzate</string>
|
||||
|
||||
<string name="settings__home__title">Benvenuto in %s</string>
|
||||
<string name="settings__home__ime_not_enabled">FlorisBoard non è abilitato nel sistema e quindi non sarà disponibile come metodo di immissione.Clicca quì per risolvere questo problema.</string>
|
||||
<string name="settings__home__ime_not_selected">FlorisBoard non è la tastiera predefinita. Clicca quì per risolvere questo problema.</string>
|
||||
<string name="settings__home__contribute">Grazie per aver provato FlorisBoard! Questo progetto è ancora in fase alfa e quindi manca di alcune funzionalità. Se trovate qualche bug o volete dare un suggerimento, date un\'occhiata al repo su GitHub e segnalate un problema. Questo aiuta a rendere FlorisBoard migliore. Grazie!</string>
|
||||
|
||||
<string name="settings__keyboard__title">Tastiera & Correzione del testo</string>
|
||||
<string name="settings__keyboard__subtype_no_subtypes_configured_warning">Sembra che tu non abbia configurato nessuno stile di input personalizzato. Come ripiego verrà utilizzato lo stile input English/QWERTY!</string>
|
||||
<string name="settings__keyboard__subtype_add">Aggiungi</string>
|
||||
<string name="settings__keyboard__subtype_add_title">Aggiungi stile input</string>
|
||||
<string name="settings__keyboard__subtype_apply">Applica</string>
|
||||
<string name="settings__keyboard__subtype_cancel">Annulla</string>
|
||||
<string name="settings__keyboard__subtype_delete">Elimina</string>
|
||||
<string name="settings__keyboard__subtype_edit_title">Modifica stile di input</string>
|
||||
<string name="settings__keyboard__subtype_locale">Locale</string>
|
||||
<string name="settings__keyboard__subtype_layout">Layout della tastiera</string>
|
||||
<string name="settings__keyboard__subtype_error_already_exists">Questo stile di input esiste già !</string>
|
||||
<string name="settings__localization__title">Lingue & Layout della tastiera</string>
|
||||
<string name="settings__localization__subtype_no_subtypes_configured_warning">Sembra che tu non abbia configurato nessuno stile di input personalizzato. Come ripiego verrà utilizzato lo stile input English/QWERTY!</string>
|
||||
<string name="settings__localization__subtype_add">Aggiungi</string>
|
||||
<string name="settings__localization__subtype_add_title">Aggiungi stile input</string>
|
||||
<string name="settings__localization__subtype_apply">Applica</string>
|
||||
<string name="settings__localization__subtype_cancel">Annulla</string>
|
||||
<string name="settings__localization__subtype_delete">Elimina</string>
|
||||
<string name="settings__localization__subtype_edit_title">Modifica stile di input</string>
|
||||
<string name="settings__localization__subtype_locale">Locale</string>
|
||||
<string name="settings__localization__subtype_layout">Layout della tastiera</string>
|
||||
<string name="settings__localization__subtype_error_already_exists">Questo stile di input esiste già !</string>
|
||||
|
||||
<string name="settings__theme__title">Tema tastiera</string>
|
||||
<string name="pref__theme__name__label">Tema tastiera</string>
|
||||
|
||||
<string name="settings__keyboard__title">Tastiera preferenze</string>
|
||||
<string name="pref__keyboard__group_layout__label">Layout</string>
|
||||
<string name="pref__keyboard__one_handed_mode__label">Modalità ad una mano</string>
|
||||
<string name="pref__keyboard__height_factor__label">Altezza tastiera</string>
|
||||
<string name="pref__keyboard__group_keypress__label">Pressione tasti</string>
|
||||
<string name="pref__keyboard__sound_enabled__label">Suono pressione tasti</string>
|
||||
<string name="pref__keyboard__sound_volume__label">Volume del suono alla pressione dei tasti</string>
|
||||
<string name="pref__keyboard__vibration_enabled__label">Vibrazione alla pressione dei tasti</string>
|
||||
<string name="pref__keyboard__vibration_strength__label">Intensità della vibrazione alla pressione dei tasti</string>
|
||||
<string name="pref__keyboard__popup_visible__label">Visibilità Popup</string>
|
||||
<string name="pref__keyboard__popup_visible__summary">Mostra popup quando si preme un tasto</string>
|
||||
<string name="pref__keyboard__long_press_delay__label">Ritardo lunga pressione tasti</string>
|
||||
|
||||
<string name="settings__typing__title">Esperienza di digitazione</string>
|
||||
<string name="pref__suggestion__title">Suggerimenti</string>
|
||||
<string name="pref__suggestion__enabled__label">Visualizza suggerimenti mentre digiti</string>
|
||||
<string name="pref__suggestion__enabled__summary">Verrà visualizzato nella parte superiore della tastiera</string>
|
||||
@@ -61,19 +79,7 @@
|
||||
<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="settings__looknfeel__title">Aspetto & funzionalità</string>
|
||||
<string name="pref__looknfeel__group_layout__label">Layout</string>
|
||||
<string name="pref__looknfeel__height_factor__label">Altezza tastiera</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__label">Modalità ad una mano</string>
|
||||
<string name="pref__looknfeel__group_keypress__label">Pressione tasti</string>
|
||||
<string name="pref__looknfeel__long_press_delay__label">Ritardo lunga pressione tasti</string>
|
||||
<string name="pref__looknfeel__sound_enabled__label">Suono pressione tasti</string>
|
||||
<string name="pref__looknfeel__sound_volume__label">Volume del suono alla pressione dei tasti</string>
|
||||
<string name="pref__looknfeel__vibration_enabled__label">Vibrazione alla pressione dei tasti</string>
|
||||
<string name="pref__looknfeel__vibration_strength__label">Intensità della vibrazione alla pressione dei tasti</string>
|
||||
<string name="pref__theme__name__label">Tema tastiera</string>
|
||||
|
||||
<string name="settings__gestures__title">Gesti</string>
|
||||
<string name="settings__gestures__title">Gesti & Digitazione a scorrimento</string>
|
||||
|
||||
<string name="settings__advanced__title">Avanzate</string>
|
||||
<string name="pref__advanced__settings_theme__label">Impostazioni tema</string>
|
||||
@@ -97,7 +103,7 @@
|
||||
<string name="setup__ok_button">OK</string>
|
||||
|
||||
<string name="setup__welcome__title">Benvenuto!</string>
|
||||
<string name="setup__welcome__intro">TGrazie per aver provato FlorisBoard! Prima che possiate iniziare ad usarlo, dobbiamo fare le solite cose e abilitarlo nelle impostazioni di sistema, impostare la vostra lingua/ il layout preferito, ecc... Ma non preoccuparti: segui questa procedura guidata </string>
|
||||
<string name="setup__welcome__intro">Grazie per aver provato FlorisBoard! Prima che possiate iniziare ad usarlo, dobbiamo fare le solite cose e abilitarlo nelle impostazioni di sistema, impostare la vostra lingua/ il layout preferito, ecc... Ma non preoccuparti: segui questa procedura guidata </string>
|
||||
<string name="setup__welcome__privacy">[[ TODO: inserisci quì la descrizione della privacy ]]</string>
|
||||
<string name="setup__welcome__trust">Il codice sorgente di FlorisBoard è accessibile pubblicamente a chiunque, quindi puoi facilmente rivedere cosa fa FlorisBoard in background. Controlla il link nel repository in basso.</string>
|
||||
<string name="setup__welcome__contribute">Un\'ultima cosa prima di iniziare l\'installazione - se riscontri errori / arresti anomali / problemi con FlorisBoard o hai una richiesta di funzionalità - vai al repository GitHub collegato di seguito e presenta un problema. Questo aiuta a migliorare l\'esperienza per tutti gli utenti!</string>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref__looknfeel__height_factor__entries">
|
||||
<item>@string/pref__looknfeel__height_factor__extra_short</item>
|
||||
<item>@string/pref__looknfeel__height_factor__short</item>
|
||||
<item>@string/pref__looknfeel__height_factor__mid_short</item>
|
||||
<item>@string/pref__looknfeel__height_factor__normal</item>
|
||||
<item>@string/pref__looknfeel__height_factor__mid_tall</item>
|
||||
<item>@string/pref__looknfeel__height_factor__tall</item>
|
||||
<item>@string/pref__looknfeel__height_factor__extra_tall</item>
|
||||
<string-array name="pref__keyboard__height_factor__entries">
|
||||
<item>@string/pref__keyboard__height_factor__extra_short</item>
|
||||
<item>@string/pref__keyboard__height_factor__short</item>
|
||||
<item>@string/pref__keyboard__height_factor__mid_short</item>
|
||||
<item>@string/pref__keyboard__height_factor__normal</item>
|
||||
<item>@string/pref__keyboard__height_factor__mid_tall</item>
|
||||
<item>@string/pref__keyboard__height_factor__tall</item>
|
||||
<item>@string/pref__keyboard__height_factor__extra_tall</item>
|
||||
</string-array>
|
||||
<string-array name="pref__looknfeel__height_factor__values">
|
||||
<string-array name="pref__keyboard__height_factor__values">
|
||||
<item>extra_short</item>
|
||||
<item>short</item>
|
||||
<item>mid_short</item>
|
||||
@@ -19,12 +19,12 @@
|
||||
<item>extra_tall</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__looknfeel__one_handed_mode__entries">
|
||||
<item>@string/pref__looknfeel__one_handed_mode__off</item>
|
||||
<item>@string/pref__looknfeel__one_handed_mode__right</item>
|
||||
<item>@string/pref__looknfeel__one_handed_mode__left</item>
|
||||
<string-array name="pref__keyboard__one_handed_mode__entries">
|
||||
<item>@string/pref__keyboard__one_handed_mode__off</item>
|
||||
<item>@string/pref__keyboard__one_handed_mode__right</item>
|
||||
<item>@string/pref__keyboard__one_handed_mode__left</item>
|
||||
</string-array>
|
||||
<string-array name="pref__looknfeel__one_handed_mode__values">
|
||||
<string-array name="pref__keyboard__one_handed_mode__values">
|
||||
<item>off</item>
|
||||
<item>end</item>
|
||||
<item>start</item>
|
||||
@@ -41,12 +41,12 @@
|
||||
<item>dark</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref__theme__name__entries">
|
||||
<item>Floris Light</item>
|
||||
<item>Floris Dark</item>
|
||||
<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__theme__name__values">
|
||||
<item>floris_light</item>
|
||||
<item>floris_dark</item>
|
||||
<string-array name="pref__suggestion__show_instead__values">
|
||||
<item>number_row</item>
|
||||
<item>clipboard_cursor_tools</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -1,39 +1,25 @@
|
||||
<?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="KeyboardTheme">
|
||||
<attr name="keyboardViewStyle" format="reference" />
|
||||
<attr name="keyboardRowViewStyle" format="reference" />
|
||||
<declare-styleable name="EditingKeyView">
|
||||
<attr name="android:text" format="string|reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
<attr name="semiTransparentColor" format="color" />
|
||||
<declare-styleable name="KeyboardThemeBase">
|
||||
<attr name="keyboardViewStyle" format="reference"/>
|
||||
<attr name="keyboardRowViewStyle" format="reference"/>
|
||||
|
||||
<attr name="key_bgColor" format="color" />
|
||||
<attr name="key_bgColorPressed" format="color" />
|
||||
<attr name="key_fgColor" format="color" />
|
||||
<attr name="key_enter_fgColor" format="color" />
|
||||
|
||||
<attr name="key_popup_bgColor" format="color" />
|
||||
<attr name="key_popup_fgColor" format="color" />
|
||||
|
||||
<attr name="key_popup_extended_bgColor" format="color" />
|
||||
<attr name="key_popup_extended_bgColorActive" format="color" />
|
||||
<attr name="key_popup_extended_shadowColor" format="color" />
|
||||
|
||||
<attr name="keyboard_bgColor" format="color" />
|
||||
|
||||
<attr name="emoji_key_bgColor" format="color" />
|
||||
<attr name="emoji_key_bgColorPressed" format="color" />
|
||||
<attr name="emoji_key_fgColor" format="color" />
|
||||
|
||||
<attr name="one_handed_bgColor" format="color" />
|
||||
<attr name="one_handed_button_fgColor" format="color" />
|
||||
|
||||
<attr name="smartbar_bgColor" format="color" />
|
||||
<attr name="smartbar_fgColor" format="color" />
|
||||
<attr name="smartbar_button_bgColor" format="color" />
|
||||
<attr name="smartbar_button_bgColorPressed" format="color" />
|
||||
<attr name="smartbar_button_fgColor" format="color" />
|
||||
<attr name="smartbar_candidate_fgColor" format="color" />
|
||||
<attr name="semiTransparentColor" format="color"/>
|
||||
<attr name="inputView_bgColorFallback" format="color"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SettingsTheme">
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="inputView_baseHeight">248dp</dimen>
|
||||
<dimen name="smartbar_baseHeight">40dp</dimen>
|
||||
<dimen name="textKeyboardView_baseHeight">208dp</dimen>
|
||||
<dimen name="mediaKeyboardView_baseHeight">@dimen/inputView_baseHeight</dimen>
|
||||
|
||||
<dimen name="key_width">33dp</dimen>
|
||||
<dimen name="key_height">42dp</dimen>
|
||||
<dimen name="emoji_key_width">@dimen/key_height</dimen>
|
||||
@@ -8,6 +13,7 @@
|
||||
<dimen name="key_marginH">2dp</dimen>
|
||||
<dimen name="key_marginV">5dp</dimen>
|
||||
<dimen name="keyboard_row_marginH">@dimen/key_marginH</dimen>
|
||||
<dimen name="keyboard_preview_margin">16dp</dimen>
|
||||
|
||||
<dimen name="key_borderRadius">6dp</dimen>
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
<string name="key__phone_wait">Wait</string>
|
||||
<string name="key_popup__threedots_alt">Three-dot icon. If visible, indicates that more letters can be used if longer pressed.</string>
|
||||
|
||||
<!-- One-handed strings -->
|
||||
<string name="one_handed__close_btn_content_description">Close one-handed mode.</string>
|
||||
<string name="one_handed__move_start_btn_content_description">Move keyboard to the left.</string>
|
||||
<string name="one_handed__move_end_btn_content_description">Move keyboard to the right.</string>
|
||||
|
||||
<!-- Media strings -->
|
||||
<string name="media__tab__emojis">Emojis</string>
|
||||
<string name="media__tab__emoticons">Emoticons</string>
|
||||
@@ -21,20 +26,24 @@
|
||||
|
||||
<!-- 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 -->
|
||||
<string name="settings__title">Settings</string>
|
||||
<string name="settings__menu">More options</string>
|
||||
<string name="settings__menu_about">About</string>
|
||||
<string name="settings__menu_advanced">@string/settings__advanced__title</string>
|
||||
<string name="settings__menu_help">Help & feedback</string>
|
||||
<string name="settings__navigation__home">Home</string>
|
||||
<string name="settings__navigation__keyboard">Keyboard</string>
|
||||
<string name="settings__navigation__looknfeel">Look & feel</string>
|
||||
<string name="settings__navigation__typing">Typing</string>
|
||||
<string name="settings__navigation__theme">Theme</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>
|
||||
@@ -42,49 +51,92 @@
|
||||
<string name="settings__home__ime_not_selected">FlorisBoard is not selected as the default input method. Click here to resolve this issue.</string>
|
||||
<string name="settings__home__contribute">Thanks for trying out FlorisBoard! This project is still in alpha and therefore missing features. If you find any bugs or want to make a suggestion, please check out the repo on GitHub and file an issue. This helps making FlorisBoard better. Thank you!</string>
|
||||
|
||||
<string name="settings__keyboard__title">Keyboard & Text Correction</string>
|
||||
<string name="settings__keyboard__subtype_no_subtypes_configured_warning">It seems that you haven\'t configured any subtypes. As a fallback the subtype English/QWERTY will be used!</string>
|
||||
<string name="settings__keyboard__subtype_add">Add</string>
|
||||
<string name="settings__keyboard__subtype_add_title">Add subtype</string>
|
||||
<string name="settings__keyboard__subtype_apply">Apply</string>
|
||||
<string name="settings__keyboard__subtype_cancel">Cancel</string>
|
||||
<string name="settings__keyboard__subtype_delete">Delete</string>
|
||||
<string name="settings__keyboard__subtype_edit_title">Edit subtype</string>
|
||||
<string name="settings__keyboard__subtype_locale">Locale</string>
|
||||
<string name="settings__keyboard__subtype_layout">Keyboard layout</string>
|
||||
<string name="settings__keyboard__subtype_error_already_exists">This subtype already exists!</string>
|
||||
<string name="settings__localization__title">Languages & Keyboard layouts</string>
|
||||
<string name="settings__localization__subtype_no_subtypes_configured_warning">It seems that you haven\'t configured any subtypes. As a fallback the subtype English/QWERTY will be used!</string>
|
||||
<string name="settings__localization__subtype_add">Add</string>
|
||||
<string name="settings__localization__subtype_add_title">Add subtype</string>
|
||||
<string name="settings__localization__subtype_apply">Apply</string>
|
||||
<string name="settings__localization__subtype_cancel">Cancel</string>
|
||||
<string name="settings__localization__subtype_delete">Delete</string>
|
||||
<string name="settings__localization__subtype_edit_title">Edit subtype</string>
|
||||
<string name="settings__localization__subtype_locale">Locale</string>
|
||||
<string name="settings__localization__subtype_layout">Keyboard layout</string>
|
||||
<string name="settings__localization__subtype_error_already_exists">This subtype already exists!</string>
|
||||
|
||||
<string name="settings__theme__title">Keyboard theme</string>
|
||||
<string name="settings__theme__undefined">Undefined</string>
|
||||
<string name="settings__theme__preset_title">Theme</string>
|
||||
<string name="settings__theme__preset_summary">Custom (based on %s)</string>
|
||||
<string name="settings__theme__preset_dialog_selected_theme">Selected theme:</string>
|
||||
<string name="settings__theme__preset_dialog_available_themes">Available themes:</string>
|
||||
<string name="settings__theme__preset_dialog_alt_arrow_right">Arrow right</string>
|
||||
<string name="settings__theme__background">Background color</string>
|
||||
<string name="settings__theme__background_active">Background color when active</string>
|
||||
<string name="settings__theme__background_pressed">Background color when pressed</string>
|
||||
<string name="settings__theme__foreground">Foreground color</string>
|
||||
<string name="settings__theme__foreground_alt">Foreground color (alternative)</string>
|
||||
<string name="settings__theme__foreground_capslock">Foreground color (caps lock)</string>
|
||||
<string name="settings__theme__dialog_title">Select a color</string>
|
||||
<string name="settings__theme__group_window">Window & System</string>
|
||||
<string name="settings__theme__group_keyboard">Keyboard</string>
|
||||
<string name="settings__theme__group_key">Key</string>
|
||||
<string name="settings__theme__group_key_enter">Enter key</string>
|
||||
<string name="settings__theme__group_key_popup">Key popup</string>
|
||||
<string name="settings__theme__group_key_shift">Shift key</string>
|
||||
<string name="settings__theme__group_media">Media context</string>
|
||||
<string name="settings__theme__group_one_handed">One-handed</string>
|
||||
<string name="settings__theme__group_one_handed_button">One-handed button</string>
|
||||
<string name="settings__theme__group_smartbar">Smartbar</string>
|
||||
<string name="settings__theme__group_smartbar_button">Smartbar button</string>
|
||||
<string name="pref__theme__name__label">Keyboard Theme</string>
|
||||
<string name="pref__theme__colorPrimary_title">Primary color</string>
|
||||
<string name="pref__theme__colorPrimary_summary">Applied to main media tab ripple and selection highlight</string>
|
||||
<string name="pref__theme__colorPrimaryDark_title">Primary color (dark)</string>
|
||||
<string name="pref__theme__colorPrimaryDark_summary">Currently not used, reserved for future implementation</string>
|
||||
<string name="pref__theme__colorAccent_title">Accent color</string>
|
||||
<string name="pref__theme__colorAccent_summary">Applied to emoji tab ripple</string>
|
||||
<string name="pref__theme__navBarColor_title">Navigation bar color</string>
|
||||
<string name="pref__theme__navBarColor_summary">The background of the navigation bar.</string>
|
||||
<string name="pref__theme__navBarIsLight_title">Navigation bar dark foreground</string>
|
||||
<string name="pref__theme__navBarIsLight_summary">Set to ON for dark or to OFF for light foreground.</string>
|
||||
|
||||
<string name="settings__keyboard__title">Keyboard Preferences</string>
|
||||
<string name="pref__keyboard__group_layout__label">Layout</string>
|
||||
<string name="pref__keyboard__one_handed_mode__label">One-handed mode</string>
|
||||
<string name="pref__keyboard__one_handed_mode__off">Off</string>
|
||||
<string name="pref__keyboard__one_handed_mode__right">Right-handed mode</string>
|
||||
<string name="pref__keyboard__one_handed_mode__left">Left-handed mode</string>
|
||||
<string name="pref__keyboard__height_factor__label">Keyboard height</string>
|
||||
<string name="pref__keyboard__height_factor__extra_short">Extra-short</string>
|
||||
<string name="pref__keyboard__height_factor__short">Short</string>
|
||||
<string name="pref__keyboard__height_factor__mid_short">Mid-short</string>
|
||||
<string name="pref__keyboard__height_factor__normal">Normal</string>
|
||||
<string name="pref__keyboard__height_factor__mid_tall">Mid-tall</string>
|
||||
<string name="pref__keyboard__height_factor__tall">Tall</string>
|
||||
<string name="pref__keyboard__height_factor__extra_tall">Extra-tall</string>
|
||||
<string name="pref__keyboard__group_keypress__label">Key press</string>
|
||||
<string name="pref__keyboard__sound_enabled__label">Sound on key press</string>
|
||||
<string name="pref__keyboard__sound_volume__label">Sound volume on key press</string>
|
||||
<string name="pref__keyboard__vibration_enabled__label">Vibrate on key press</string>
|
||||
<string name="pref__keyboard__vibration_strength__label">Vibration strength on key press</string>
|
||||
<string name="pref__keyboard__popup_visible__label">PopUp Visibility</string>
|
||||
<string name="pref__keyboard__popup_visible__summary">Show popup when you press a key</string>
|
||||
<string name="pref__keyboard__long_press_delay__label">Long key press delay</string>
|
||||
|
||||
<string name="settings__typing__title">Typing experience</string>
|
||||
<string name="pref__suggestion__title">Suggestions</string>
|
||||
<string name="pref__suggestion__enabled__label">Display suggestions while you type</string>
|
||||
<string name="pref__suggestion__enabled__label">[NYI] 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__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="settings__looknfeel__title">Look & feel</string>
|
||||
<string name="pref__looknfeel__group_layout__label">Layout</string>
|
||||
<string name="pref__looknfeel__height_factor__label">Keyboard height</string>
|
||||
<string name="pref__looknfeel__height_factor__extra_short">Extra-short</string>
|
||||
<string name="pref__looknfeel__height_factor__short">Short</string>
|
||||
<string name="pref__looknfeel__height_factor__mid_short">Mid-short</string>
|
||||
<string name="pref__looknfeel__height_factor__normal">Normal</string>
|
||||
<string name="pref__looknfeel__height_factor__mid_tall">Mid-tall</string>
|
||||
<string name="pref__looknfeel__height_factor__tall">Tall</string>
|
||||
<string name="pref__looknfeel__height_factor__extra_tall">Extra-tall</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__label">One-handed mode</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__off">Off</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__right">Right-handed mode</string>
|
||||
<string name="pref__looknfeel__one_handed_mode__left">Left-handed mode</string>
|
||||
<string name="pref__looknfeel__group_keypress__label">Key press</string>
|
||||
<string name="pref__looknfeel__long_press_delay__label">Long key press delay</string>
|
||||
<string name="pref__looknfeel__sound_enabled__label">Sound on key press</string>
|
||||
<string name="pref__looknfeel__sound_volume__label">Sound volume on key press</string>
|
||||
<string name="pref__looknfeel__vibration_enabled__label">Vibrate on key press</string>
|
||||
<string name="pref__looknfeel__vibration_strength__label">Vibration strength on key press</string>
|
||||
<string name="pref__theme__name__label">Keyboard Theme</string>
|
||||
|
||||
<string name="settings__gestures__title">Gestures</string>
|
||||
<string name="settings__gestures__title">Gestures & Glide typing</string>
|
||||
|
||||
<string name="settings__advanced__title">Advanced</string>
|
||||
<string name="pref__advanced__settings_theme__label">Settings theme</string>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<string name="key__view_symbols" translatable="false">\?123</string>
|
||||
<string name="key__view_symbols2" translatable="false">=\\<</string>
|
||||
<string name="key__view_half_space" translatable="false">↲</string>
|
||||
<string name="key__view_keshida" translatable="false">"یــــ"</string>
|
||||
|
||||
<!-- Media strings -->
|
||||
<string name="media__tab__emoticons_label" translatable="false">;-)</string>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<?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>
|
||||
<item name="android:layout_height">match_parent</item>
|
||||
<item name="android:layout_weight">0</item>
|
||||
<item name="android:layout_alignParentBottom">true</item>
|
||||
<item name="android:background">?one_handed_bgColor</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:orientation">vertical</item>
|
||||
</style>
|
||||
@@ -18,7 +16,7 @@
|
||||
<item name="android:autoMirrored">true</item>
|
||||
<item name="android:background">@drawable/button_transparent_bg_on_press</item>
|
||||
<item name="android:padding">0dp</item>
|
||||
<item name="android:tint">?one_handed_button_fgColor</item>
|
||||
<item name="android:tint">#000000</item>
|
||||
</style>
|
||||
|
||||
<style name="SmartbarCandidate">
|
||||
@@ -54,10 +52,10 @@
|
||||
<item name="android:layout_height">match_parent</item>
|
||||
<item name="android:layout_margin">@dimen/smartbar_button_margin</item>
|
||||
<item name="android:background">@drawable/shape_oval</item>
|
||||
<item name="android:backgroundTint">?smartbar_button_bgColor</item>
|
||||
<item name="android:backgroundTint">#FFFFFF</item>
|
||||
<item name="android:padding">@dimen/smartbar_button_padding</item>
|
||||
<item name="android:scaleType">fitCenter</item>
|
||||
<item name="android:tint">?smartbar_button_fgColor</item>
|
||||
<item name="android:tint">#000000</item>
|
||||
</style>
|
||||
|
||||
<style name="SmartbarQuickAction.Toggle">
|
||||
@@ -65,6 +63,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>
|
||||
|
||||
@@ -1,88 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<style name="KeyboardTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<style name="KeyboardThemeBase" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="KeyboardTheme.FlorisLight">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
||||
<item name="android:navigationBarColor" tools:targetApi="o_mr1">?keyboard_bgColor</item>
|
||||
<style name="KeyboardThemeBase.Day">
|
||||
<item name="android:navigationBarColor" tools:targetApi="o_mr1">#FFFFFF</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 -->
|
||||
|
||||
<item name="key_bgColor">#FFFFFF</item><!-- White -->
|
||||
<item name="key_bgColorPressed">#F5F5F5</item><!-- Gray 100 -->
|
||||
<item name="key_fgColor">?android:textColor</item>
|
||||
<item name="key_enter_fgColor">#FFFFFF</item><!-- White -->
|
||||
|
||||
<item name="key_popup_bgColor">#EEEEEE</item><!-- Gray 200 -->
|
||||
<item name="key_popup_fgColor">?android:textColor</item>
|
||||
|
||||
<item name="key_popup_extended_bgColor">@android:color/transparent</item>
|
||||
<item name="key_popup_extended_bgColorActive">#BDBDBD</item><!-- Gray 400 -->
|
||||
<item name="key_popup_extended_shadowColor">#CDCACACA</item>
|
||||
|
||||
<item name="keyboard_bgColor">#E0E0E0</item><!-- Gray 300 -->
|
||||
|
||||
<item name="emoji_key_bgColor">?keyboard_bgColor</item>
|
||||
<item name="emoji_key_bgColorPressed">?key_popup_extended_bgColorActive</item>
|
||||
<item name="emoji_key_fgColor">#757575</item><!-- Gray 600 -->
|
||||
|
||||
<item name="one_handed_bgColor">#E8F5E9</item><!-- Green 50 -->
|
||||
<item name="one_handed_button_fgColor">#424242</item><!-- Gray 800 -->
|
||||
|
||||
<item name="smartbar_bgColor">?keyboard_bgColor</item>
|
||||
<item name="smartbar_fgColor">?android:textColor</item>
|
||||
<item name="smartbar_button_bgColor">?key_bgColor</item>
|
||||
<item name="smartbar_button_bgColorPressed">?key_bgColorPressed</item>
|
||||
<item name="smartbar_button_fgColor">?key_fgColor</item>
|
||||
<item name="smartbar_candidate_fgColor">?android:textColor</item>
|
||||
<item name="inputView_bgColorFallback">#FFFFFF</item>
|
||||
</style>
|
||||
|
||||
<style name="KeyboardTheme.FlorisDark">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
||||
<item name="android:navigationBarColor" tools:targetApi="o_mr1">?keyboard_bgColor</item>
|
||||
<style name="KeyboardThemeBase.Night">
|
||||
<item name="android:navigationBarColor" tools:targetApi="o_mr1">#000000</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 -->
|
||||
|
||||
<item name="key_bgColor">#424242</item><!-- Gray 800 -->
|
||||
<item name="key_bgColorPressed">#616161</item><!-- Gray 700 -->
|
||||
<item name="key_fgColor">?android:textColor</item>
|
||||
<item name="key_enter_fgColor">#FFFFFF</item><!-- White -->
|
||||
|
||||
<item name="key_popup_bgColor">#757575</item><!-- Gray 600 -->
|
||||
<item name="key_popup_fgColor">?android:textColor</item>
|
||||
|
||||
<item name="key_popup_extended_bgColor">@android:color/transparent</item>
|
||||
<item name="key_popup_extended_bgColorActive">#BDBDBD</item><!-- Gray 400 -->
|
||||
<item name="key_popup_extended_shadowColor">#CD353535</item>
|
||||
|
||||
<item name="keyboard_bgColor">#212121</item><!-- Gray 900 -->
|
||||
|
||||
<item name="emoji_key_bgColor">?keyboard_bgColor</item>
|
||||
<item name="emoji_key_bgColorPressed">?key_popup_extended_bgColorActive</item>
|
||||
<item name="emoji_key_fgColor">#BDBDBD</item><!-- Gray 400 -->
|
||||
|
||||
<item name="one_handed_bgColor">#1B5E20</item><!-- Green 900 -->
|
||||
<item name="one_handed_button_fgColor">#EEEEEE</item><!-- Gray 200 -->
|
||||
|
||||
<item name="smartbar_bgColor">?keyboard_bgColor</item>
|
||||
<item name="smartbar_fgColor">?android:textColor</item>
|
||||
<item name="smartbar_button_bgColor">?key_bgColor</item>
|
||||
<item name="smartbar_button_bgColorPressed">?key_bgColorPressed</item>
|
||||
<item name="smartbar_button_fgColor">?key_fgColor</item>
|
||||
<item name="smartbar_candidate_fgColor">?android:textColor</item>
|
||||
<item name="inputView_bgColorFallback">#000000</item>
|
||||
</style>
|
||||
|
||||
<style name="SettingsToolbarTheme" parent="ThemeOverlay.AppCompat.DayNight.ActionBar">
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
app:fragment="dev.patrickgold.florisboard.settings.SettingsMainActivity$KeyboardFragment"
|
||||
app:key="pref__keyboard"
|
||||
app:icon="@drawable/ic_keyboard"
|
||||
app:title="@string/settings__keyboard__title" />
|
||||
|
||||
<Preference
|
||||
app:fragment="dev.patrickgold.florisboard.settings.SettingsMainActivity$AdvancedFragment"
|
||||
app:key="pref__advanced"
|
||||
app:icon="@drawable/ic_more_horiz"
|
||||
app:title="@string/settings__advanced__title" />
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -9,13 +9,13 @@
|
||||
app:key="advanced__settings_theme"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__advanced__settings_theme__label"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:key="advanced__show_app_icon"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__advanced__show_app_icon__label"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
9
app/src/main/res/xml/prefs_gestures.xml
Normal file
9
app/src/main/res/xml/prefs_gestures.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
app:title="Not yet implemented..."
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -4,35 +4,89 @@
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__suggestion__title">
|
||||
app:title="@string/pref__keyboard__group_layout__label">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:key="suggestion__enabled"
|
||||
<ListPreference
|
||||
android:defaultValue="off"
|
||||
app:entries="@array/pref__keyboard__one_handed_mode__entries"
|
||||
app:entryValues="@array/pref__keyboard__one_handed_mode__values"
|
||||
app:key="keyboard__one_handed_mode"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__suggestion__enabled__label"
|
||||
app:summary="@string/pref__suggestion__enabled__summary"/>
|
||||
app:title="@string/pref__keyboard__one_handed_mode__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:dependency="suggestion__enabled"
|
||||
app:key="suggestion__use_prev_words"
|
||||
<ListPreference
|
||||
android:defaultValue="normal"
|
||||
app:entries="@array/pref__keyboard__height_factor__entries"
|
||||
app:entryValues="@array/pref__keyboard__height_factor__values"
|
||||
app:key="keyboard__height_factor"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__suggestion__use_pref_words__label"
|
||||
app:summary="@string/pref__suggestion__use_pref_words__summary"/>
|
||||
app:title="@string/pref__keyboard__height_factor__label"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__correction__title">
|
||||
app:title="@string/pref__keyboard__group_keypress__label">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:key="correction__double_space_period"
|
||||
android:key="keyboard__sound_enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__correction__double_space_period__label"
|
||||
app:summary="@string/pref__correction__double_space_period__summary"/>
|
||||
app:title="@string/pref__keyboard__sound_enabled__label"/>
|
||||
|
||||
<dev.patrickgold.florisboard.settings.components.DialogSeekBarPreference
|
||||
app:allowDividerAbove="false"
|
||||
android:defaultValue="-1"
|
||||
app:systemDefaultValue="-1"
|
||||
app:systemDefaultValueText="@string/settings__system_default"
|
||||
app:dependency="keyboard__sound_enabled"
|
||||
app:key="keyboard__sound_volume"
|
||||
app:min="0"
|
||||
app:max="100"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__sound_volume__label"
|
||||
app:seekBarIncrement="1"
|
||||
app:unit="%"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="keyboard__vibration_enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__vibration_enabled__label"/>
|
||||
|
||||
<dev.patrickgold.florisboard.settings.components.DialogSeekBarPreference
|
||||
app:allowDividerAbove="false"
|
||||
android:defaultValue="-1"
|
||||
app:systemDefaultValue="-1"
|
||||
app:systemDefaultValueText="@string/settings__system_default"
|
||||
app:dependency="keyboard__vibration_enabled"
|
||||
app:key="keyboard__vibration_strength"
|
||||
app:min="0"
|
||||
app:max="100"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__vibration_strength__label"
|
||||
app:seekBarIncrement="1"
|
||||
app:unit="%"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:key="keyboard__popup_enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__popup_visible__label"
|
||||
app:summary="@string/pref__keyboard__popup_visible__summary"/>
|
||||
|
||||
<dev.patrickgold.florisboard.settings.components.DialogSeekBarPreference
|
||||
app:allowDividerAbove="false"
|
||||
android:defaultValue="300"
|
||||
app:key="keyboard__long_press_delay"
|
||||
app:min="100"
|
||||
app:max="700"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__keyboard__long_press_delay__label"
|
||||
app:seekBarIncrement="10"
|
||||
app:unit=" ms"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__group_layout__label">
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="off"
|
||||
app:entries="@array/pref__looknfeel__one_handed_mode__entries"
|
||||
app:entryValues="@array/pref__looknfeel__one_handed_mode__values"
|
||||
app:key="looknfeel__one_handed_mode"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__one_handed_mode__label"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="normal"
|
||||
app:entries="@array/pref__looknfeel__height_factor__entries"
|
||||
app:entryValues="@array/pref__looknfeel__height_factor__values"
|
||||
app:key="looknfeel__height_factor"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__height_factor__label"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__group_keypress__label">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="looknfeel__sound_enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__sound_enabled__label" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
app:dependency="looknfeel__sound_enabled"
|
||||
app:key="looknfeel__sound_volume"
|
||||
app:min="0"
|
||||
android:max="100"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__sound_volume__label"
|
||||
app:seekBarIncrement="1"
|
||||
app:showSeekBarValue="true"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="looknfeel__vibration_enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__vibration_enabled__label" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
app:dependency="looknfeel__vibration_enabled"
|
||||
app:key="looknfeel__vibration_strength"
|
||||
app:min="0"
|
||||
android:max="100"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__vibration_strength__label"
|
||||
app:seekBarIncrement="1"
|
||||
app:showSeekBarValue="true"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="300"
|
||||
app:key="looknfeel__long_press_delay"
|
||||
app:min="100"
|
||||
android:max="700"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__looknfeel__long_press_delay__label"
|
||||
app:seekBarIncrement="10"
|
||||
app:showSeekBarValue="true"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -1,14 +1,225 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="floris_light"
|
||||
app:entries="@array/pref__theme__name__entries"
|
||||
app:entryValues="@array/pref__theme__name__values"
|
||||
app:key="theme__name"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__theme__name__label"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<dev.patrickgold.florisboard.settings.components.ThemePresetSelectorPreference
|
||||
app:key="theme__preset"
|
||||
app:title="@string/settings__theme__preset_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_window"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
app:key="theme__colorPrimary"
|
||||
app:title="@string/pref__theme__colorPrimary_title"
|
||||
app:summary="@string/pref__theme__colorPrimary_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
app:key="theme__colorPrimaryDark"
|
||||
app:title="@string/pref__theme__colorPrimaryDark_title"
|
||||
app:summary="@string/pref__theme__colorPrimaryDark_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
app:key="theme__colorAccent"
|
||||
app:title="@string/pref__theme__colorAccent_title"
|
||||
app:summary="@string/pref__theme__colorAccent_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="theme__navBarIsLight"
|
||||
app:title="@string/pref__theme__navBarIsLight_title"
|
||||
app:summary="@string/pref__theme__navBarIsLight_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__navBarColor"
|
||||
android:title="@string/pref__theme__navBarColor_title"
|
||||
android:summary="@string/pref__theme__navBarColor_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_keyboard"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyboard_bgColor"
|
||||
android:title="@string/settings__theme__background"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_key"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__key_bgColor"
|
||||
android:title="@string/settings__theme__background"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__key_bgColorPressed"
|
||||
android:title="@string/settings__theme__background_pressed"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__key_fgColor"
|
||||
android:title="@string/settings__theme__foreground"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_key_enter"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyEnter_bgColor"
|
||||
android:title="@string/settings__theme__background"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyEnter_bgColorPressed"
|
||||
android:title="@string/settings__theme__background_pressed"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyEnter_fgColor"
|
||||
android:title="@string/settings__theme__foreground"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_key_shift"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyShift_bgColor"
|
||||
android:title="@string/settings__theme__background"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyShift_bgColorPressed"
|
||||
android:title="@string/settings__theme__background_pressed"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyShift_fgColor"
|
||||
android:title="@string/settings__theme__foreground"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyShift_fgColorCapsLock"
|
||||
android:title="@string/settings__theme__foreground_capslock"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_key_popup"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyPopup_bgColor"
|
||||
android:title="@string/settings__theme__background"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyPopup_bgColorActive"
|
||||
android:title="@string/settings__theme__background_active"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__keyPopup_fgColor"
|
||||
android:title="@string/settings__theme__foreground"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_media"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__media_fgColor"
|
||||
android:title="@string/settings__theme__foreground"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__media_fgColorAlt"
|
||||
android:title="@string/settings__theme__foreground_alt"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_one_handed"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__oneHanded_bgColor"
|
||||
android:title="@string/settings__theme__background"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_one_handed_button"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__oneHandedButton_fgColor"
|
||||
android:title="@string/settings__theme__foreground"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_smartbar"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__smartbar_bgColor"
|
||||
android:title="@string/settings__theme__background"
|
||||
app:iconSpaceReserved="false"
|
||||
app:cpv_showAlphaSlider="true"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__smartbar_fgColor"
|
||||
android:title="@string/settings__theme__foreground"
|
||||
app:iconSpaceReserved="false"
|
||||
app:cpv_showAlphaSlider="true"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__smartbar_fgColorAlt"
|
||||
android:title="@string/settings__theme__foreground_alt"
|
||||
app:iconSpaceReserved="false"
|
||||
app:cpv_showAlphaSlider="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings__theme__group_smartbar_button"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__smartbarButton_bgColor"
|
||||
android:title="@string/settings__theme__background"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
||||
android:key="theme__smartbarButton_fgColor"
|
||||
android:title="@string/settings__theme__foreground"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
48
app/src/main/res/xml/prefs_typing.xml
Normal file
48
app/src/main/res/xml/prefs_typing.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__suggestion__title">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
app:key="suggestion__enabled"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__suggestion__enabled__label"
|
||||
app:summary="@string/pref__suggestion__enabled__summary"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="clipboard_cursor_tools"
|
||||
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"
|
||||
app:key="suggestion__use_prev_words"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__suggestion__use_pref_words__label"
|
||||
app:summary="@string/pref__suggestion__use_pref_words__summary"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__correction__title">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
app:key="correction__double_space_period"
|
||||
app:iconSpaceReserved="false"
|
||||
app:title="@string/pref__correction__double_space_period__label"
|
||||
app:summary="@string/pref__correction__double_space_period__summary"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -36,7 +36,7 @@ class SubtypeManagerTest {
|
||||
|
||||
@Test
|
||||
fun testSubtypesGetter() {
|
||||
doReturn("1/de-DE/qwertz;23/fr-CH/swiss_french").`when`(prefs.keyboard).subtypes
|
||||
doReturn("1/de-DE/qwertz;23/fr-CH/swiss_french").`when`(prefs.localization).subtypes
|
||||
assertThat(subtypeManagerUnderTest.subtypes, `is`(listOf(
|
||||
Subtype(1, Locale("de", "DE"), "qwertz"),
|
||||
Subtype(23, Locale("fr", "CH"), "swiss_french")
|
||||
@@ -49,38 +49,38 @@ class SubtypeManagerTest {
|
||||
Subtype(1, Locale("de", "DE"), "qwertz"),
|
||||
Subtype(23, Locale("fr", "CH"), "swiss_french")
|
||||
)
|
||||
verify(prefs.keyboard).subtypes = "1/de-DE/qwertz;23/fr-CH/swiss_french"
|
||||
verify(prefs.localization).subtypes = "1/de-DE/qwertz;23/fr-CH/swiss_french"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddSubtype() {
|
||||
doReturn("1/de-DE/qwertz;23/fr-CH/swiss_french").`when`(prefs.keyboard).subtypes
|
||||
doReturn("1/de-DE/qwertz;23/fr-CH/swiss_french").`when`(prefs.localization).subtypes
|
||||
|
||||
// First test adding a non-existing subtype to the list.
|
||||
val locale = Locale("pt", "PT")
|
||||
val layout = "qwerty"
|
||||
val id = locale.hashCode() + layout.hashCode()
|
||||
subtypeManagerUnderTest.addSubtype(locale, layout)
|
||||
verify(prefs.keyboard).subtypes = "1/de-DE/qwertz;23/fr-CH/swiss_french;$id/pt-PT/qwerty"
|
||||
verify(prefs.localization).subtypes = "1/de-DE/qwertz;23/fr-CH/swiss_french;$id/pt-PT/qwerty"
|
||||
|
||||
// Now test to add the same subtype twice. It should work fine for the first call but should fail
|
||||
// on the second call.
|
||||
val locale2 = Locale("pt", "PT")
|
||||
val layout2 = "qwerty"
|
||||
val id2 = locale.hashCode() + layout.hashCode()
|
||||
doReturn("").`when`(prefs.keyboard).subtypes
|
||||
doReturn("").`when`(prefs.localization).subtypes
|
||||
assertThat(subtypeManagerUnderTest.addSubtype(locale2, layout2), `is`(true))
|
||||
doReturn("$id2/pt-PT/qwerty").`when`(prefs.keyboard).subtypes
|
||||
doReturn("$id2/pt-PT/qwerty").`when`(prefs.localization).subtypes
|
||||
assertThat(subtypeManagerUnderTest.addSubtype(locale2, layout2), `is`(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetActiveSubtype() {
|
||||
doReturn("42/en-CA/qwerty;1/de-DE/qwertz;23/fr-CH/swiss_french")
|
||||
.`when`(prefs.keyboard).subtypes
|
||||
.`when`(prefs.localization).subtypes
|
||||
|
||||
// First test activeSubtypeId pointing to existing subtype in list
|
||||
doReturn(1).`when`(prefs.keyboard).activeSubtypeId
|
||||
doReturn(1).`when`(prefs.localization).activeSubtypeId
|
||||
assertThat(
|
||||
subtypeManagerUnderTest.getActiveSubtype(),
|
||||
`is`(Subtype(1, Locale("de", "DE"), "qwertz"))
|
||||
@@ -88,28 +88,28 @@ class SubtypeManagerTest {
|
||||
|
||||
// Now test that the first subtype in the list is returned if activeSubtypeId points to a
|
||||
// non-existent subtype and that activeSubtypeId value is updated accordingly.
|
||||
doReturn(99).`when`(prefs.keyboard).activeSubtypeId
|
||||
doReturn(99).`when`(prefs.localization).activeSubtypeId
|
||||
assertThat(
|
||||
subtypeManagerUnderTest.getActiveSubtype(),
|
||||
`is`(Subtype(42, Locale("en", "CA"), "qwerty"))
|
||||
)
|
||||
verify(prefs.keyboard).activeSubtypeId = 42
|
||||
verify(prefs.localization).activeSubtypeId = 42
|
||||
|
||||
// Now test that the subtype list is empty. null should be returned and activeSubtypeId
|
||||
// should be returned.
|
||||
doReturn("").`when`(prefs.keyboard).subtypes
|
||||
doReturn(99).`when`(prefs.keyboard).activeSubtypeId
|
||||
doReturn("").`when`(prefs.localization).subtypes
|
||||
doReturn(99).`when`(prefs.localization).activeSubtypeId
|
||||
assertThat(
|
||||
subtypeManagerUnderTest.getActiveSubtype(),
|
||||
`is`(nullValue())
|
||||
)
|
||||
verify(prefs.keyboard).activeSubtypeId = -1
|
||||
verify(prefs.localization).activeSubtypeId = -1
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSubtypeById() {
|
||||
doReturn("42/en-CA/qwerty;1/de-DE/qwertz;23/fr-CH/swiss_french")
|
||||
.`when`(prefs.keyboard).subtypes
|
||||
.`when`(prefs.localization).subtypes
|
||||
|
||||
// First test a existing subtype for given id.
|
||||
assertThat(
|
||||
@@ -127,67 +127,67 @@ class SubtypeManagerTest {
|
||||
@Test
|
||||
fun testModifySubtypeWithSameId() {
|
||||
doReturn("42/en-CA/qwerty;1/de-DE/qwertz;23/fr-CH/swiss_french")
|
||||
.`when`(prefs.keyboard).subtypes
|
||||
.`when`(prefs.localization).subtypes
|
||||
|
||||
// First test a subtype with id that does not exist in subtype list.
|
||||
subtypeManagerUnderTest.modifySubtypeWithSameId(
|
||||
Subtype(99, Locale("de", "AT"), "qwertz")
|
||||
)
|
||||
verify(prefs.keyboard).subtypes = "42/en-CA/qwerty;1/de-DE/qwertz;23/fr-CH/swiss_french"
|
||||
verify(prefs.localization).subtypes = "42/en-CA/qwerty;1/de-DE/qwertz;23/fr-CH/swiss_french"
|
||||
|
||||
// Now test a subtype with id that does exist in subtype list.
|
||||
subtypeManagerUnderTest.modifySubtypeWithSameId(
|
||||
Subtype(23, Locale("de", "AT"), "qwertz")
|
||||
)
|
||||
verify(prefs.keyboard).subtypes = "42/en-CA/qwerty;1/de-DE/qwertz;23/de-AT/qwertz"
|
||||
verify(prefs.localization).subtypes = "42/en-CA/qwerty;1/de-DE/qwertz;23/de-AT/qwertz"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveSubtype() {
|
||||
doReturn("42/en-CA/qwerty;1/de-DE/qwertz;23/fr-CH/swiss_french")
|
||||
.`when`(prefs.keyboard).subtypes
|
||||
.`when`(prefs.localization).subtypes
|
||||
|
||||
// First test that subtype is removed from list.
|
||||
subtypeManagerUnderTest.removeSubtype(
|
||||
Subtype(42, Locale("en", "CA"), "qwerty")
|
||||
)
|
||||
verify(prefs.keyboard).subtypes = "1/de-DE/qwertz;23/fr-CH/swiss_french"
|
||||
verify(prefs.localization).subtypes = "1/de-DE/qwertz;23/fr-CH/swiss_french"
|
||||
|
||||
// Now test that list stays untouched when attempting to remove non-existent subtype.
|
||||
subtypeManagerUnderTest.removeSubtype(
|
||||
Subtype(99, Locale("es", "ES"), "spanish")
|
||||
)
|
||||
verify(prefs.keyboard).subtypes = "42/en-CA/qwerty;1/de-DE/qwertz;23/fr-CH/swiss_french"
|
||||
verify(prefs.localization).subtypes = "42/en-CA/qwerty;1/de-DE/qwertz;23/fr-CH/swiss_french"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSwitchToNextSubtype() {
|
||||
doReturn("1/en-CA/qwerty;2/de-DE/qwertz;3/fr-CH/swiss_french")
|
||||
.`when`(prefs.keyboard).subtypes
|
||||
.`when`(prefs.localization).subtypes
|
||||
|
||||
// First test that next subtype is returned from existing id.
|
||||
doReturn(2).`when`(prefs.keyboard).activeSubtypeId
|
||||
doReturn(2).`when`(prefs.localization).activeSubtypeId
|
||||
assertThat(
|
||||
subtypeManagerUnderTest.switchToNextSubtype(),
|
||||
`is`(Subtype(3, Locale("fr", "CH"), "swiss_french"))
|
||||
)
|
||||
verify(prefs.keyboard).activeSubtypeId = 3
|
||||
verify(prefs.localization).activeSubtypeId = 3
|
||||
|
||||
// Now test that second subtype is selected when id points to non-existent.
|
||||
doReturn(99).`when`(prefs.keyboard).activeSubtypeId
|
||||
doReturn(99).`when`(prefs.localization).activeSubtypeId
|
||||
assertThat(
|
||||
subtypeManagerUnderTest.switchToNextSubtype(),
|
||||
`is`(Subtype(2, Locale("de", "DE"), "qwertz"))
|
||||
)
|
||||
verify(prefs.keyboard).activeSubtypeId = 2
|
||||
verify(prefs.localization).activeSubtypeId = 2
|
||||
|
||||
// Now test that null / -1 is used when list is empty.
|
||||
doReturn("").`when`(prefs.keyboard).subtypes
|
||||
doReturn(99).`when`(prefs.keyboard).activeSubtypeId
|
||||
doReturn("").`when`(prefs.localization).subtypes
|
||||
doReturn(99).`when`(prefs.localization).activeSubtypeId
|
||||
assertThat(
|
||||
subtypeManagerUnderTest.switchToNextSubtype(),
|
||||
`is`(nullValue())
|
||||
)
|
||||
verify(prefs.keyboard).activeSubtypeId = -1
|
||||
verify(prefs.localization).activeSubtypeId = -1
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user