mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-20 14:19:08 +00:00
parent
7dac19cec7
commit
74571a3202
3 changed files with 159 additions and 7 deletions
18
README.md
18
README.md
|
@ -14,7 +14,7 @@ Does not use internet permission, and thus is 100% offline.
|
||||||
- [Contributing](#contributing-)
|
- [Contributing](#contributing-)
|
||||||
* [Reporting Issues](#reporting-issues)
|
* [Reporting Issues](#reporting-issues)
|
||||||
* [Translations](#translations)
|
* [Translations](#translations)
|
||||||
* [Dictionary Creation](#dictionary-creation)
|
* [To Community Creation](#to-community)
|
||||||
* [Code Contribution](CONTRIBUTING.md)
|
* [Code Contribution](CONTRIBUTING.md)
|
||||||
- [To-do](#to-do)
|
- [To-do](#to-do)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
@ -113,9 +113,17 @@ If you're interested, you can read the following useful text about effective bug
|
||||||
Translations can be added using [Weblate](https://translate.codeberg.org/projects/heliboard/). You will need an account to update translations and add languages. Add the language you want to translate to in Languages -> Manage translated languages in the top menu bar.
|
Translations can be added using [Weblate](https://translate.codeberg.org/projects/heliboard/). You will need an account to update translations and add languages. Add the language you want to translate to in Languages -> Manage translated languages in the top menu bar.
|
||||||
Updating translations in a PR will not be accepted, as it may cause conflicts with Weblate translations.
|
Updating translations in a PR will not be accepted, as it may cause conflicts with Weblate translations.
|
||||||
|
|
||||||
## Dictionary Creation
|
## To Community
|
||||||
There will not be any further dictionaries bundled in this app. However, you can add dictionaries to the [dictionaries repository](https://codeberg.org/Helium314/aosp-dictionaries).
|
You can share your themes, layouts and dictionaries with other people:
|
||||||
To create or update a dictionary for your language, you can use [this tool](https://github.com/remi0s/aosp-dictionary-tools). You will need a wordlist, as described [here](https://codeberg.org/Helium314/aosp-dictionaries/src/branch/main/wordlists/sample.combined) and in the repository readme.
|
* Themes can be saved and loaded using the menu on top-right in the _adjust colors_ screen
|
||||||
|
* Custom keyboard layouts are text files whose content you can edit, copy and share
|
||||||
|
* this applies to main keyboard layouts and to special layouts adjustable in advanced settings
|
||||||
|
* see [layouts.md](layouts.md) for details
|
||||||
|
* Creating dictionaries is a little more work
|
||||||
|
* first you will need a wordlist, as described [here](https://codeberg.org/Helium314/aosp-dictionaries/src/branch/main/wordlists/sample.combined) and in the repository readme
|
||||||
|
* the you need to compile the dictionary using [external tools](https://github.com/remi0s/aosp-dictionary-tools)
|
||||||
|
* the resulting file (and ideally the wordlist too) can be shared with other users
|
||||||
|
* note that there will not be any further dictionaries added to this app, but you can add dictionaries to the [dictionaries repository](https://codeberg.org/Helium314/aosp-dictionaries)
|
||||||
|
|
||||||
## Code Contribution
|
## Code Contribution
|
||||||
See [Contribution Guidelines](CONTRIBUTING.md)
|
See [Contribution Guidelines](CONTRIBUTING.md)
|
||||||
|
@ -133,8 +141,6 @@ __Planned features and improvements:__
|
||||||
* Add and enable emoji dictionaries by default (if available for language)
|
* Add and enable emoji dictionaries by default (if available for language)
|
||||||
* Clearer / more intuitive arrangement of settings
|
* Clearer / more intuitive arrangement of settings
|
||||||
* Maybe hide some less used settings by default (similar to color customization)
|
* Maybe hide some less used settings by default (similar to color customization)
|
||||||
* Customizable currency keys
|
|
||||||
* Ability to export/import (share) custom colors
|
|
||||||
* Make use of the `.com` key in URL fields (currently only available for tablets)
|
* Make use of the `.com` key in URL fields (currently only available for tablets)
|
||||||
* With language-dependent TLDs
|
* With language-dependent TLDs
|
||||||
* Internal cleanup (a lot of over-complicated and convoluted code)
|
* Internal cleanup (a lot of over-complicated and convoluted code)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
package helium314.keyboard.latin.settings
|
package helium314.keyboard.latin.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
|
@ -9,7 +14,11 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
@ -17,6 +26,7 @@ import androidx.core.view.MenuProvider
|
||||||
import androidx.core.view.forEach
|
import androidx.core.view.forEach
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.rarepebble.colorpicker.ColorPickerView
|
import com.rarepebble.colorpicker.ColorPickerView
|
||||||
import helium314.keyboard.keyboard.KeyboardSwitcher
|
import helium314.keyboard.keyboard.KeyboardSwitcher
|
||||||
|
@ -25,12 +35,19 @@ import helium314.keyboard.latin.RichInputMethodManager
|
||||||
import helium314.keyboard.latin.common.ColorType
|
import helium314.keyboard.latin.common.ColorType
|
||||||
import helium314.keyboard.latin.common.default
|
import helium314.keyboard.latin.common.default
|
||||||
import helium314.keyboard.latin.common.readAllColorsMap
|
import helium314.keyboard.latin.common.readAllColorsMap
|
||||||
|
import helium314.keyboard.latin.common.splitOnWhitespace
|
||||||
import helium314.keyboard.latin.common.writeAllColorsMap
|
import helium314.keyboard.latin.common.writeAllColorsMap
|
||||||
import helium314.keyboard.latin.databinding.ColorSettingBinding
|
import helium314.keyboard.latin.databinding.ColorSettingBinding
|
||||||
import helium314.keyboard.latin.databinding.ColorSettingsBinding
|
import helium314.keyboard.latin.databinding.ColorSettingsBinding
|
||||||
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
||||||
import helium314.keyboard.latin.utils.ExecutorUtils
|
import helium314.keyboard.latin.utils.ExecutorUtils
|
||||||
import helium314.keyboard.latin.utils.ResourceUtils
|
import helium314.keyboard.latin.utils.ResourceUtils
|
||||||
|
import helium314.keyboard.latin.utils.infoDialog
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.SerializationException
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.util.EnumMap
|
||||||
|
|
||||||
open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvider {
|
open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvider {
|
||||||
|
|
||||||
|
@ -96,6 +113,8 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi
|
||||||
menu.add(Menu.NONE, 0, Menu.NONE, R.string.main_colors)
|
menu.add(Menu.NONE, 0, Menu.NONE, R.string.main_colors)
|
||||||
menu.add(Menu.NONE, 1, Menu.NONE, R.string.more_colors)
|
menu.add(Menu.NONE, 1, Menu.NONE, R.string.more_colors)
|
||||||
menu.add(Menu.NONE, 2, Menu.NONE, R.string.all_colors)
|
menu.add(Menu.NONE, 2, Menu.NONE, R.string.all_colors)
|
||||||
|
menu.add(Menu.NONE, 3, Menu.NONE, R.string.save)
|
||||||
|
menu.add(Menu.NONE, 4, Menu.NONE, R.string.load)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||||
|
@ -111,6 +130,14 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi
|
||||||
updateColorPrefs()
|
updateColorPrefs()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if (menuItem.itemId == 3) {
|
||||||
|
saveDialog()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (menuItem.itemId == 4) {
|
||||||
|
loadDialog()
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +325,117 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi
|
||||||
companion object {
|
companion object {
|
||||||
var forceOppositeTheme = false
|
var forceOppositeTheme = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------- stuff for import / export ---------------------------
|
||||||
|
@Serializable
|
||||||
|
private data class SaveThoseColors(val moreColors: Int, val colors: Map<String, Pair<Int?, Boolean>>)
|
||||||
|
|
||||||
|
private fun saveDialog() {
|
||||||
|
AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.save)
|
||||||
|
.setPositiveButton(R.string.button_save_file) { _, _ ->
|
||||||
|
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.putExtra(Intent.EXTRA_TITLE,"theme.json")
|
||||||
|
.setType("application/json")
|
||||||
|
saveFilePicker.launch(intent)
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setNeutralButton(R.string.copy_to_clipboard) { _, _ ->
|
||||||
|
val cm = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
cm.setPrimaryClip(ClipData.newPlainText("HeliBoard theme", getColorString()))
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadDialog() {
|
||||||
|
val layout = LinearLayout(requireContext())
|
||||||
|
layout.orientation = LinearLayout.VERTICAL
|
||||||
|
layout.addView(TextView(requireContext()).apply { setText(R.string.load_will_overwrite) })
|
||||||
|
val et = EditText(requireContext())
|
||||||
|
layout.addView(et)
|
||||||
|
val padding = ResourceUtils.toPx(8, resources)
|
||||||
|
layout.setPadding(3 * padding, padding, padding, padding)
|
||||||
|
val d = AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.load)
|
||||||
|
.setView(layout)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
loadColorString(et.text.toString())
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setNeutralButton(R.string.button_load_custom) { _, _ ->
|
||||||
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("text/*", "application/octet-stream", "application/json"))
|
||||||
|
.setType("*/*")
|
||||||
|
loadFilePicker.launch(intent)
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
et.doAfterTextChanged { d.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled = et.text.toString().isNotBlank() }
|
||||||
|
d.show()
|
||||||
|
d.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadColorString(colorString: String) {
|
||||||
|
// show dialog
|
||||||
|
// load from file or from text field
|
||||||
|
// do some sanity check (only write correct settings, consider current night mode)
|
||||||
|
try {
|
||||||
|
val that = Json.decodeFromString<SaveThoseColors>(colorString)
|
||||||
|
// save mode to moreColors and PREF_SHOW_MORE_COLORS (with night dependence!)
|
||||||
|
that.colors.forEach {
|
||||||
|
val pref = Settings.getColorPref(it.key, isNight)
|
||||||
|
if (it.value.first == null)
|
||||||
|
prefs.edit { remove(pref) }
|
||||||
|
else prefs.edit { putInt(pref, it.value.first!!) }
|
||||||
|
prefs.edit { putBoolean(pref + Settings.PREF_AUTO_USER_COLOR_SUFFIX, it.value.second) }
|
||||||
|
}
|
||||||
|
moreColors = that.moreColors
|
||||||
|
} catch (e: SerializationException) {
|
||||||
|
try {
|
||||||
|
val allColorsStringMap = Json.decodeFromString<Map<String, Int>>(colorString)
|
||||||
|
val allColors = EnumMap<ColorType, Int>(ColorType::class.java)
|
||||||
|
allColorsStringMap.forEach {
|
||||||
|
try {
|
||||||
|
allColors[ColorType.valueOf(it.key)] = it.value
|
||||||
|
} catch (_: IllegalArgumentException) {}
|
||||||
|
}
|
||||||
|
writeAllColorsMap(allColors, prefs, isNight)
|
||||||
|
moreColors = 2
|
||||||
|
} catch (e: SerializationException) {
|
||||||
|
infoDialog(requireContext(), "error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateColorPrefs()
|
||||||
|
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getColorString(): String {
|
||||||
|
if (moreColors == 2)
|
||||||
|
return Json.encodeToString(readAllColorsMap(prefs, isNight).map { it.key.name to it.value }.toMap())
|
||||||
|
// read the actual prefs!
|
||||||
|
val colors = colorPrefsAndNames.associate {
|
||||||
|
val pref = Settings.getColorPref(it.first, isNight)
|
||||||
|
val color = if (prefs.contains(pref)) prefs.getInt(pref, 0) else null
|
||||||
|
it.first to (color to prefs.getBoolean(pref + Settings.PREF_AUTO_USER_COLOR_SUFFIX, true))
|
||||||
|
}
|
||||||
|
return Json.encodeToString(SaveThoseColors(moreColors, colors))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val saveFilePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
if (it.resultCode != Activity.RESULT_OK) return@registerForActivityResult
|
||||||
|
val uri = it.data?.data ?: return@registerForActivityResult
|
||||||
|
activity?.contentResolver?.openOutputStream(uri)?.writer()?.use { it.write(getColorString()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val loadFilePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
if (it.resultCode != Activity.RESULT_OK) return@registerForActivityResult
|
||||||
|
val uri = it.data?.data ?: return@registerForActivityResult
|
||||||
|
activity?.contentResolver?.openInputStream(uri)?.use {
|
||||||
|
loadColorString(it.reader().readText())
|
||||||
|
} ?: infoDialog(requireContext(), R.string.file_read_error)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ColorsNightSettingsFragment : ColorsSettingsFragment() {
|
class ColorsNightSettingsFragment : ColorsSettingsFragment() {
|
||||||
|
|
|
@ -451,6 +451,8 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM
|
||||||
<string name="remove">Remove</string>
|
<string name="remove">Remove</string>
|
||||||
<!-- Title of the button to save a custom style entry in the settings dialog [CHAR LIMIT=15] -->
|
<!-- Title of the button to save a custom style entry in the settings dialog [CHAR LIMIT=15] -->
|
||||||
<string name="save">Save</string>
|
<string name="save">Save</string>
|
||||||
|
<!-- Title of the button to load a custom style entry in the settings dialog [CHAR LIMIT=15] -->
|
||||||
|
<string name="load">Load</string>
|
||||||
<!-- Title of the spinner for choosing a language of custom style in the settings dialog [CHAR LIMIT=15] -->
|
<!-- Title of the spinner for choosing a language of custom style in the settings dialog [CHAR LIMIT=15] -->
|
||||||
<string name="subtype_locale">Language</string>
|
<string name="subtype_locale">Language</string>
|
||||||
<!-- Title of the section for choosing a keyboard layout language settings dialog -->
|
<!-- Title of the section for choosing a keyboard layout language settings dialog -->
|
||||||
|
@ -459,8 +461,12 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM
|
||||||
<string name="button_title_add_custom_layout">Add custom layout</string>
|
<string name="button_title_add_custom_layout">Add custom layout</string>
|
||||||
<!-- Message text for adding a custom layout, interpreted as HTML -->
|
<!-- Message text for adding a custom layout, interpreted as HTML -->
|
||||||
<string name="message_add_custom_layout">Select a file in a compatible format. Information about the formats is available %s.</string>
|
<string name="message_add_custom_layout">Select a file in a compatible format. Information about the formats is available %s.</string>
|
||||||
<!-- Button text for loading a custom layout or image file -->
|
<!-- Button text for loading a custom layout, image or theme file -->
|
||||||
<string name="button_load_custom">Load file</string>
|
<string name="button_load_custom">Load file</string>
|
||||||
|
<!-- Button text for saving a theme file -->
|
||||||
|
<string name="button_save_file">Save to file</string>
|
||||||
|
<!-- Button text for copying text to clipboard -->
|
||||||
|
<string name="copy_to_clipboard">Copy to Clipboard</string>
|
||||||
<!-- Error message when file cannot be read -->
|
<!-- Error message when file cannot be read -->
|
||||||
<string name="file_read_error">Cannot read file</string>
|
<string name="file_read_error">Cannot read file</string>
|
||||||
<!-- Button text for copying an existing layout -->
|
<!-- Button text for copying an existing layout -->
|
||||||
|
@ -721,6 +727,8 @@ New dictionary:
|
||||||
<string name="more_colors">Show more colors</string>
|
<string name="more_colors">Show more colors</string>
|
||||||
<!-- Menu item for showing all colors -->
|
<!-- Menu item for showing all colors -->
|
||||||
<string name="all_colors">Show all colors</string>
|
<string name="all_colors">Show all colors</string>
|
||||||
|
<!-- Warning message displayed when loading theme colors -->
|
||||||
|
<string name="load_will_overwrite">Loading will overwrite the current theme</string>
|
||||||
<!-- Warning message displayed when showing all colors -->
|
<!-- Warning message displayed when showing all colors -->
|
||||||
<string name="all_colors_warning">This setting exposes all colors that are used internally. The list of colors may change at any time. The default color is random, and the names will not be translated.</string>
|
<string name="all_colors_warning">This setting exposes all colors that are used internally. The list of colors may change at any time. The default color is random, and the names will not be translated.</string>
|
||||||
<!-- Hint for text field just to show keyboard (in color settings) -->
|
<!-- Hint for text field just to show keyboard (in color settings) -->
|
||||||
|
|
Loading…
Add table
Reference in a new issue