Touchscreen support + small improvements
* Soft-Keyboard numpad * no more SoftKeyHandler, the main view is in its own package * settings are no longer passed unnecessarilly here and there * fixed numeric mode not working in some cases * simplified suggestion handling * fixed crashing when changing the phone orientation
This commit is contained in:
parent
7f6cd6110d
commit
4e5416f6b4
36 changed files with 1142 additions and 368 deletions
10
README.md
10
README.md
|
|
@ -10,11 +10,11 @@ This is an updated version of the [original project](https://github.com/Clam-/Tr
|
|||
or get the APK from the [Releases Section](https://github.com/sspanak/tt9/releases/latest).
|
||||
|
||||
## System Requirements
|
||||
- Android 4.4 or higher. _(Tested and confirmed on Android 4.4.2, 5.1.1 and 11)_
|
||||
- Android 4.4 or higher. _(Tested and confirmed on Android 4.4.2, 10 and 11)_
|
||||
- Free space:
|
||||
- Minimum 25 Mb when not using Predictive mode and no dictionaries are loaded.
|
||||
- Plenty of space per each enabled language in Predictive mode (25-100 Mb, depending on the word count).
|
||||
- A hardware keypad or a keyboard. The application is not usable on touchscreen-only devices.
|
||||
- A hardware keypad or a keyboard. For touchscreen-only devices, an on-screen keypad can be enabled in the Settings.
|
||||
|
||||
_If you own a phone with Android 2.2 up to 4.4, please refer to the original version of Traditional T9 from 2016._
|
||||
|
||||
|
|
@ -24,9 +24,9 @@ Before using Traditional T9 for the first time you would need to load a dictiona
|
|||
So make sure to read the initial setup and the hotkey tips in the [user manual](docs/user-manual.md).
|
||||
|
||||
## Contributing to the Project
|
||||
As with many other open-source projects, this one is also maintained by its author in his free time. Any help in making Traditional T9 better will be highly appreciated. Here is what you could do:
|
||||
- [Report bugs](https://github.com/sspanak/tt9/issues) or other unusual behavior on different phones. Currently, the only testing and development device is: Qin F21 Pro+ / Android 11.
|
||||
- Add [a new language](CONTRIBUTING.md#adding-a-new-language), [new UI translations](CONTRIBUTING.md#translating-the-ui) or simply fix a spelling mistake. The process is very simple and even with minimum techincal knowledge, your skills as a native speaker will be of great use. Or, if you are not tech-savvy, just [open a new issue](https://github.com/sspanak/tt9/issues) and put the correct translations there.
|
||||
As with many other open-source projects, this one is also maintained by its author in his free time. Any help in making Traditional T9 better will be highly appreciated. Here is how:
|
||||
- [Report bugs](https://github.com/sspanak/tt9/issues) or other unusual behavior on different phones. Currently, the only testing and development devices are: Qin F21 Pro+ / Android 11; Energizer H620SEU / Android 10. But Android behaviour and appearance varies a lot across the millions of devices available out there.
|
||||
- Add [a new language](CONTRIBUTING.md#adding-a-new-language), [new UI translations](CONTRIBUTING.md#translating-the-ui) or simply fix a spelling mistake. The process is very simple and even with minimum technical knowledge, your skills as a native speaker will be of great use. Or, if you are not tech-savvy, just [open a new issue](https://github.com/sspanak/tt9/issues) and put the correct translations there.
|
||||
- Experienced developers who are willing fix a bug, or maybe create a brand new feature, see the [Contribution Guide](CONTRIBUTING.md).
|
||||
|
||||
Your PRs are welcome!
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
# Traditional T9
|
||||
TT9 is an IME (Input Method Editor) for Android devices with hardware keypad. It supports multiple languages and predictive text typing. _NOTE: TT9 is not usable on touchscreen-only devices._
|
||||
TT9 is an IME (Input Method Editor) for Android devices with a hardware keypad. It supports multiple languages and predictive text typing, and an on-screen numpad for touchscreen devices.
|
||||
|
||||
All source code, documentation and the privacy policy are available on Github: [https://github.com/sspanak/tt9](https://github.com/sspanak/tt9).
|
||||
|
||||
## Initial Setup
|
||||
After installing, in order to use Traditional T9, you need to enable it as an Android keyboard. To do so, click on the launcher icon. If you
|
||||
need to take any action, you will see the Initial Setup screen, where you will be prompted to enable TT9 and set it as default system
|
||||
keyboard.
|
||||
After installing, in order to use Traditional T9, you need to enable it as an Android keyboard. To do so, click on the launcher icon. If you need to take any action, all options besides Initial Setup would be disabled and there would be a label saying TT9 is disabled. Go to Initial Setup and enable it.
|
||||
|
||||
_If you don't see the icon right after installing, restart your phone and it should appear. Android is trying to save some battery life by not refreshing the newly installed apps list in some cases._
|
||||
|
||||
### Using on a touchscreen-only phone
|
||||
If your phone does not have a hardware keypad, check out the [On-screen Keypad section](#on-screen-keypad).
|
||||
|
||||
### Enabling Predictive Mode
|
||||
With the default settings, it is only possible to type in 123 and ABC modes. In order to enable the Predictive mode, there are additional steps:
|
||||
|
||||
|
|
@ -47,8 +48,8 @@ Select next word/letter suggestion.
|
|||
#### D-pad Right (→):
|
||||
_Predictive mode only._
|
||||
|
||||
- **Single press**: Filter the suggestion list, leaving out only the ones that start with the current word. It doesn't matter if it is a complete word or not. For example, type "rewin" and press Right. It will leave out all words starting with "rewin": "rewin" itself, "rewind", "rewinds", "rewinded", "rewinding", and so on.
|
||||
- **Double press**: Expand the filter to the full suggestion. For example, type "rewin" and press Right twice. It will first filter by "rewin", then expand the filter to "rewind". You can keep expanding the filter with Right, until you get to the longest suggestion in the list.
|
||||
- **Single press**: Filter the suggestion list, leaving out only the ones that start with the current word. It doesn't matter if it is a complete word or not. For example, type "remin" and press Right. It will leave out all words starting with "remin": "remin" itself, "remind", "reminds", "reminded", "reminding", and so on.
|
||||
- **Double press**: Expand the filter to the full suggestion. For example, type "remin" and press Right twice. It will first filter by "remin", then expand the filter to "remind". You can keep expanding the filter with Right, until you get to the longest suggestion in the list.
|
||||
|
||||
Filtering can also be used to type unknown words. Let's say you want to type "Anakin", which is not in the dictionary. Start with "A", then press Right to hide "B" and "C". Now press 6-key. Since the filter is on, in addition to the real dictionary words, it will provide all possible combinations for 6: "Am", "An", "Ao". Select "An" and press Right to confirm your selection. Now pressing 2-key, will provide "Ana", "Anb", "Anc". You can keep going, until you complete "Anakin".
|
||||
|
||||
|
|
@ -103,19 +104,17 @@ _In these cases, you could assign another key (all other keys are fully usable),
|
|||
- **Number-only fields:** No special action. Type a "#" with the default key. Changing the mode is not possible in such fields.
|
||||
|
||||
#### Next Language Key (Default: Hold #):
|
||||
Select the next language, when mulitple languages have been enabled from the Settings.
|
||||
Select the next language, when multiple languages have been enabled from the Settings.
|
||||
|
||||
#### Settings Key (Default: Hold ✱):
|
||||
Open the Configration screen.
|
||||
Open the Settings configuration screen.
|
||||
|
||||
## On-screen Soft keys
|
||||
All functionality is available using the keypad, but for convenience, on touchscreen phones, you could also use the on-screen keys. If you instead prefer to have more screen space, disable them from the Settings.
|
||||
## On-screen Keypad
|
||||
On touchscreen-only phones, a fully functional on-screen keypad is available. Enable it from Settings -> Appearance -> Show On-Screen Numpad.
|
||||
|
||||
#### Left Soft Key:
|
||||
Open the [Settings screen](#settings-screen).
|
||||
It is also recommended to disable the special behavior of "Back" key working as "Backspace". It is useful only for a hardware keypad. To do so, go to: Settings -> Keyboard -> Select Hotkeys -> Backspace key, then select the "--" option.
|
||||
|
||||
#### Right Soft Key:
|
||||
Backspace.
|
||||
If you do have a hardware keypad and prefer having more screen space, disable the software keys from the Settings -> Appearance.
|
||||
|
||||
## Settings Screen
|
||||
On the Settings screen, you can choose languages for typing, configure the keypad hotkeys or change the application appearance.
|
||||
|
|
|
|||
231
res/layout/main_numpad.xml
Normal file
231
res/layout/main_numpad.xml
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main_numpad"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/numpad_candidate_height">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:textStyle="italic"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/suggestions_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fadingEdge="horizontal"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:scrollbars="none" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_soft_keys"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/numpad_padding_top"
|
||||
android:paddingBottom="@dimen/numpad_padding_bottom">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/numpad_key_height"
|
||||
tools:ignore="HardcodedText,KeyboardInaccessibleWidget">
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftKey
|
||||
android:id="@+id/soft_key_settings"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="⚙"
|
||||
android:textSize="@dimen/soft_key_icon_size" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_1"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_2"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_3"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftBackspaceKey
|
||||
android:id="@+id/soft_key_backspace"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="⌫"
|
||||
android:textSize="@dimen/soft_key_icon_size"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/numpad_key_height"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftKey
|
||||
android:id="@+id/soft_key_add_word"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="+WORD" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_4"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_5"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_6"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/numpad_key_height"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftKey
|
||||
android:id="@+id/soft_key_input_mode"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="MODE" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_7"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_8"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_9"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/numpad_key_height"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftKey
|
||||
android:id="@+id/soft_key_language"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="🌐"
|
||||
android:textSize="@dimen/soft_key_icon_size"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftPunctuationKey
|
||||
android:id="@+id/soft_key_punctuation_1"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftNumberKey
|
||||
android:id="@+id/soft_key_0"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftPunctuationKey
|
||||
android:id="@+id/soft_key_punctuation_2"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
|
||||
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftKey
|
||||
android:id="@+id/soft_key_ok"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/ok"
|
||||
tools:ignore="ButtonOrder" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/candidate_list_height">
|
||||
android:layout_height="@dimen/candidate_height">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_bar"
|
||||
|
|
@ -34,55 +35,47 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/soft_key_height"
|
||||
android:baselineAligned="true"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="HardcodedText,KeyboardInaccessibleWidget">
|
||||
|
||||
<Button
|
||||
android:id="@+id/main_left"
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftKey
|
||||
android:id="@+id/soft_key_settings"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="3"
|
||||
android:clickable="true"
|
||||
android:focusable="false"
|
||||
android:longClickable="true"
|
||||
android:text="⚙"
|
||||
android:textSize="@dimen/soft_key_icon_size"
|
||||
tools:ignore="HardcodedText,KeyboardInaccessibleWidget" />
|
||||
android:textSize="@dimen/soft_key_icon_size" />
|
||||
|
||||
<View
|
||||
android:id="@+id/main_separator_left"
|
||||
style="@style/hSeparator"
|
||||
android:background="@drawable/button_separator_dark" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/main_mid"
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftKey
|
||||
android:id="@+id/soft_key_ok"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="5"
|
||||
android:clickable="true"
|
||||
android:focusable="false"
|
||||
android:longClickable="true"
|
||||
android:text="@android:string/ok"
|
||||
tools:ignore="ButtonOrder,KeyboardInaccessibleWidget" />
|
||||
android:text="@android:string/ok" />
|
||||
|
||||
<View
|
||||
android:id="@+id/main_separator_right"
|
||||
android:background="@drawable/button_separator_dark"
|
||||
style="@style/hSeparator" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/main_right"
|
||||
<io.github.sspanak.tt9.ui.main.keys.SoftBackspaceKey
|
||||
android:id="@+id/soft_key_backspace"
|
||||
style="@android:style/Widget.Holo.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="3"
|
||||
android:clickable="true"
|
||||
android:focusable="false"
|
||||
android:longClickable="true"
|
||||
android:text="⌫"
|
||||
android:textSize="@dimen/soft_key_icon_size"
|
||||
tools:ignore="HardcodedText,KeyboardInaccessibleWidget" />
|
||||
android:textSize="@dimen/soft_key_icon_size" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
|
@ -2,15 +2,14 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/suggestion_list_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/suggestion_list_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="@dimen/candidate_padding_horizontal"
|
||||
android:paddingVertical="@dimen/candidate_padding_vertical"
|
||||
android:text=""
|
||||
android:textSize="@dimen/candidate_font_size" />
|
||||
</LinearLayout>
|
||||
16
res/layout/suggestion_list_numpad.xml
Normal file
16
res/layout/suggestion_list_numpad.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/suggestion_list_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/suggestion_list_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:minWidth="@dimen/numpad_candidate_min_width"
|
||||
android:paddingHorizontal="@dimen/candidate_padding_horizontal"
|
||||
android:textSize="@dimen/numpad_candidate_font_size" />
|
||||
</LinearLayout>
|
||||
|
|
@ -59,4 +59,5 @@
|
|||
<string name="pref_upside_down_keys_summary">Включете настройката, ако на първият ред са 7–8–9, вместо 1–2–3.</string>
|
||||
<string name="dictionary_truncate_unselected">Изтрий неизбраните</string>
|
||||
<string name="pref_category_setup">Начална настройка</string>
|
||||
<string name="pref_show_soft_numpad">Цифрова клавиатура на екрана</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -35,4 +35,5 @@
|
|||
<string name="pref_upside_down_keys">Orden de teclas inverso</string>
|
||||
<string name="pref_upside_down_keys_summary">Habilite la configuración si hay 7–8–9 en la primera fila, en lugar de 1–2–3.</string>
|
||||
<string name="pref_category_setup">Configuración inicial</string>
|
||||
<string name="pref_show_soft_numpad">Teclado numérico en pantalla</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -50,4 +50,5 @@
|
|||
<string name="pref_upside_down_keys_summary">Activez le paramètre s\'il y a 7–8–9 sur le premier rang, au lieu de 1–2–3.</string>
|
||||
<string name="dictionary_truncate_unselected">Vider les non sélectionnés</string>
|
||||
<string name="pref_category_setup">Configuration initiale</string>
|
||||
<string name="pref_show_soft_numpad">Pavé numérique à l\'écran</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -59,4 +59,5 @@
|
|||
<string name="pref_upside_down_keys_summary">Используйте настройку, если в первом ряду 7–8–9 вместо 1–2–3.</string>
|
||||
<string name="dictionary_truncate_unselected">Очистить невыбранные</string>
|
||||
<string name="pref_category_setup">Начальная настройка</string>
|
||||
<string name="pref_show_soft_numpad">Экранная цифровая клавиатура</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -59,4 +59,5 @@
|
|||
<string name="pref_upside_down_keys_summary">Використовуйте налаштування, якщо 7–8–9 у першому рядку замість 1–2–3.</string>
|
||||
<string name="dictionary_truncate_unselected">Очистіть невибрані</string>
|
||||
<string name="pref_category_setup">Початкове налаштування</string>
|
||||
<string name="pref_show_soft_numpad">Екранна цифрова клавіатура</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -8,10 +8,14 @@
|
|||
<color name="candidate_selected">#AAAAAA</color>
|
||||
<color name="candidate_separator">#888888</color>
|
||||
|
||||
<color name="numpad_background">#E7F0E7</color>
|
||||
|
||||
<!-- Dark theme -->
|
||||
<color name="dark_button_text">#C0C0C0</color>
|
||||
|
||||
<color name="dark_candidate_background">#333333</color>
|
||||
<color name="dark_candidate_background">#2C2C2C</color>
|
||||
<color name="dark_candidate_color">#CCCCCC</color>
|
||||
<color name="dark_candidate_selected">#555555</color>
|
||||
|
||||
<color name="dark_numpad_background">#353835</color>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="candidate_list_height">26sp</dimen>
|
||||
<dimen name="candidate_font_size">18sp</dimen>
|
||||
<dimen name="candidate_height">26sp</dimen>
|
||||
<dimen name="candidate_padding_horizontal">6sp</dimen>
|
||||
<dimen name="candidate_padding_vertical">1sp</dimen>
|
||||
|
||||
<dimen name="soft_key_height">44dp</dimen>
|
||||
<dimen name="soft_key_icon_size">24sp</dimen>
|
||||
|
|
@ -14,4 +13,13 @@
|
|||
<dimen name="pref_padding_vertical">18dp</dimen>
|
||||
<dimen name="pref_text_size">22sp</dimen>
|
||||
<dimen name="pref_summary_size">19sp</dimen>
|
||||
|
||||
<!-- Numpad -->
|
||||
<dimen name="numpad_padding_top">5dp</dimen>
|
||||
<dimen name="numpad_padding_bottom">15dp</dimen>
|
||||
<dimen name="numpad_key_height">56dp</dimen>
|
||||
|
||||
<dimen name="numpad_candidate_font_size">17sp</dimen>
|
||||
<dimen name="numpad_candidate_height">32dp</dimen>
|
||||
<dimen name="numpad_candidate_min_width">36dp</dimen>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
<string name="pref_dark_theme">Dark Theme</string>
|
||||
<string name="pref_double_zero_char">Character for Double 0-key Press</string>
|
||||
<string name="pref_show_soft_function_keys">Show On-Screen Keys</string>
|
||||
<string name="pref_show_soft_numpad">Show On-Screen Numpad</string>
|
||||
<string name="pref_show_soft_numpad_summary" translatable="false">(BETA)</string>
|
||||
<string name="pref_help">Help</string>
|
||||
<string name="pref_upside_down_keys">Reverse Key Order</string>
|
||||
<string name="pref_upside_down_keys_summary">Use this if you have 7–8–9 on the first row, instead of 1–2–3.</string>
|
||||
|
|
|
|||
|
|
@ -13,4 +13,12 @@
|
|||
app:layout="@layout/pref_switch"
|
||||
app:title="@string/pref_show_soft_function_keys" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:defaultValue="false"
|
||||
app:key="pref_show_soft_numpad"
|
||||
app:layout="@layout/pref_switch"
|
||||
app:title="@string/pref_show_soft_numpad"
|
||||
app:summary="@string/pref_show_soft_numpad_summary"
|
||||
app:dependency="pref_show_soft_keys" />
|
||||
|
||||
</PreferenceScreen>
|
||||
|
|
|
|||
|
|
@ -143,36 +143,17 @@ abstract class KeyPadHandler extends InputMethodService {
|
|||
isBackspaceHandled = false;
|
||||
}
|
||||
|
||||
if (Key.isOK(keyCode)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In numeric fields, we do not want to handle anything, but "backspace"
|
||||
if (mEditing == EDITING_STRICT_NUMERIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// holding "0" is important in all cases
|
||||
if (keyCode == KeyEvent.KEYCODE_0) {
|
||||
event.startTracking();
|
||||
return true;
|
||||
}
|
||||
|
||||
// In dialer fields we just want passthrough, but we do handle holding "0",
|
||||
// to convert it to "+".
|
||||
if (mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// start tracking key hold
|
||||
if (shouldTrackNumPress() || Key.isHotkey(settings, -keyCode)) {
|
||||
if (Key.isNumber(keyCode) || Key.isHotkey(settings, -keyCode)) {
|
||||
event.startTracking();
|
||||
}
|
||||
|
||||
return Key.isHotkey(settings, keyCode) || Key.isHotkey(settings, -keyCode)
|
||||
return
|
||||
Key.isNumber(keyCode)
|
||||
|| Key.isOK(keyCode)
|
||||
|| Key.isHotkey(settings, keyCode) || Key.isHotkey(settings, -keyCode)
|
||||
|| keyCode == KeyEvent.KEYCODE_STAR
|
||||
|| keyCode == KeyEvent.KEYCODE_POUND
|
||||
|| (Key.isNumber(keyCode) && shouldTrackNumPress())
|
||||
|| ((keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) && shouldTrackUpDown())
|
||||
|| ((keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) && shouldTrackLeftRight());
|
||||
}
|
||||
|
|
@ -217,12 +198,15 @@ abstract class KeyPadHandler extends InputMethodService {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Logger.d("onKeyUp", "Key: " + keyCode + " repeat?: " + event.getRepeatCount());
|
||||
|
||||
if (keyCode == ignoreNextKeyUp) {
|
||||
// Logger.d("onKeyUp", "Ignored: " + keyCode);
|
||||
ignoreNextKeyUp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// repeat handling
|
||||
keyRepeatCounter = (lastKeyCode == keyCode) ? keyRepeatCounter + 1 : 0;
|
||||
lastKeyCode = keyCode;
|
||||
|
||||
|
|
@ -231,46 +215,31 @@ abstract class KeyPadHandler extends InputMethodService {
|
|||
lastNumKeyCode = keyCode;
|
||||
}
|
||||
|
||||
// Logger.d("onKeyUp", "Key: " + keyCode + " repeat?: " + event.getRepeatCount());
|
||||
|
||||
// backspace is handled in onKeyDown only, so we ignore it here
|
||||
if (isBackspaceHandled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Key.isOK(keyCode)) {
|
||||
return onOK();
|
||||
}
|
||||
|
||||
// in numeric fields, we just handle backspace and let the rest go as-is.
|
||||
if (mEditing == EDITING_STRICT_NUMERIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_0) {
|
||||
return onNumber(Key.codeToNumber(settings, keyCode), false, numKeyRepeatCounter);
|
||||
}
|
||||
|
||||
// dialer fields are similar to pure numeric fields, but for user convenience, holding "0"
|
||||
// is converted to "+"
|
||||
if (mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handleHotkey(keyCode, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Key.isNumber(keyCode)) {
|
||||
return onNumber(Key.codeToNumber(settings, keyCode), false, numKeyRepeatCounter);
|
||||
}
|
||||
|
||||
if (Key.isOK(keyCode)) {
|
||||
return onOK();
|
||||
}
|
||||
|
||||
if (handleHotkey(keyCode, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_DPAD_UP: return onUp();
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN: return onDown();
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT: return onLeft();
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT: return onRight(keyRepeatCounter > 0);
|
||||
case KeyEvent.KEYCODE_STAR: return onStar();
|
||||
case KeyEvent.KEYCODE_POUND: return onPound();
|
||||
case KeyEvent.KEYCODE_STAR:
|
||||
case KeyEvent.KEYCODE_POUND:
|
||||
return onOtherKey(keyCode);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -324,8 +293,7 @@ abstract class KeyPadHandler extends InputMethodService {
|
|||
abstract protected boolean onLeft();
|
||||
abstract protected boolean onRight(boolean repeat);
|
||||
abstract protected boolean onNumber(int key, boolean hold, int repeat);
|
||||
abstract protected boolean onStar();
|
||||
abstract protected boolean onPound();
|
||||
abstract protected boolean onOtherKey(int keyCode);
|
||||
|
||||
// customized key handlers
|
||||
abstract protected boolean onKeyAddWord();
|
||||
|
|
|
|||
|
|
@ -1,150 +0,0 @@
|
|||
package io.github.sspanak.tt9.ime;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ui.UI;
|
||||
|
||||
class SoftKeyHandler implements View.OnTouchListener {
|
||||
private static final int[] buttons = { R.id.main_left, R.id.main_mid, R.id.main_right };
|
||||
private final TraditionalT9 tt9;
|
||||
private View view = null;
|
||||
|
||||
private long lastBackspaceCall = 0;
|
||||
|
||||
public SoftKeyHandler(TraditionalT9 tt9) {
|
||||
this.tt9 = tt9;
|
||||
|
||||
getView();
|
||||
}
|
||||
|
||||
|
||||
View getView() {
|
||||
if (view == null) {
|
||||
view = View.inflate(tt9.getApplicationContext(), R.layout.mainview, null);
|
||||
|
||||
for (int buttonId : buttons) {
|
||||
view.findViewById(buttonId).setOnTouchListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
void show() {
|
||||
if (view != null) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hide() {
|
||||
if (view != null) {
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setSoftKeysVisibility(boolean visible) {
|
||||
if (view != null) {
|
||||
view.findViewById(R.id.main_soft_keys).setVisibility(visible ? LinearLayout.VISIBLE : LinearLayout.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** setDarkTheme
|
||||
* Changes the main view colors according to the theme.
|
||||
*
|
||||
* We need to do this manually, instead of relying on the Context to resolve the appropriate colors,
|
||||
* because this View is part of the main service View. And service Views are always locked to the
|
||||
* system context and theme.
|
||||
*
|
||||
* More info:
|
||||
* https://stackoverflow.com/questions/72382886/system-applies-night-mode-to-views-added-in-service-type-application-overlay
|
||||
*/
|
||||
void setDarkTheme(boolean darkEnabled) {
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// background
|
||||
view.findViewById(R.id.main_soft_keys).setBackground(ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.drawable.button_background_dark : R.drawable.button_background
|
||||
));
|
||||
|
||||
// text
|
||||
int textColor = ContextCompat.getColor(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.color.dark_button_text : R.color.button_text
|
||||
);
|
||||
|
||||
for (int buttonId : buttons) {
|
||||
Button button = view.findViewById(buttonId);
|
||||
button.setTextColor(textColor);
|
||||
}
|
||||
|
||||
// separators
|
||||
Drawable separatorColor = ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.drawable.button_separator_dark : R.drawable.button_separator
|
||||
);
|
||||
|
||||
view.findViewById(R.id.main_separator_left).setBackground(separatorColor);
|
||||
view.findViewById(R.id.main_separator_right).setBackground(separatorColor);
|
||||
}
|
||||
|
||||
|
||||
private boolean handleBackspaceHold() {
|
||||
if (System.currentTimeMillis() - lastBackspaceCall < tt9.settings.getSoftKeyRepeatDelay()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean handled = tt9.onBackspace();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
lastBackspaceCall = lastBackspaceCall == 0 ? tt9.settings.getSoftKeyInitialDelay() + now : now;
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
||||
private boolean handleBackspaceUp() {
|
||||
lastBackspaceCall = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
int action = event.getAction();
|
||||
int buttonId = view.getId();
|
||||
|
||||
if (buttonId == R.id.main_left && action == MotionEvent.ACTION_UP) {
|
||||
UI.showSettingsScreen(tt9);
|
||||
return view.performClick();
|
||||
}
|
||||
|
||||
if (buttonId == R.id.main_mid && action == MotionEvent.ACTION_UP) {
|
||||
tt9.onOK();
|
||||
return view.performClick();
|
||||
}
|
||||
|
||||
if (buttonId == R.id.main_right) {
|
||||
if (action == MotionEvent.AXIS_PRESSURE) {
|
||||
return handleBackspaceHold();
|
||||
} else if (action == MotionEvent.ACTION_UP) {
|
||||
return handleBackspaceUp();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,13 +17,16 @@ import io.github.sspanak.tt9.Logger;
|
|||
import io.github.sspanak.tt9.db.DictionaryDb;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputModeValidator;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputType;
|
||||
import io.github.sspanak.tt9.ime.helpers.Key;
|
||||
import io.github.sspanak.tt9.ime.helpers.TextField;
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
import io.github.sspanak.tt9.ui.bottom.StatusBar;
|
||||
import io.github.sspanak.tt9.ui.bottom.SuggestionsBar;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
import io.github.sspanak.tt9.ui.UI;
|
||||
import io.github.sspanak.tt9.ui.main.MainView;
|
||||
import io.github.sspanak.tt9.ui.tray.StatusBar;
|
||||
import io.github.sspanak.tt9.ui.tray.SuggestionsBar;
|
||||
|
||||
public class TraditionalT9 extends KeyPadHandler {
|
||||
// internal settings/data
|
||||
|
|
@ -40,7 +43,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
protected Language mLanguage;
|
||||
|
||||
// soft key view
|
||||
private SoftKeyHandler softKeyHandler = null;
|
||||
private MainView mainView = null;
|
||||
private StatusBar statusBar = null;
|
||||
private SuggestionsBar suggestionBar = null;
|
||||
|
||||
|
|
@ -50,6 +53,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
return self.getApplicationContext();
|
||||
}
|
||||
|
||||
public SettingsStore getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
||||
private void loadSettings() {
|
||||
mLanguage = LanguageCollection.getLanguage(settings.getInputLanguage());
|
||||
|
|
@ -80,16 +87,9 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
DictionaryDb.init(this);
|
||||
DictionaryDb.normalizeWordFrequencies(settings);
|
||||
|
||||
if (softKeyHandler == null) {
|
||||
softKeyHandler = new SoftKeyHandler(this);
|
||||
}
|
||||
|
||||
if (statusBar == null) {
|
||||
statusBar = new StatusBar(softKeyHandler.getView());
|
||||
}
|
||||
|
||||
if (suggestionBar == null) {
|
||||
suggestionBar = new SuggestionsBar(settings, softKeyHandler.getView());
|
||||
if (mainView == null) {
|
||||
mainView = new MainView(this);
|
||||
initTray();
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
|
|
@ -115,17 +115,28 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
private void initUi() {
|
||||
statusBar
|
||||
.setText(mInputMode != null ? mInputMode.toString() : "")
|
||||
.setDarkTheme(settings.getDarkTheme());
|
||||
private void initTray() {
|
||||
setInputView(mainView.getView());
|
||||
statusBar = new StatusBar(mainView.getView());
|
||||
suggestionBar = new SuggestionsBar(this, mainView.getView());
|
||||
}
|
||||
|
||||
clearSuggestions();
|
||||
|
||||
private void setDarkTheme() {
|
||||
mainView.setDarkTheme(settings.getDarkTheme());
|
||||
statusBar.setDarkTheme(settings.getDarkTheme());
|
||||
suggestionBar.setDarkTheme(settings.getDarkTheme());
|
||||
}
|
||||
|
||||
softKeyHandler.setDarkTheme(settings.getDarkTheme());
|
||||
softKeyHandler.setSoftKeysVisibility(settings.getShowSoftKeys());
|
||||
softKeyHandler.show();
|
||||
|
||||
private void initUi() {
|
||||
if (mainView.createView()) {
|
||||
initTray();
|
||||
}
|
||||
clearSuggestions();
|
||||
statusBar.setText(mInputMode != null ? mInputMode.toString() : "");
|
||||
setDarkTheme();
|
||||
mainView.render();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -163,8 +174,6 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
protected void onStop() {
|
||||
onFinishTyping();
|
||||
clearSuggestions();
|
||||
|
||||
softKeyHandler.hide();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -193,10 +202,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
public boolean onOK() {
|
||||
if (!textField.isThereText()) {
|
||||
if (!isInputViewShown() && !textField.isThereText()) {
|
||||
forceShowWindowIfHidden();
|
||||
return true;
|
||||
} else if (isSuggestionViewHidden() && currentInputConnection != null) {
|
||||
} else if (isSuggestionViewHidden()) {
|
||||
return performOKAction();
|
||||
}
|
||||
|
||||
|
|
@ -275,12 +284,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
String currentWord = getComposingText();
|
||||
|
||||
// Automatically accept the current word, when the next one is a space or whatnot,
|
||||
// Automatically accept the current word, when the next one is a space or punctuation,
|
||||
// instead of requiring "OK" before that.
|
||||
if (mInputMode.shouldAcceptCurrentSuggestion(key, hold, repeat > 0)) {
|
||||
mInputMode.onAcceptSuggestion(currentWord);
|
||||
commitCurrentSuggestion(false);
|
||||
autoCorrectSpace(currentWord, false, key, hold, repeat > 0);
|
||||
autoCorrectSpace(acceptIncompleteSuggestion(), false, key, hold, repeat > 0);
|
||||
currentWord = "";
|
||||
}
|
||||
|
||||
|
|
@ -296,17 +303,6 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
if (mInputMode.shouldSelectNextSuggestion() && !isSuggestionViewHidden()) {
|
||||
nextSuggestion();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mInputMode.getWord() != null) {
|
||||
currentWord = mInputMode.getWord();
|
||||
|
||||
mInputMode.onAcceptSuggestion(currentWord);
|
||||
textField.setText(currentWord);
|
||||
clearSuggestions();
|
||||
autoCorrectSpace(currentWord, true, key, hold, repeat > 0);
|
||||
resetKeyRepeat();
|
||||
} else {
|
||||
getSuggestions();
|
||||
}
|
||||
|
|
@ -315,19 +311,33 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
protected boolean onPound() {
|
||||
textField.setText("#");
|
||||
public boolean onOtherKey(int keyCode) {
|
||||
if (
|
||||
keyCode <= 0 ||
|
||||
(mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) && !Key.isNumber(keyCode)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
autoCorrectSpace(acceptIncompleteSuggestion(), false, -1, false, false);
|
||||
sendDownUpKeyEvents(keyCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected boolean onStar() {
|
||||
textField.setText("*");
|
||||
public boolean onText(String text) {
|
||||
if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER || text.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
autoCorrectSpace(acceptIncompleteSuggestion(), false, -1, false, false);
|
||||
textField.setText(text);
|
||||
autoCorrectSpace(text, false, -1, false, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected boolean onKeyAddWord() {
|
||||
public boolean onKeyAddWord() {
|
||||
if (mEditing == EDITING_STRICT_NUMERIC || mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -337,7 +347,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
protected boolean onKeyNextLanguage() {
|
||||
public boolean onKeyNextLanguage() {
|
||||
if (nextLang()) {
|
||||
commitCurrentSuggestion(false);
|
||||
mInputMode.changeLanguage(mLanguage);
|
||||
|
|
@ -345,6 +355,7 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
resetKeyRepeat();
|
||||
clearSuggestions();
|
||||
statusBar.setText(mInputMode.toString());
|
||||
mainView.render();
|
||||
forceShowWindowIfHidden();
|
||||
|
||||
return true;
|
||||
|
|
@ -354,14 +365,15 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
protected boolean onKeyNextInputMode() {
|
||||
public boolean onKeyNextInputMode() {
|
||||
nextInputMode();
|
||||
mainView.render();
|
||||
forceShowWindowIfHidden();
|
||||
return (mEditing != EDITING_STRICT_NUMERIC && mEditing != EDITING_DIALER);
|
||||
}
|
||||
|
||||
|
||||
protected boolean onKeyShowSettings() {
|
||||
public boolean onKeyShowSettings() {
|
||||
if (mEditing == EDITING_DIALER) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -414,6 +426,15 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
}
|
||||
|
||||
|
||||
private String acceptIncompleteSuggestion() {
|
||||
String currentWord = getComposingText();
|
||||
mInputMode.onAcceptSuggestion(currentWord);
|
||||
commitCurrentSuggestion(false);
|
||||
|
||||
return currentWord;
|
||||
}
|
||||
|
||||
|
||||
private void commitCurrentSuggestion() {
|
||||
commitCurrentSuggestion(true);
|
||||
}
|
||||
|
|
@ -448,9 +469,22 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
private void handleSuggestions() {
|
||||
// key code "suggestions" take priority over words
|
||||
if (mInputMode.getKeyCode() > 0) {
|
||||
sendDownUpKeyEvents(mInputMode.getKeyCode());
|
||||
mInputMode.onAcceptSuggestion(null);
|
||||
}
|
||||
|
||||
// display the list of suggestions
|
||||
setSuggestions(mInputMode.getSuggestions());
|
||||
|
||||
// Put the first suggestion in the text field,
|
||||
// flush the first suggestion immediately, if the InputMode has requested it
|
||||
if (mInputMode.getAutoAcceptTimeout() == 0) {
|
||||
onOK();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, put the first suggestion in the text field,
|
||||
// but cut it off to the length of the sequence (how many keys were pressed),
|
||||
// for a more intuitive experience.
|
||||
String word = suggestionBar.getCurrentSuggestion();
|
||||
|
|
@ -615,6 +649,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
|
||||
|
||||
private boolean performOKAction() {
|
||||
if (currentInputConnection == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int action = textField.getAction();
|
||||
switch (action) {
|
||||
case EditorInfo.IME_ACTION_NONE:
|
||||
|
|
@ -684,7 +722,10 @@ public class TraditionalT9 extends KeyPadHandler {
|
|||
* Generates the actual UI of TT9.
|
||||
*/
|
||||
protected View createSoftKeyView() {
|
||||
return softKeyHandler.getView();
|
||||
mainView.forceCreateView();
|
||||
initTray();
|
||||
setDarkTheme();
|
||||
return mainView.getView();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -65,4 +65,12 @@ public class Key {
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int numberToCode(int number) {
|
||||
if (number >= 0 && number <= 9) {
|
||||
return KeyEvent.KEYCODE_0 + number;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@ abstract public class InputMode {
|
|||
protected int textFieldTextCase = CASE_UNDEFINED;
|
||||
|
||||
// data
|
||||
protected int autoAcceptTimeout = -1;
|
||||
protected Language language;
|
||||
protected ArrayList<String> suggestions = new ArrayList<>();
|
||||
protected String word = null;
|
||||
protected int keyCode = 0;
|
||||
|
||||
|
||||
public static InputMode getInstance(SettingsStore settings, Language language, int mode) {
|
||||
|
|
@ -47,7 +48,7 @@ abstract public class InputMode {
|
|||
|
||||
// Key handlers. Return "true" when handling the key or "false", when is nothing to do.
|
||||
public boolean onBackspace() { return false; }
|
||||
abstract public boolean onNumber(int key, boolean hold, int repeat);
|
||||
abstract public boolean onNumber(int number, boolean hold, int repeat);
|
||||
|
||||
// Predictions
|
||||
public void onAcceptSuggestion(String suggestion) {}
|
||||
|
|
@ -63,9 +64,6 @@ abstract public class InputMode {
|
|||
return newSuggestions;
|
||||
}
|
||||
|
||||
// Word
|
||||
public String getWord() { return word; }
|
||||
|
||||
// Mode identifiers
|
||||
public boolean isPredictive() { return false; }
|
||||
public boolean isABC() { return false; }
|
||||
|
|
@ -74,6 +72,10 @@ abstract public class InputMode {
|
|||
// Utility
|
||||
abstract public int getId();
|
||||
abstract public int getSequenceLength(); // The number of key presses for the current word.
|
||||
public int getAutoAcceptTimeout() {
|
||||
return autoAcceptTimeout;
|
||||
}
|
||||
public int getKeyCode() { return keyCode; }
|
||||
public void changeLanguage(Language newLanguage) {
|
||||
if (newLanguage != null) {
|
||||
language = newLanguage;
|
||||
|
|
@ -90,8 +92,9 @@ abstract public class InputMode {
|
|||
public boolean shouldTrackLeftRight() { return false; }
|
||||
|
||||
public void reset() {
|
||||
suggestions = new ArrayList<>();
|
||||
word = null;
|
||||
autoAcceptTimeout = -1;
|
||||
keyCode = 0;
|
||||
suggestions.clear();
|
||||
}
|
||||
|
||||
// Text case
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package io.github.sspanak.tt9.ime.modes;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import io.github.sspanak.tt9.ime.helpers.Key;
|
||||
|
||||
public class Mode123 extends InputMode {
|
||||
public int getId() { return MODE_123; }
|
||||
|
|
@ -11,21 +11,24 @@ public class Mode123 extends InputMode {
|
|||
allowedTextCases.add(CASE_LOWER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNumber(int number, boolean hold, int repeat) {
|
||||
reset();
|
||||
|
||||
public boolean onNumber(int key, boolean hold, int repeat) {
|
||||
if (key != 0) {
|
||||
return false;
|
||||
if (number == 0 && hold) {
|
||||
autoAcceptTimeout = 0;
|
||||
suggestions.add("+");
|
||||
} else {
|
||||
keyCode = Key.numberToCode(number);
|
||||
}
|
||||
|
||||
suggestions = new ArrayList<>();
|
||||
word = hold ? "+" : "0";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
final public boolean is123() { return true; }
|
||||
public int getSequenceLength() { return 0; }
|
||||
public boolean shouldTrackNumPress() { return false; }
|
||||
@Override final public boolean is123() { return true; }
|
||||
@Override public int getSequenceLength() { return 0; }
|
||||
@Override public boolean shouldTrackNumPress() { return false; }
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package io.github.sspanak.tt9.ime.modes;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
|
||||
public class ModeABC extends InputMode {
|
||||
|
|
@ -17,16 +15,16 @@ public class ModeABC extends InputMode {
|
|||
|
||||
|
||||
@Override
|
||||
public boolean onNumber(int key, boolean hold, int repeat) {
|
||||
shouldSelectNextLetter = false;
|
||||
suggestions = language.getKeyCharacters(key);
|
||||
word = null;
|
||||
|
||||
public boolean onNumber(int number, boolean hold, int repeat) {
|
||||
if (hold) {
|
||||
suggestions = new ArrayList<>();
|
||||
word = String.valueOf(key);
|
||||
reset();
|
||||
suggestions.add(String.valueOf(number));
|
||||
autoAcceptTimeout = 0;
|
||||
} else if (repeat > 0) {
|
||||
shouldSelectNextLetter = true;
|
||||
} else {
|
||||
reset();
|
||||
suggestions.addAll(language.getKeyCharacters(number));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -59,6 +57,12 @@ public class ModeABC extends InputMode {
|
|||
return shouldSelectNextLetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
shouldSelectNextLetter = false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import io.github.sspanak.tt9.ime.helpers.TextField;
|
|||
import io.github.sspanak.tt9.ime.modes.helpers.AutoSpace;
|
||||
import io.github.sspanak.tt9.ime.modes.helpers.AutoTextCase;
|
||||
import io.github.sspanak.tt9.ime.modes.helpers.Predictions;
|
||||
import io.github.sspanak.tt9.languages.InvalidLanguageCharactersException;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
||||
|
|
@ -69,40 +68,25 @@ public class ModePredictive extends InputMode {
|
|||
|
||||
|
||||
@Override
|
||||
public boolean onNumber(int key, boolean hold, int repeat) {
|
||||
public boolean onNumber(int number, boolean hold, int repeat) {
|
||||
if (hold) {
|
||||
// hold to type any digit
|
||||
reset();
|
||||
word = String.valueOf(key);
|
||||
} else if (key == 0 && repeat > 0) {
|
||||
onDouble0();
|
||||
autoAcceptTimeout = 0;
|
||||
suggestions.add(String.valueOf(number));
|
||||
} else {
|
||||
// words
|
||||
super.reset();
|
||||
digitSequence += key;
|
||||
digitSequence += number;
|
||||
if (number == 0 && repeat > 0) {
|
||||
autoAcceptTimeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* onDouble0
|
||||
* Double "0" is a shortcut for the preferred character.
|
||||
*/
|
||||
private void onDouble0() {
|
||||
try {
|
||||
reset();
|
||||
word = settings.getDoubleZeroChar();
|
||||
digitSequence = language.getDigitSequenceForWord(word);
|
||||
} catch (InvalidLanguageCharactersException e) {
|
||||
Logger.w("tt9/onDouble0", "Failed getting the sequence for word: '" + word + "'. Performing standard 0-key action.");
|
||||
reset();
|
||||
digitSequence = "0";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void changeLanguage(Language language) {
|
||||
super.changeLanguage(language);
|
||||
|
|
@ -217,7 +201,6 @@ public class ModePredictive extends InputMode {
|
|||
.setWordsChangedHandler(handleSuggestions);
|
||||
|
||||
handleSuggestionsExternal = handler;
|
||||
super.reset();
|
||||
|
||||
return predictions.load();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class Predictions {
|
|||
private Handler wordsChangedHandler;
|
||||
|
||||
// data
|
||||
private ArrayList<String> words = new ArrayList<>();
|
||||
private final ArrayList<String> words = new ArrayList<>();
|
||||
|
||||
// punctuation/emoji
|
||||
private final Pattern containsOnly1Regex = Pattern.compile("^1+$");
|
||||
|
|
@ -127,7 +127,7 @@ public class Predictions {
|
|||
*/
|
||||
public boolean load() {
|
||||
if (digitSequence == null || digitSequence.length() == 0) {
|
||||
words = new ArrayList<>();
|
||||
words.clear();
|
||||
onWordsChanged();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -155,16 +155,27 @@ public class Predictions {
|
|||
* Returns "false", when there are no static options for the current digitSequence.
|
||||
*/
|
||||
private boolean loadStatic() {
|
||||
// whitespace/special/math characters
|
||||
if (digitSequence.equals("0")) {
|
||||
words.clear();
|
||||
stem = "";
|
||||
words = language.getKeyCharacters(0, false);
|
||||
} else if (containsOnly1Regex.matcher(digitSequence).matches()) {
|
||||
words.addAll(language.getKeyCharacters(0, false));
|
||||
}
|
||||
// "00" is a shortcut for the preferred character
|
||||
else if (digitSequence.equals("00")) {
|
||||
words.clear();
|
||||
stem = "";
|
||||
words.add(settings.getDoubleZeroChar());
|
||||
}
|
||||
// emoji
|
||||
else if (containsOnly1Regex.matcher(digitSequence).matches()) {
|
||||
words.clear();
|
||||
stem = "";
|
||||
if (digitSequence.length() == 1) {
|
||||
words = language.getKeyCharacters(1, false);
|
||||
words.addAll(language.getKeyCharacters(1, false));
|
||||
} else {
|
||||
digitSequence = digitSequence.length() <= maxEmojiSequence.length() ? digitSequence : maxEmojiSequence;
|
||||
words = Characters.getEmoji(digitSequence.length() - 2);
|
||||
words.addAll(Characters.getEmoji(digitSequence.length() - 2));
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -205,10 +205,9 @@ public class SettingsStore {
|
|||
|
||||
public boolean getDarkTheme() { return prefs.getBoolean("pref_dark_theme", true); }
|
||||
|
||||
|
||||
public boolean getShowSoftKeys() { return prefs.getBoolean("pref_show_soft_keys", true); }
|
||||
|
||||
|
||||
public boolean getShowSoftNumpad() { return getShowSoftKeys() && prefs.getBoolean("pref_show_soft_numpad", false); }
|
||||
|
||||
/************* typing settings *************/
|
||||
|
||||
|
|
|
|||
92
src/io/github/sspanak/tt9/ui/main/BaseMainLayout.java
Normal file
92
src/io/github/sspanak/tt9/ui/main/BaseMainLayout.java
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package io.github.sspanak.tt9.ui.main;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.ui.main.keys.SoftKey;
|
||||
|
||||
abstract class BaseMainLayout {
|
||||
protected TraditionalT9 tt9;
|
||||
private final int xml;
|
||||
|
||||
protected View view = null;
|
||||
protected ArrayList<SoftKey> keys = new ArrayList<>();
|
||||
|
||||
public BaseMainLayout(TraditionalT9 tt9, int xml) {
|
||||
this.tt9 = tt9;
|
||||
this.xml = xml;
|
||||
}
|
||||
|
||||
|
||||
/** setDarkTheme
|
||||
* Changes the main view colors according to the theme.
|
||||
*
|
||||
* We need to do this manually, instead of relying on the Context to resolve the appropriate colors,
|
||||
* because this View is part of the main service View. And service Views are always locked to the
|
||||
* system context and theme.
|
||||
*
|
||||
* More info:
|
||||
* <a href="https://stackoverflow.com/questions/72382886/system-applies-night-mode-to-views-added-in-service-type-application-overlay">...</a>
|
||||
*/
|
||||
abstract public void setDarkTheme(boolean yes);
|
||||
|
||||
|
||||
/**
|
||||
* render
|
||||
* Do all the necessary stuff to display the View.
|
||||
*/
|
||||
abstract public void render();
|
||||
|
||||
|
||||
/**
|
||||
* getKeys
|
||||
* Returns a list of all the usable Soft Keys.
|
||||
*/
|
||||
abstract protected ArrayList<SoftKey> getKeys();
|
||||
|
||||
|
||||
public View getView() {
|
||||
if (view == null) {
|
||||
view = View.inflate(tt9.getApplicationContext(), xml, null);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (view != null) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
if (view != null) {
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void enableClickHandlers() {
|
||||
for (SoftKey key : getKeys()) {
|
||||
key.setTT9(tt9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected ArrayList<SoftKey> getKeysFromContainer(ViewGroup container) {
|
||||
ArrayList<SoftKey> keyList = new ArrayList<>();
|
||||
final int childrenCount = container != null ? container.getChildCount() : 0;
|
||||
|
||||
for (int i = 0; i < childrenCount; i++) {
|
||||
View child = container.getChildAt(i);
|
||||
if (child instanceof SoftKey) {
|
||||
keyList.add((SoftKey) child);
|
||||
}
|
||||
}
|
||||
|
||||
return keyList;
|
||||
}
|
||||
}
|
||||
64
src/io/github/sspanak/tt9/ui/main/MainLayoutNumpad.java
Normal file
64
src/io/github/sspanak/tt9/ui/main/MainLayoutNumpad.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package io.github.sspanak.tt9.ui.main;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.ui.main.keys.SoftKey;
|
||||
|
||||
class MainLayoutNumpad extends BaseMainLayout {
|
||||
public MainLayoutNumpad(TraditionalT9 tt9) {
|
||||
super(tt9, R.layout.main_numpad);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDarkTheme(boolean darkEnabled) {
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// background
|
||||
view.setBackground(ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.color.dark_numpad_background : R.color.numpad_background
|
||||
));
|
||||
|
||||
// text
|
||||
for (SoftKey key : getKeys()) {
|
||||
key.setDarkTheme(darkEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
getView();
|
||||
enableClickHandlers();
|
||||
for (SoftKey key : getKeys()) {
|
||||
key.render();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayList<SoftKey> getKeys() {
|
||||
if (keys != null && keys.size() > 0) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
ViewGroup table = view.findViewById(R.id.main_soft_keys);
|
||||
int tableRowsCount = table.getChildCount();
|
||||
|
||||
for (int rowId = 0; rowId < tableRowsCount; rowId++) {
|
||||
View row = table.getChildAt(rowId);
|
||||
if (row instanceof ViewGroup) {
|
||||
keys.addAll(getKeysFromContainer((ViewGroup) row));
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
67
src/io/github/sspanak/tt9/ui/main/MainLayoutSmall.java
Normal file
67
src/io/github/sspanak/tt9/ui/main/MainLayoutSmall.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package io.github.sspanak.tt9.ui.main;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
import io.github.sspanak.tt9.ui.main.keys.SoftKey;
|
||||
|
||||
class MainLayoutSmall extends BaseMainLayout {
|
||||
public MainLayoutSmall(TraditionalT9 tt9) {
|
||||
super(tt9, R.layout.main_small);
|
||||
}
|
||||
|
||||
private void setSoftKeysVisibility() {
|
||||
if (view != null) {
|
||||
view.findViewById(R.id.main_soft_keys).setVisibility(tt9.getSettings().getShowSoftKeys() ? LinearLayout.VISIBLE : LinearLayout.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
getView();
|
||||
enableClickHandlers();
|
||||
setSoftKeysVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void setDarkTheme(boolean darkEnabled) {
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// background
|
||||
view.findViewById(R.id.main_soft_keys).setBackground(ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.drawable.button_background_dark : R.drawable.button_background
|
||||
));
|
||||
|
||||
// text
|
||||
for (SoftKey key : getKeys()) {
|
||||
key.setDarkTheme(darkEnabled);
|
||||
}
|
||||
|
||||
// separators
|
||||
Drawable separatorColor = ContextCompat.getDrawable(
|
||||
view.getContext(),
|
||||
darkEnabled ? R.drawable.button_separator_dark : R.drawable.button_separator
|
||||
);
|
||||
|
||||
view.findViewById(R.id.main_separator_left).setBackground(separatorColor);
|
||||
view.findViewById(R.id.main_separator_right).setBackground(separatorColor);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ArrayList<SoftKey> getKeys() {
|
||||
if (view != null && (keys == null || keys.size() == 0)) {
|
||||
keys = getKeysFromContainer(view.findViewById(R.id.main_soft_keys));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
47
src/io/github/sspanak/tt9/ui/main/MainView.java
Normal file
47
src/io/github/sspanak/tt9/ui/main/MainView.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package io.github.sspanak.tt9.ui.main;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
|
||||
public class MainView {
|
||||
private final TraditionalT9 tt9;
|
||||
private BaseMainLayout main;
|
||||
|
||||
public MainView(TraditionalT9 tt9) {
|
||||
this.tt9 = tt9;
|
||||
|
||||
forceCreateView();
|
||||
}
|
||||
|
||||
public boolean createView() {
|
||||
if (tt9.getSettings().getShowSoftNumpad() && !(main instanceof MainLayoutNumpad)) {
|
||||
main = new MainLayoutNumpad(tt9);
|
||||
main.render();
|
||||
return true;
|
||||
} else if (!tt9.getSettings().getShowSoftNumpad() && !(main instanceof MainLayoutSmall)) {
|
||||
main = new MainLayoutSmall(tt9);
|
||||
main.render();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void forceCreateView() {
|
||||
main = null;
|
||||
createView();
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return main.getView();
|
||||
}
|
||||
|
||||
public void render() {
|
||||
main.render();
|
||||
}
|
||||
|
||||
public void setDarkTheme(boolean darkEnabled) {
|
||||
main.setDarkTheme(darkEnabled);
|
||||
}
|
||||
}
|
||||
75
src/io/github/sspanak/tt9/ui/main/keys/SoftBackspaceKey.java
Normal file
75
src/io/github/sspanak/tt9/ui/main/keys/SoftBackspaceKey.java
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package io.github.sspanak.tt9.ui.main.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
||||
public class SoftBackspaceKey extends SoftKey {
|
||||
private SettingsStore settings;
|
||||
long lastBackspaceCall = 0;
|
||||
|
||||
public SoftBackspaceKey(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SoftBackspaceKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SoftBackspaceKey(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private SettingsStore getSettings() {
|
||||
if (settings == null) {
|
||||
settings = new SettingsStore(getContext());
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
final public boolean onTouch(View view, MotionEvent event) {
|
||||
if (tt9 == null) {
|
||||
Logger.w(getClass().getCanonicalName(), "Traditional T9 handler is not set. Ignoring key press.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int action = event.getAction() & MotionEvent.ACTION_MASK;
|
||||
|
||||
if (action == MotionEvent.AXIS_PRESSURE) {
|
||||
handleHold();
|
||||
} else if (action == MotionEvent.ACTION_UP) {
|
||||
handleUp();
|
||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||
// Fallback for phones that do not report AXIS_PRESSURE, when a key is being held
|
||||
handlePress(-1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleHold() {
|
||||
if (System.currentTimeMillis() - lastBackspaceCall < getSettings().getSoftKeyRepeatDelay()) {
|
||||
return;
|
||||
}
|
||||
|
||||
handlePress(-1);
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
lastBackspaceCall = lastBackspaceCall == 0 ? getSettings().getSoftKeyInitialDelay() + now : now;
|
||||
}
|
||||
|
||||
private void handleUp() {
|
||||
lastBackspaceCall = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
final protected boolean handlePress(int b) {
|
||||
return tt9.onBackspace();
|
||||
}
|
||||
}
|
||||
126
src/io/github/sspanak/tt9/ui/main/keys/SoftKey.java
Normal file
126
src/io/github/sspanak/tt9/ui/main/keys/SoftKey.java
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
package io.github.sspanak.tt9.ui.main.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
|
||||
public class SoftKey extends androidx.appcompat.widget.AppCompatButton implements View.OnTouchListener {
|
||||
protected TraditionalT9 tt9;
|
||||
|
||||
public SoftKey(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SoftKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SoftKey(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setTT9(TraditionalT9 tt9) {
|
||||
this.tt9 = tt9;
|
||||
}
|
||||
|
||||
public void setDarkTheme(boolean darkEnabled) {
|
||||
int textColor = ContextCompat.getColor(
|
||||
getContext(),
|
||||
darkEnabled ? R.color.dark_button_text : R.color.button_text
|
||||
);
|
||||
setTextColor(textColor);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
getRootView().setOnTouchListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
super.onTouchEvent(event);
|
||||
|
||||
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
return handlePress(view.getId());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean handlePress(int keyId) {
|
||||
if (tt9 == null) {
|
||||
Logger.w(getClass().getCanonicalName(), "Traditional T9 handler is not set. Ignoring key press.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyId == R.id.soft_key_add_word) return tt9.onKeyAddWord();
|
||||
if (keyId == R.id.soft_key_input_mode) return tt9.onKeyNextInputMode();
|
||||
if (keyId == R.id.soft_key_language) return tt9.onKeyNextLanguage();
|
||||
if (keyId == R.id.soft_key_ok) return tt9.onOK();
|
||||
if (keyId == R.id.soft_key_settings) return tt9.onKeyShowSettings();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the name of the key, for example: "OK", "Backspace", "1", etc...
|
||||
*/
|
||||
protected String getKeyNameLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a String describing what the key does.
|
||||
* For example: "ABC" for 2-key; "⌫" for Backspace key, "⚙" for Settings key, and so on.
|
||||
*
|
||||
* The function label is optional.
|
||||
*/
|
||||
protected String getKeyFunctionLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* render
|
||||
* Sets the key label using "getKeyNameLabel()" and "getKeyFunctionLabel()" or if they both
|
||||
* return NULL, the XML "text" attribute will be preserved.
|
||||
*
|
||||
* If there is only name label, it will be centered and at normal font size.
|
||||
* If there is also a function label, it will be displayed below the name label and both will
|
||||
* have their font size adjusted to fit inside the key.
|
||||
*/
|
||||
public void render() {
|
||||
String name = getKeyNameLabel();
|
||||
String func = getKeyFunctionLabel();
|
||||
|
||||
if (name == null) {
|
||||
return;
|
||||
} else if (func == null) {
|
||||
setText(name);
|
||||
return;
|
||||
}
|
||||
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder(name);
|
||||
sb.append('\n');
|
||||
sb.append(func);
|
||||
|
||||
sb.setSpan(new RelativeSizeSpan(0.55f), 0, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
sb.setSpan(new StyleSpan(Typeface.ITALIC), 0, 2, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
|
||||
sb.setSpan(new RelativeSizeSpan(0.75f), 1, sb.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
setText(sb);
|
||||
}
|
||||
}
|
||||
99
src/io/github/sspanak/tt9/ui/main/keys/SoftNumberKey.java
Normal file
99
src/io/github/sspanak/tt9/ui/main/keys/SoftNumberKey.java
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package io.github.sspanak.tt9.ui.main.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.helpers.Key;
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
|
||||
public class SoftNumberKey extends SoftKey {
|
||||
public SoftNumberKey(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SoftNumberKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SoftNumberKey(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
protected boolean handlePress(int keyId) {
|
||||
if (tt9 == null) {
|
||||
Logger.w(getClass().getCanonicalName(), "Traditional T9 handler is not set. Ignoring key press.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int keyCode = Key.numberToCode(getNumber(keyId));
|
||||
if (keyCode < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tt9.onKeyDown(keyCode, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
|
||||
tt9.onKeyUp(keyCode, new KeyEvent(KeyEvent.ACTION_UP, keyCode));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getNumber(int keyId) {
|
||||
if (keyId == R.id.soft_key_0) return 0;
|
||||
if (keyId == R.id.soft_key_1) return 1;
|
||||
if (keyId == R.id.soft_key_2) return 2;
|
||||
if (keyId == R.id.soft_key_3) return 3;
|
||||
if (keyId == R.id.soft_key_4) return 4;
|
||||
if (keyId == R.id.soft_key_5) return 5;
|
||||
if (keyId == R.id.soft_key_6) return 6;
|
||||
if (keyId == R.id.soft_key_7) return 7;
|
||||
if (keyId == R.id.soft_key_8) return 8;
|
||||
if (keyId == R.id.soft_key_9) return 9;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKeyNameLabel() {
|
||||
return String.valueOf(getNumber(getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKeyFunctionLabel() {
|
||||
if (tt9 == null || tt9.getSettings().getInputMode() == InputMode.MODE_123) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int number = getNumber(getId());
|
||||
int textCase = tt9.getSettings().getTextCase();
|
||||
Language language = LanguageCollection.getLanguage(tt9.getSettings().getInputLanguage());
|
||||
|
||||
if (language == null) {
|
||||
Logger.d("SoftNumberKey.getLabel", "Cannot generate a label when the language is NULL.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (number == 0) {
|
||||
return "␣";
|
||||
}
|
||||
|
||||
if (number == 1) {
|
||||
return ",:-)";
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ArrayList<String> chars = language.getKeyCharacters(number, false);
|
||||
for (int i = 0; i < 5 && i < chars.size(); i++) {
|
||||
sb.append(
|
||||
textCase == InputMode.CASE_UPPER ? chars.get(i).toUpperCase(language.getLocale()) : chars.get(i)
|
||||
);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package io.github.sspanak.tt9.ui.main.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||
|
||||
public class SoftPunctuationKey extends SoftKey {
|
||||
public SoftPunctuationKey(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SoftPunctuationKey(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SoftPunctuationKey(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
protected boolean handlePress(int keyId) {
|
||||
if (tt9 == null) {
|
||||
Logger.w(getClass().getCanonicalName(), "Traditional T9 handler is not set. Ignoring key press.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tt9.getSettings().getInputMode() == InputMode.MODE_123) {
|
||||
if (keyId == R.id.soft_key_punctuation_1) return tt9.onOtherKey(KeyEvent.KEYCODE_STAR);
|
||||
if (keyId == R.id.soft_key_punctuation_2) return tt9.onOtherKey(KeyEvent.KEYCODE_POUND);
|
||||
} else {
|
||||
if (keyId == R.id.soft_key_punctuation_1) return tt9.onText("!");
|
||||
if (keyId == R.id.soft_key_punctuation_2) return tt9.onText("?");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKeyNameLabel() {
|
||||
int keyId = getId();
|
||||
|
||||
if (tt9.getSettings().getInputMode() == InputMode.MODE_123) {
|
||||
if (keyId == R.id.soft_key_punctuation_1) return "✱";
|
||||
if (keyId == R.id.soft_key_punctuation_2) return "#";
|
||||
} else {
|
||||
if (keyId == R.id.soft_key_punctuation_1) return "!";
|
||||
if (keyId == R.id.soft_key_punctuation_2) return "?";
|
||||
}
|
||||
|
||||
return "PUNC";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.sspanak.tt9.ui.bottom;
|
||||
package io.github.sspanak.tt9.ui.tray;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.sspanak.tt9.ui.bottom;
|
||||
package io.github.sspanak.tt9.ui.tray;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
|
|
@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import java.util.List;
|
||||
|
||||
public class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.ViewHolder> {
|
||||
private final SuggestionsBar suggestionsBar;
|
||||
private final int layout;
|
||||
private final int textViewResourceId;
|
||||
private final LayoutInflater mInflater;
|
||||
|
|
@ -23,7 +24,8 @@ public class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.
|
|||
private int selectedIndex = 0;
|
||||
|
||||
|
||||
public SuggestionsAdapter(Context context, int layout, int textViewResourceId, List<String> suggestions) {
|
||||
public SuggestionsAdapter(Context context, SuggestionsBar suggestionBar, int layout, int textViewResourceId, List<String> suggestions) {
|
||||
this.suggestionsBar = suggestionBar;
|
||||
this.layout = layout;
|
||||
this.textViewResourceId = textViewResourceId;
|
||||
this.mInflater = LayoutInflater.from(context);
|
||||
|
|
@ -43,6 +45,7 @@ public class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.
|
|||
holder.suggestionItem.setText(mSuggestions.get(position));
|
||||
holder.suggestionItem.setTextColor(colorDefault);
|
||||
holder.suggestionItem.setBackgroundColor(selectedIndex == position ? colorHighlight : Color.TRANSPARENT);
|
||||
holder.suggestionItem.setOnClickListener(v -> suggestionsBar.onItemClick(holder.getAdapterPosition()));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.sspanak.tt9.ui.bottom;
|
||||
package io.github.sspanak.tt9.ui.tray;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
|
@ -16,7 +16,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
import io.github.sspanak.tt9.ime.TraditionalT9;
|
||||
|
||||
public class SuggestionsBar {
|
||||
private final List<String> suggestions = new ArrayList<>();
|
||||
|
|
@ -24,14 +24,14 @@ public class SuggestionsBar {
|
|||
private boolean isDarkThemeEnabled = false;
|
||||
|
||||
private final RecyclerView mView;
|
||||
private final SettingsStore settings;
|
||||
private final TraditionalT9 tt9;
|
||||
private SuggestionsAdapter mSuggestionsAdapter;
|
||||
|
||||
|
||||
public SuggestionsBar(SettingsStore settings, View mainView) {
|
||||
public SuggestionsBar(TraditionalT9 tt9, View mainView) {
|
||||
super();
|
||||
|
||||
this.settings = settings;
|
||||
this.tt9 = tt9;
|
||||
|
||||
mView = mainView.findViewById(R.id.suggestions_bar);
|
||||
mView.setLayoutManager(new LinearLayoutManager(mainView.getContext(), RecyclerView.HORIZONTAL,false));
|
||||
|
|
@ -45,8 +45,8 @@ public class SuggestionsBar {
|
|||
private void configureAnimation() {
|
||||
DefaultItemAnimator animator = new DefaultItemAnimator();
|
||||
|
||||
int translateDuration = settings.getSuggestionTranslateAnimationDuration();
|
||||
int selectDuration = settings.getSuggestionSelectAnimationDuration();
|
||||
int translateDuration = tt9.getSettings().getSuggestionTranslateAnimationDuration();
|
||||
int selectDuration = tt9.getSettings().getSuggestionSelectAnimationDuration();
|
||||
|
||||
animator.setMoveDuration(selectDuration);
|
||||
animator.setChangeDuration(translateDuration);
|
||||
|
|
@ -60,13 +60,14 @@ public class SuggestionsBar {
|
|||
private void initDataAdapter(Context context) {
|
||||
mSuggestionsAdapter = new SuggestionsAdapter(
|
||||
context,
|
||||
R.layout.suggestion_list_view,
|
||||
this,
|
||||
tt9.getSettings().getShowSoftNumpad() ? R.layout.suggestion_list_numpad : R.layout.suggestion_list,
|
||||
R.id.suggestion_list_item,
|
||||
suggestions
|
||||
);
|
||||
mView.setAdapter(mSuggestionsAdapter);
|
||||
|
||||
setDarkTheme(settings.getDarkTheme());
|
||||
setDarkTheme(tt9.getSettings().getDarkTheme());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -213,4 +214,14 @@ public class SuggestionsBar {
|
|||
|
||||
setBackground(newSuggestions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* onItemClick
|
||||
* Passes through suggestion selected using the touchscreen.
|
||||
*/
|
||||
public void onItemClick(int position) {
|
||||
selectedIndex = position;
|
||||
tt9.onOK();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue