Several changes. UI revamp, added a UX document explaining the choices, misc. bugfixes, double click to toggle meta key.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# Titanboard user experience
|
||||
|
||||
Keyboards are an integral part of the functioning of any computer, including the smartphone. Care
|
||||
should then be taken to design keyboards to be as intuitive, easy to use, and unobstructed as
|
||||
possible.
|
||||
|
||||
The Unihertz Titan already has a physical keyboard. It's poorly designed in many aspects. Mainly, it
|
||||
isn't very reminiscent of a regular QWERTY keyboard which people are used to:
|
||||
|
||||
1. The worst part is arguably the shift and alt keys being at the top of the keyboard, instead of on
|
||||
the bottom.
|
||||
2. The spacebar key is on the same row as the bottom QWERTY alphabet keys.
|
||||
3. The numerical keys, which are some of the most commonly used keys in phones (passwords, PIN
|
||||
codes)
|
||||
and in regular language are right below the alt key, which makes typing them very awkward. One
|
||||
could
|
||||
also argue that the numpad is in a "reversed" layout, but this is consistent with the usual
|
||||
layout
|
||||
found on phones.
|
||||
4. It lacks some commonly used symbols: ';', '&', '=', '<', '>', '$'. Other than that, it lacks some
|
||||
other
|
||||
symbols from regular QWERTY keyboards.
|
||||
5. There is no extra alt-gr key to type in localized letters.
|
||||
6. In my opinion, it also lacks a control key, but that is unnecessary on smartphones, unless you
|
||||
use the terminal or modify spreadsheets. However, providing appropriate UI elements for the
|
||||
actions
|
||||
possible on regular computers should be the responsibility of the application. Smartphones are
|
||||
simply different devices, with smaller screens and less space for complexity.
|
||||
7. Emojis.
|
||||
|
||||
However, when designing a keyboard app for it one should already use all pre-existing quirks and
|
||||
decisions which materialized in the physical keyboard. For example, the alt key should be used for
|
||||
the alternate characters printed on the keyboard, the shift key should give us uppercase characters
|
||||
and so on.
|
||||
|
||||
# Solutions
|
||||
|
||||
Here are my solutions for the above listed problems:
|
||||
|
||||
1. This is solvable with shift/alt latching. This makes it so when you press these keys once, the
|
||||
next key pressed will be either uppercased or the alternate key visible on the physical button.
|
||||
Additionally, the shift and alt key can be toggled by double-clicking in a fast succession,
|
||||
similar
|
||||
to regular visual keyboards on Android.
|
||||
2. This is unfortunately unsolvable with software.
|
||||
3. See 1., latching the keys solves this issue.
|
||||
4. This requires a visual keyboard, the UI of which is discussed later. Why does this require a
|
||||
visual keyboard, and not some alternate "symbol" mode, perhaps accessible with some keyboard
|
||||
shortcuts? That would make the learning curve of the keyboard higher, and it would be less
|
||||
intuitive. One should make sure not to complicate the physical keyboard any more than it already
|
||||
is,
|
||||
and use all of its idiosyncrasies but add no more.
|
||||
5. Similar to regular visual keyboards on Android, a diacritic/alternate key is assigned to each key
|
||||
according to the language. Long pressing a key will print it. Ideally, there should also be a
|
||||
popup
|
||||
menu allowing for several characters under one key. Cycling through the keys would use the
|
||||
shift/alt
|
||||
keys (so that no matter what key you use, the other hand can cycle through) and selecting it
|
||||
would
|
||||
happen on release of the key.
|
||||
6. I have no plans on adding a control key, as it is largely unnecessary on phones.
|
||||
7. I do not really use emojis, but I am aware that they are used by the majority of people. Most
|
||||
IM apps provide their own emoji list, so this isn't a priority. Designing a good UX for it is
|
||||
also
|
||||
hard on the Titan.
|
||||
|
||||
Taking into account typing ergonomics and pre-existing UX choices of other keyboard designers, this
|
||||
input method should make typing on the Unihertz Titan a pleasant experience. Other than fixing the
|
||||
fundamental issues, the keyboard should also:
|
||||
|
||||
1. Provide a way of managing the clipboard, or at the very least pasting things with an UI element.
|
||||
This is to make the usage intuitive and not introduce any additional keys such as a ctrl key.
|
||||
2. Autocorrect, language switching, auto caps and periods.
|
||||
3. Some minimal customization, but just enough not to overwhelm the user.
|
||||
|
||||
For the Unihertz Titan Pocket/Slim, the sym key will be used as the alt+gr key, providing diacritics
|
||||
and such. This is because in the official documentation and implementation the alt key is designed
|
||||
for the alternate keys printed on the buttons.
|
||||
|
||||
# User interface
|
||||
|
||||
Google recommends that visual keyboards should not occupy more than half of the screen estate.
|
||||
However, most phones have long, vertical screens, which means essentially that the keyboard will
|
||||
be a square, and so will the remaining view. On the Titan, the screen is already square, so the
|
||||
remaining space will be proportionally much smaller. Therefore, the keyboard should occupy very
|
||||
minimal screen estate, as to allow the user to see the application they are interacting with.
|
||||
|
||||
For this reason, I make sure the keyboard occupies 20–25 % screen estate. This is enough for a
|
||||
symbol bar and a suggestions bar, as well as some additional buttons such as language change or
|
||||
settings.
|
||||
|
||||
Both the symbol bar and suggestions bar are scrollable. The suggestions being scrollable is a
|
||||
feature on some visual keyboards already, however, I have come to the decision to make this for the
|
||||
symbol bar as well. An expandable menu would make the keyboard occupy half, or more of the screen.
|
||||
A scrollable bar is more intuitive and can hold an indefinite amount of characters, possibly also
|
||||
user-defined.
|
||||
|
||||
A simple divider between the suggestion and symbol bar is added to avoid accidental clicks.
|
||||
+2
-1
@@ -11,7 +11,7 @@ android {
|
||||
minSdk 19
|
||||
targetSdk 34
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
versionName "0.1"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -28,3 +28,4 @@ android {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,26 +3,36 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
tools:targetApi="31">
|
||||
<service
|
||||
android:name="TitanboardIME"
|
||||
android:name=".IME"
|
||||
android:directBootAware="true"
|
||||
android:exported="false"
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/ime_label"
|
||||
android:permission="android.permission.BIND_INPUT_METHOD">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.view.InputMethod" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.view.im"
|
||||
android:resource="@xml/method" />
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".PreferencesActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/preferences_label">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
+45
-30
@@ -1,19 +1,42 @@
|
||||
/*
|
||||
* An input method for the Unihertz Titan/physical keyboard phones
|
||||
* Copyright (C) 2024 Emil Kosz
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package cfpi_fpsi.eu.titanboard;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.os.Build;
|
||||
import android.os.Vibrator;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class TitanboardIME extends InputMethodService {
|
||||
private final MetaKeyManager mMetaKeyManager = new MetaKeyManager();
|
||||
public class IME extends InputMethodService {
|
||||
private MetaKeyManager mMetaKeyManager;
|
||||
private Keymap mKeymap;
|
||||
|
||||
private LinearLayout mInputView;
|
||||
private Vibrator mVibrator;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -23,6 +46,8 @@ public class TitanboardIME extends InputMethodService {
|
||||
// Unihertz phones: the Pocket and the Slim. However, as I don't have them, it'll be guesswork.
|
||||
// Sym key support is already implemented.
|
||||
mKeymap = new Keymap(getResources().getXml(R.xml.keymap_titan));
|
||||
mVibrator = (Vibrator) getBaseContext().getSystemService(VIBRATOR_SERVICE);
|
||||
mMetaKeyManager = new MetaKeyManager(mVibrator);
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@@ -50,17 +75,20 @@ public class TitanboardIME extends InputMethodService {
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
// The back key behaviour is handled by the IMS already.
|
||||
// If the keyboard is visible, hide it, otherwise, let the system handle the key.
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
if (isInputViewShown()) {
|
||||
requestHideSelf(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// If it's the Enter key, we let the application do it's job.
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
return false;
|
||||
// If it's the backspace key, we remove one character.
|
||||
case KeyEvent.KEYCODE_DEL:
|
||||
conn.deleteSurroundingText(1, 0);
|
||||
mMetaKeyManager.adjustMetaStateAfterKeypress();
|
||||
if (conn.getSelectedText(0) == null) {
|
||||
conn.deleteSurroundingText(1, 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case KeyEvent.KEYCODE_SPACE:
|
||||
conn.commitText(" ", 1);
|
||||
@@ -68,18 +96,19 @@ public class TitanboardIME extends InputMethodService {
|
||||
return true;
|
||||
default:
|
||||
String key = getKey(keyCode);
|
||||
if (key != null) {
|
||||
conn.commitText(getKey(keyCode), 1);
|
||||
mMetaKeyManager.adjustMetaStateAfterKeypress();
|
||||
return true;
|
||||
} else {
|
||||
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
conn.commitText(getKey(keyCode), 1);
|
||||
mMetaKeyManager.adjustMetaStateAfterKeypress();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
mMetaKeyManager.updateMetaStateUp(keyCode);
|
||||
mMetaKeyManager.updateMetaStateUp(keyCode, event);
|
||||
updateMetaStateTextView();
|
||||
return false;
|
||||
}
|
||||
@@ -93,9 +122,6 @@ public class TitanboardIME extends InputMethodService {
|
||||
public void onFinishInput() {
|
||||
mMetaKeyManager.resetMetaKeyState();
|
||||
updateMetaStateTextView();
|
||||
mInputView.findViewById(R.id.extra_view).setVisibility(View.GONE);
|
||||
Button key = mInputView.findViewById(R.id.expand_button);
|
||||
key.setText("▲");
|
||||
}
|
||||
|
||||
private String getKey(int keyCode) {
|
||||
@@ -144,20 +170,9 @@ public class TitanboardIME extends InputMethodService {
|
||||
}
|
||||
}
|
||||
|
||||
/** @noinspection unused*/
|
||||
public void onInputViewExtraKeyClick(View v) {
|
||||
int visibility = mInputView.findViewById(R.id.extra_view).getVisibility();
|
||||
Button key = (Button) v;
|
||||
if (visibility == View.VISIBLE) {
|
||||
mInputView.findViewById(R.id.extra_view).setVisibility(View.GONE);
|
||||
key.setText("▲");
|
||||
} else {
|
||||
mInputView.findViewById(R.id.extra_view).setVisibility(View.VISIBLE);
|
||||
key.setText("▼");
|
||||
}
|
||||
}
|
||||
|
||||
/** @noinspection unused*/
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
public void onInputViewKeyClick(View v) {
|
||||
Button key = (Button) v;
|
||||
InputConnection conn = getCurrentInputConnection();
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* An input method for the Unihertz Titan/physical keyboard phones
|
||||
* Copyright (C) 2024 Emil Kosz
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package cfpi_fpsi.eu.titanboard;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
@@ -1,62 +1,93 @@
|
||||
/*
|
||||
* An input method for the Unihertz Titan/physical keyboard phones
|
||||
* Copyright (C) 2024 Emil Kosz
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package cfpi_fpsi.eu.titanboard;
|
||||
|
||||
import android.os.Vibrator;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
public class MetaKeyManager {
|
||||
private static final int mRepeatCount = 5;
|
||||
private MetaKey mMetaKeyPressed = MetaKey.NONE;
|
||||
private MetaKey mMetaKeyLatched = MetaKey.NONE;
|
||||
private MetaKey mMetaKeyLocked = MetaKey.NONE;
|
||||
private boolean mMetaKeyUsed = false;
|
||||
private long mKeyUpTime;
|
||||
|
||||
private Vibrator mVibrator;
|
||||
|
||||
public MetaKeyManager(Vibrator vibrator) {
|
||||
mVibrator = vibrator;
|
||||
}
|
||||
|
||||
public void updateMetaStateDown(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_ALT_LEFT:
|
||||
case KeyEvent.KEYCODE_ALT_RIGHT:
|
||||
updateMetaKeyDown(MetaKey.ALT, event.getRepeatCount());
|
||||
updateMetaKeyDown(MetaKey.ALT, event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_SHIFT_LEFT:
|
||||
case KeyEvent.KEYCODE_SHIFT_RIGHT:
|
||||
updateMetaKeyDown(MetaKey.SHIFT, event.getRepeatCount());
|
||||
updateMetaKeyDown(MetaKey.SHIFT, event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_SYM:
|
||||
updateMetaKeyDown(MetaKey.SYM, event.getRepeatCount());
|
||||
updateMetaKeyDown(MetaKey.SYM, event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void updateMetaKeyDown(MetaKey key, int repeatCount) {
|
||||
void updateMetaKeyDown(MetaKey key, KeyEvent event) {
|
||||
// Perhaps this function should be renamed to something...
|
||||
mMetaKeyUsed = false;
|
||||
mMetaKeyPressed = key;
|
||||
mMetaKeyLatched = mMetaKeyLocked != MetaKey.NONE ? mMetaKeyLocked : mMetaKeyLatched;
|
||||
mMetaKeyLocked = repeatCount > mRepeatCount ? key : MetaKey.NONE;
|
||||
mMetaKeyLocked = MetaKey.NONE;
|
||||
}
|
||||
|
||||
public void updateMetaStateUp(int keyCode) {
|
||||
public void updateMetaStateUp(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_ALT_LEFT:
|
||||
case KeyEvent.KEYCODE_ALT_RIGHT:
|
||||
updateMetaKeyUp(MetaKey.ALT);
|
||||
updateMetaKeyUp(MetaKey.ALT, event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_SHIFT_LEFT:
|
||||
case KeyEvent.KEYCODE_SHIFT_RIGHT:
|
||||
updateMetaKeyUp(MetaKey.SHIFT);
|
||||
updateMetaKeyUp(MetaKey.SHIFT, event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_SYM:
|
||||
updateMetaKeyUp(MetaKey.SYM);
|
||||
updateMetaKeyUp(MetaKey.SYM, event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMetaKeyUp(MetaKey key) {
|
||||
public void updateMetaKeyUp(MetaKey key, KeyEvent event) {
|
||||
if (event.getEventTime() - mKeyUpTime < 400 && mMetaKeyLatched == key) {
|
||||
mMetaKeyLocked = key;
|
||||
mVibrator.vibrate(30);
|
||||
}
|
||||
|
||||
mKeyUpTime = event.getEventTime();
|
||||
mMetaKeyPressed = MetaKey.NONE;
|
||||
if (mMetaKeyLatched == MetaKey.NONE && mMetaKeyLocked == MetaKey.NONE
|
||||
&& !mMetaKeyUsed) {
|
||||
if (mMetaKeyLatched == MetaKey.NONE && mMetaKeyLocked == MetaKey.NONE && !mMetaKeyUsed) {
|
||||
mMetaKeyLatched = key;
|
||||
} else {
|
||||
mMetaKeyLatched = MetaKey.NONE;
|
||||
@@ -67,7 +98,9 @@ public class MetaKeyManager {
|
||||
if (mMetaKeyLatched != MetaKey.NONE) {
|
||||
mMetaKeyLatched = MetaKey.NONE;
|
||||
mMetaKeyUsed = false;
|
||||
} else if (mMetaKeyPressed != MetaKey.NONE) {
|
||||
}
|
||||
|
||||
if (mMetaKeyPressed != MetaKey.NONE) {
|
||||
mMetaKeyUsed = true;
|
||||
}
|
||||
}
|
||||
@@ -79,6 +112,11 @@ public class MetaKeyManager {
|
||||
mMetaKeyUsed = false;
|
||||
}
|
||||
|
||||
public void setMetaKeyLatched(MetaKey key) {
|
||||
resetMetaKeyState();
|
||||
mMetaKeyLatched = key;
|
||||
}
|
||||
|
||||
public MetaKey getMetaKeyPressed() {
|
||||
return mMetaKeyPressed;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* An input method for the Unihertz Titan/physical keyboard phones
|
||||
* Copyright (C) 2024 Emil Kosz
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package cfpi_fpsi.eu.titanboard;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class PreferencesActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.preferences);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="false">
|
||||
<shape>
|
||||
<solid android:color="#333" />
|
||||
<stroke android:width="1dp" android:color="#222" />
|
||||
<gradient android:angle="-45"
|
||||
android:startColor="#333"
|
||||
android:endColor="#222"/>
|
||||
<stroke android:width="1dp" android:color="#333" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_pressed="true">
|
||||
|
||||
@@ -14,264 +14,127 @@
|
||||
android:orientation="horizontal"
|
||||
android:visibility="visible">
|
||||
|
||||
<Button
|
||||
android:id="@+id/suggestion_1"
|
||||
style="@style/KeyboardCandidate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/suggestion_2"
|
||||
style="@style/KeyboardCandidate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/suggestion_3"
|
||||
style="@style/KeyboardCandidate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/state"
|
||||
style="@style/KeyboardState"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="aa"
|
||||
android:typeface="normal"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/extra_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<View
|
||||
style="@style/KeyboardDivider"
|
||||
android:layout_width="match_parent" />
|
||||
|
||||
<LinearLayout
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
android:layout_weight="1">
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="–"
|
||||
tools:ignore="HardcodedText" />
|
||||
android:orientation="horizontal" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="—"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="`"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="~"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="{"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="}"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="\\"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="π"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="≤"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="≥"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="^"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="["
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="]"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="|"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/KeyboardDivider"
|
||||
android:layout_width="match_parent" />
|
||||
|
||||
<LinearLayout
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="<"
|
||||
tools:ignore="HardcodedText" />
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text=">"
|
||||
tools:ignore="HardcodedText" />
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="<" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="="
|
||||
tools:ignore="HardcodedText" />
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=">" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="$"
|
||||
tools:ignore="HardcodedText" />
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="^" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="%"
|
||||
tools:ignore="HardcodedText" />
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="~" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text=";"
|
||||
tools:ignore="HardcodedText" />
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="\\" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="&"
|
||||
tools:ignore="HardcodedText" />
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="=" />
|
||||
|
||||
<!--suppress AndroidMissingOnClickHandler -->
|
||||
<Button
|
||||
android:id="@+id/expand_button"
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:onClick="onInputViewExtraKeyClick"
|
||||
android:text="▲ "
|
||||
tools:ignore="HardcodedText,OnClick" />
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="&" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=";" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="|" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="[" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="]" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="{" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="}" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="←" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="↑" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="↓" />
|
||||
|
||||
<Button
|
||||
style="@style/KeyboardKey"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="→" />
|
||||
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Automatic capitalization" />
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Automatic periods" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBox3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Autocorrect" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Time between clicks to detect double click" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekBar3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,4 +1,5 @@
|
||||
<resources>
|
||||
<string name="app_name">Titanboard</string>
|
||||
<string name="ime_label">Titanboard</string>
|
||||
<string name="preferences_label">Preferences</string>
|
||||
</resources>
|
||||
@@ -18,8 +18,7 @@
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:textAlignment">center</item>
|
||||
<item name="android:typeface">monospace</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:width">0px</item>
|
||||
<item name="android:layout_width">72dp</item>
|
||||
</style>
|
||||
|
||||
<style name="KeyboardStateLocked" parent="KeyboardState">
|
||||
@@ -31,8 +30,7 @@
|
||||
<item name="android:textColor">#FFF</item>
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:layout_marginTop">2dp</item>
|
||||
<item name="android:layout_weight">2.33</item>
|
||||
<item name="android:width">0px</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
</style>
|
||||
|
||||
<style name="KeyboardKey">
|
||||
@@ -40,8 +38,8 @@
|
||||
<item name="android:textColor">#FFF</item>
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:layout_marginTop">2dp</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_marginBottom">2dp</item>
|
||||
<item name="android:onClick">onInputViewKeyClick</item>
|
||||
<item name="android:width">0px</item>
|
||||
<item name="android:layout_width">72dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -3,7 +3,8 @@
|
||||
<key
|
||||
code="29"
|
||||
value="a"
|
||||
valueAlt="@" />
|
||||
valueAlt="@"
|
||||
valueSym="ą" />
|
||||
<key
|
||||
code="30"
|
||||
value="b"
|
||||
@@ -11,7 +12,8 @@
|
||||
<key
|
||||
code="31"
|
||||
value="c"
|
||||
valueAlt="," />
|
||||
valueAlt=","
|
||||
valueSym="ć" />
|
||||
<key
|
||||
code="32"
|
||||
value="d"
|
||||
@@ -19,7 +21,8 @@
|
||||
<key
|
||||
code="33"
|
||||
value="e"
|
||||
valueAlt="_" />
|
||||
valueAlt="_"
|
||||
valueSym="ę" />
|
||||
<key
|
||||
code="34"
|
||||
value="f"
|
||||
@@ -47,7 +50,8 @@
|
||||
<key
|
||||
code="40"
|
||||
value="l"
|
||||
valueAlt="6" />
|
||||
valueAlt="6"
|
||||
valueSym="ł" />
|
||||
<key
|
||||
code="41"
|
||||
value="m"
|
||||
@@ -55,11 +59,13 @@
|
||||
<key
|
||||
code="42"
|
||||
value="n"
|
||||
valueAlt="8" />
|
||||
valueAlt="8"
|
||||
valueSym="ń" />
|
||||
<key
|
||||
code="43"
|
||||
value="o"
|
||||
valueAlt="3" />
|
||||
valueAlt="3"
|
||||
valueSym="ó" />
|
||||
<key
|
||||
code="44"
|
||||
value="p"
|
||||
@@ -75,7 +81,8 @@
|
||||
<key
|
||||
code="47"
|
||||
value="s"
|
||||
valueAlt="'" />
|
||||
valueAlt="'"
|
||||
valueSym="ś" />
|
||||
<key
|
||||
code="48"
|
||||
value="t"
|
||||
@@ -95,7 +102,8 @@
|
||||
<key
|
||||
code="52"
|
||||
value="x"
|
||||
valueAlt="?" />
|
||||
valueAlt="?"
|
||||
valueSym="ź" />
|
||||
<key
|
||||
code="53"
|
||||
value="y"
|
||||
@@ -103,5 +111,6 @@
|
||||
<key
|
||||
code="54"
|
||||
value="z"
|
||||
valueAlt="!" />
|
||||
valueAlt="!"
|
||||
valueSym="ż" />
|
||||
</keymap>
|
||||
Reference in New Issue
Block a user