Strict inspection warnings fixed, slight restructurings

This commit is contained in:
2024-11-05 23:36:09 +01:00
parent 0445279671
commit 001ce70392
19 changed files with 393 additions and 354 deletions
+1
View File
@@ -1,3 +1,4 @@
<!--suppress IncorrectFormatting, LongLine -->
<profile version="1.0">
<option name="myName" value="Very strict" />
<inspection_tool class="AbsoluteAlignmentInUserInterface" enabled="true" level="WARNING" enabled_by_default="true" />
+3 -2
View File
@@ -11,14 +11,15 @@ android {
minSdk 3
targetSdk 35
versionCode 30
versionName "0.3"
versionName '0.3'
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
debug {
+2 -3
View File
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
@@ -48,7 +47,7 @@
</service>
<activity
android:name=".PreferenceActivity"
android:name=".SettingsActivity"
android:exported="false"
android:label="@string/preferences_name">
<intent-filter>
@@ -27,15 +27,17 @@ import android.os.Build;
import java.util.ArrayList;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ClipManager extends ArrayList<ClipData.Item>
implements ClipboardManager.OnPrimaryClipChangedListener {
public class ClipManager implements ClipboardManager.OnPrimaryClipChangedListener {
private final InputMethod mInputMethod;
private final ClipboardManager mClipboardManager;
private final ArrayList<ClipData.Item> mClips;
private long mLastCallbackTimestamp;
public ClipManager(Context context) {
mInputMethod = (InputMethod) context;
mClipboardManager = (ClipboardManager) mInputMethod.getSystemService(Context.CLIPBOARD_SERVICE);
mClips = new ArrayList<>(mInputMethod.mPreferenceHolder.getClipboardSize());
mClipboardManager = (ClipboardManager)
mInputMethod.getSystemService(Context.CLIPBOARD_SERVICE);
mClipboardManager.addPrimaryClipChangedListener(this);
}
@@ -43,29 +45,38 @@ public class ClipManager extends ArrayList<ClipData.Item>
public void onPrimaryClipChanged() {
// The callback can be launched several times at once, so we have to check if it isn't the
// same clipboard data.
if (System.currentTimeMillis() - mLastCallbackTimestamp < 100) {
return;
}
if (System.currentTimeMillis() - mLastCallbackTimestamp >= 100) {
mLastCallbackTimestamp = System.currentTimeMillis();
ClipData clip = mClipboardManager.getPrimaryClip();
mLastCallbackTimestamp = System.currentTimeMillis();
ClipData clip = mClipboardManager.getPrimaryClip();
if (clip != null) {
int itemCount = clip.getItemCount();
for (int i = 0; i < itemCount; ++i) {
ClipData.Item item = clip.getItemAt(i);
// We do not support non-text clips for now, so all we add are text clips.
if (item.getText() != null) {
mClips.add(0, item);
}
if (clip == null) {
return;
}
if (mClips.size() > mInputMethod.mPreferenceHolder.getClipboardSize()) {
mClips.remove(mClips.size() - 1);
}
}
for (int i = 0; i < clip.getItemCount(); ++i) {
ClipData.Item item = clip.getItemAt(i);
// We do not support non-text clips for now, so all we add are text clips.
if (item.getText() != null) {
add(0, item);
}
if (size() > mInputMethod.mPreferenceHolder.getClipboardSize()) {
remove(size() - 1);
mInputMethod.mInputView.setClips(mClips);
}
}
}
mInputMethod.mInputView.setClips(this);
public boolean isEmpty() {
return mClips.isEmpty();
}
public void removeItem(ClipData.Item item) {
mClips.remove(item);
}
public ClipData.Item getItemAt(int index) {
return mClips.get(index);
}
}
@@ -20,10 +20,14 @@ package eur.cfpifpsi.titanboard;
import java.util.ArrayList;
/**
* @noinspection AbstractClassWithOnlyOneDirectInheritor
*/
public abstract class Cycler implements Runnable {
private ArrayList<?> mList = new ArrayList<>();
private boolean mHasCycled = false;
private int mIndex = 0;
private static final int LIST_INITIAL_SIZE = 10;
private ArrayList<?> mList = new ArrayList<>(LIST_INITIAL_SIZE);
private boolean mHasCycled;
private int mIndex;
public int getIndex() {
return mIndex;
@@ -62,8 +66,7 @@ public abstract class Cycler implements Runnable {
onCycle();
}
public void onFirstCycle() {
}
public abstract void onFirstCycle();
public abstract void onCycle();
}
@@ -34,12 +34,6 @@ import java.util.ArrayList;
// TODO: Implement device recognition and a keymap picking based on it.
// UI: clicking on the text field shows the UI near the cursor (or in the center of the screen on
// older APIs. From that UI
// The candidates view, while not needing any inset computation, is really buggy, so I instead opt
// for using the input view and computing insets accordingly.
public class InputMethod extends InputMethodService {
public MetaKeyManager mMetaKeyManager;
public KeymapManager mKeymapManager;
@@ -47,8 +41,8 @@ public class InputMethod extends InputMethodService {
public ClipManager mClipManager;
public InputView mInputView;
public Vibrator mVibrator;
private int mCurrentCursorPosition;
private Handler mHandler;
int mCurrentCursorPosition;
Handler mHandler;
private Cycler mSymbolCycler;
@Override
@@ -61,7 +55,7 @@ public class InputMethod extends InputMethodService {
mClipManager = new ClipManager(this);
mHandler = new Handler(Looper.getMainLooper());
mSymbolCycler = new SymbolCycler();
mKeymapManager.cycleKeymaps(mPreferenceHolder.getSelectedKeymaps());
mKeymapManager.nextKeymap(mPreferenceHolder.getSelectedKeymaps());
}
@SuppressLint("InflateParams")
@@ -84,6 +78,9 @@ public class InputMethod extends InputMethodService {
return false;
}
/**
* @noinspection FeatureEnvy
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mMetaKeyManager.updateMetaStateDown(keyCode);
@@ -152,9 +149,9 @@ public class InputMethod extends InputMethodService {
}
@Override
public void onStartInput(EditorInfo info, boolean restarting) {
super.onStartInput(info, restarting);
mKeymapManager.checkCurrentKeymap(mPreferenceHolder.getSelectedKeymaps());
public void onStartInput(EditorInfo attribute, boolean restarting) {
super.onStartInput(attribute, restarting);
mKeymapManager.adjustCurrentKeymap(mPreferenceHolder.getSelectedKeymaps());
mInputView.updateStateText();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -170,7 +167,7 @@ public class InputMethod extends InputMethodService {
// Cyclers & runnables
private class SymbolCycler extends Cycler {
class SymbolCycler extends Cycler {
@Override
public void onCycle() {
mVibrator.vibrate(10);
@@ -183,7 +180,7 @@ public class InputMethod extends InputMethodService {
}
}
@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") //NON-NLS
@Override
public void onFirstCycle() {
mInputView.setSymbolsVisible(true);
@@ -26,6 +26,7 @@ import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
@@ -40,9 +41,8 @@ import java.util.ArrayList;
// TODO: programmatically scroll if current selected symbol is outside the visible space of the
// scrollview
public class InputView extends RelativeLayout {
protected final int[] BUTTON_STATE_TOGGLED = {R.attr.state_toggled};
public final class InputView extends RelativeLayout {
private final int[] buttonStateToggled = {R.attr.state_toggled};
private final InputMethod mInputMethod;
private final LinearLayout.LayoutParams mSymbolLayout;
private final LinearLayout.LayoutParams mClipLayout;
@@ -57,10 +57,10 @@ public class InputView extends RelativeLayout {
mInputMethod = (InputMethod) context;
mSymbolLayout = new LinearLayout.LayoutParams(
getResources().getDimensionPixelSize(R.dimen.symbol_width),
LinearLayout.LayoutParams.MATCH_PARENT, 0);
ViewGroup.LayoutParams.MATCH_PARENT, 0);
mClipLayout = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 0);
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
mSymbolLayout.setMargins(0, 0,
getResources().getDimensionPixelSize(R.dimen.default_space), 0);
@@ -78,7 +78,7 @@ public class InputView extends RelativeLayout {
mSymbolList = findViewById(R.id.symbol_list);
mStateButton = findViewById(R.id.state_button);
mClipboardButton = findViewById(R.id.clipboard_button);
ScrollView mClipboardView = (ScrollView) inflate(this.getContext(), R.layout.clipboard, null);
ScrollView mClipboardView = (ScrollView) inflate(getContext(), R.layout.clipboard, null);
mClipList = mClipboardView.findViewById(R.id.clip_list);
mClipboardPopup = new PopupWindow(mClipboardView,
getResources().getDimensionPixelSize(R.dimen.clipboard_popup_width),
@@ -90,13 +90,14 @@ public class InputView extends RelativeLayout {
if (metaManager.getAlt().isActive()) {
metaManager.getAlt().resetState();
} else {
mInputMethod.mKeymapManager.cycleKeymaps(mInputMethod.mPreferenceHolder.getSelectedKeymaps());
mInputMethod.mKeymapManager
.nextKeymap(mInputMethod.mPreferenceHolder.getSelectedKeymaps());
}
updateStateText();
});
mStateButton.setOnLongClickListener(button -> {
Intent intent = new Intent(getContext(), PreferenceActivity.class);
Intent intent = new Intent(getContext(), SettingsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
// For some reason, this vibrates by default, so we don't need to call the vibrator.
@@ -106,7 +107,7 @@ public class InputView extends RelativeLayout {
mClipboardButton.setOnClickListener(button -> {
mClipboardPopup.showAsDropDown(mClipboardButton,
-(mClipboardPopup.getWidth() - mClipboardButton.getWidth()) / 2, 0);
mClipboardButton.setImageState(BUTTON_STATE_TOGGLED, true);
mClipboardButton.setImageState(buttonStateToggled, true);
});
mClipboardButton.setOnLongClickListener(button -> {
@@ -114,13 +115,16 @@ public class InputView extends RelativeLayout {
if (!mInputMethod.mClipManager.isEmpty()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mInputMethod.getCurrentInputConnection()
.commitText(mInputMethod.mClipManager.get(0).getText(), 1);
.commitText(mInputMethod.mClipManager.getItemAt(0).getText(), 1);
}
return true;
});
}
/**
* @noinspection FeatureEnvy
*/
public void updateStateText() {
MetaKeyManager metaManager = mInputMethod.mMetaKeyManager;
KeymapManager keymapManager = mInputMethod.mKeymapManager;
@@ -142,26 +146,27 @@ public class InputView extends RelativeLayout {
}
if (metaManager.getShift().isPressed() || metaManager.getShift().isLocked()) {
mStateButton.setText(keymapManager.getCurrentKeymap().getLockedCode());
mStateButton.setText(keymapManager.getCurrentLockedLabel());
return;
}
if (metaManager.getShift().isLatched()) {
mStateButton.setText(keymapManager.getCurrentKeymap().getLatchedCode());
mStateButton.setText(keymapManager.getCurrentLatchedLabel());
return;
}
mStateButton.setText(keymapManager.getCurrentKeymap().getCode());
mStateButton.setText(keymapManager.getCurrentLabel());
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void setClips(ArrayList<ClipData.Item> items) {
for (int i = 0; i < items.size(); ++i) {
public void setClips(ArrayList<? extends ClipData.Item> items) {
int size = items.size();
for (int i = 0; i < size; ++i) {
ClipData.Item item = items.get(i);
LinearLayout clipView = (LinearLayout) mClipList.getChildAt(i);
if (clipView == null) {
clipView = (LinearLayout) inflate(this.getContext(), R.layout.clip, null);
clipView = (LinearLayout) inflate(getContext(), R.layout.clip, null);
clipView.setTag(i);
clipView.setLayoutParams(mClipLayout);
mClipList.addView(clipView);
@@ -169,15 +174,13 @@ public class InputView extends RelativeLayout {
Button pasteButton = clipView.findViewById(R.id.paste_button);
pasteButton.setText(item.getText());
// Required for marquee to work.
pasteButton.setSelected(true);
pasteButton.setOnClickListener(button -> mInputMethod.getCurrentInputConnection()
.commitText(item.getText(), 1));
LinearLayout finalClipView = clipView;
clipView.findViewById(R.id.button_delete).setOnClickListener(button -> {
mClipList.removeView(finalClipView);
mInputMethod.mClipManager.remove(item);
mInputMethod.mClipManager.removeItem(item);
});
}
}
@@ -186,12 +189,16 @@ public class InputView extends RelativeLayout {
mSymbolList.setVisibility(state ? VISIBLE : GONE);
}
/**
* @noinspection MethodWithMultipleLoops
*/
public void setSymbols(ArrayList<String> strings) {
for (int i = 0; i < strings.size(); ++i) {
int size = strings.size();
for (int i = 0; i < size; ++i) {
TextView tv = (TextView) mSymbolList.getChildAt(i);
if (tv == null) {
tv = (TextView) inflate(this.getContext(), R.layout.symbol, null);
tv = (TextView) inflate(getContext(), R.layout.symbol, null);
tv.setTag(i);
tv.setLayoutParams(mSymbolLayout);
mSymbolList.addView(tv);
@@ -201,13 +208,15 @@ public class InputView extends RelativeLayout {
tv.setVisibility(VISIBLE);
}
for (int i = strings.size(); i < mSymbolList.getChildCount(); ++i) {
int childCount = mSymbolList.getChildCount();
for (int i = size; i < childCount; ++i) {
mSymbolList.getChildAt(i).setVisibility(GONE);
}
}
public void setSelectedSymbol(int index) {
for (int i = 0; i < mSymbolList.getChildCount(); ++i) {
int childCount = mSymbolList.getChildCount();
for (int i = 0; i < childCount; ++i) {
TextView tv = (TextView) mSymbolList.getChildAt(i);
if (i == index) {
@@ -220,7 +229,7 @@ public class InputView extends RelativeLayout {
// keymap that has a lot of symbols under one key, and also this is a safeguard for
// smaller screen sizes.
hsw.smoothScrollTo(tv.getRight()
+ getResources().getDimensionPixelSize(R.dimen.default_space) * 2
+ (getResources().getDimensionPixelSize(R.dimen.default_space) << 1)
- hsw.getRight(), 0);
} else {
tv.setPressed(false);
@@ -26,98 +26,129 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
import java.util.List;
public class KeymapManager {
private static final String TAG_KEY = "key"; //NON-NLS
private static final String TAG_KEYMAP = "keymap"; //NON-NLS
private static final String TAG_BASE = "base"; //NON-NLS
private static final String TAG_ALT = "alt"; //NON-NLS
private static final String TAG_SHIFT = "shift"; //NON-NLS
private static final String ATTR_CODE = "code"; //NON-NLS
private static final String ATTR_VALUE = "value"; //NON-NLS
private static final String ATTR_LABEL = "label"; //NON-NLS
private static final String ATTR_LATCHED_LABEL = "latched-label"; //NON-NLS
private static final String ATTR_LOCKED_LABEL = "locked-label"; //NON-NLS
private final Context mContext;
private final HashMap<String, Keymap> mKeymaps = new HashMap<>();
public Keymap mCurrentKeymap;
private final HashMap<String, Keymap> mKeymaps;
private Keymap mCurrentKeymap;
public KeymapManager(Context context) {
mContext = context;
mKeymaps = new HashMap<>(context.getResources()
.getStringArray(R.array.preferences_keymaps).length);
loadDeviceKeymaps(context.getResources().getXml(R.xml.keymaps_titan));
}
/**
* @noinspection BreakStatement
*/
private void loadDeviceKeymaps(XmlPullParser parser) {
Keymap keymap = null;
int code = 0;
String name = null, latchedName = null, lockedName = null;
String label = null, latchedLabel = null, lockedLabel = null;
Key key = null;
try {
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
int type = parser.getEventType();
while (true) {
int eventType;
// We encounter a new keymap.
if (type == XmlPullParser.START_TAG && parser.getName().equals("keymap")) {
keymap = new Keymap();
for (int i = 0; i < parser.getAttributeCount(); ++i) {
switch (parser.getAttributeName(i)) {
case "name":
name = parser.getAttributeValue(i);
break;
case "latched-name":
latchedName = parser.getAttributeValue(i);
break;
case "locked-name":
lockedName = parser.getAttributeValue(i);
break;
}
try {
eventType = parser.getEventType();
} catch (XmlPullParserException e) {
break;
}
if (eventType == XmlPullParser.END_DOCUMENT) break;
// We encounter a new keymap.
if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TAG_KEYMAP)) {
keymap = new Keymap();
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; ++i) {
switch (parser.getAttributeName(i)) {
case ATTR_LABEL:
label = parser.getAttributeValue(i);
break;
case ATTR_LATCHED_LABEL:
latchedLabel = parser.getAttributeValue(i);
break;
case ATTR_LOCKED_LABEL:
lockedLabel = parser.getAttributeValue(i);
break;
}
} else if (type == XmlPullParser.END_TAG && parser.getName().equals("keymap")
&& keymap != null) {
keymap.setStrings(name, latchedName, lockedName);
mKeymaps.put(name, keymap);
}
} else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG_KEYMAP)
&& keymap != null) {
keymap.code = label;
keymap.latchedCode = latchedLabel;
keymap.lockedCode = lockedLabel;
mKeymaps.put(label, keymap);
}
// We got a new key.
if (type == XmlPullParser.START_TAG && parser.getName().equals("key")) {
key = new Key();
for (int i = 0; i < parser.getAttributeCount(); ++i) {
if (parser.getAttributeName(i).equals("code")) {
code = Integer.parseInt(parser.getAttributeValue(i));
}
// We got a new key.
if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TAG_KEY)) {
key = new Key();
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; ++i) {
if (parser.getAttributeName(i).equals(ATTR_CODE)) {
code = Integer.parseInt(parser.getAttributeValue(i));
}
} else if (type == XmlPullParser.END_TAG && parser.getName().equals("key")
&& keymap != null) {
keymap.put(code, key);
}
} else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG_KEY)
&& keymap != null) {
keymap.put(code, key);
}
// We encountered a base value.
if (type == XmlPullParser.START_TAG && parser.getName().equals("base")) {
for (int i = 0; i < parser.getAttributeCount(); ++i) {
if (parser.getAttributeName(i).startsWith("value") && key != null) {
key.getSymbolsBase().add(parser.getAttributeValue(i));
}
// We encountered a base value.
if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TAG_BASE)) {
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; ++i) {
if (parser.getAttributeName(i).startsWith(ATTR_VALUE) && key != null) {
key.symbolsBase.add(parser.getAttributeValue(i));
}
}
// We encountered a shift value.
if (type == XmlPullParser.START_TAG && parser.getName().equals("shift")) {
for (int i = 0; i < parser.getAttributeCount(); ++i) {
if (parser.getAttributeName(i).startsWith("value") && key != null) {
key.getSymbolsShift().add(parser.getAttributeValue(i));
}
}
// We encountered a shift value.
if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TAG_SHIFT)) {
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; ++i) {
if (parser.getAttributeName(i).startsWith(ATTR_VALUE) && key != null) {
key.symbolsShift.add(parser.getAttributeValue(i));
}
}
// We encountered an alt value.
if (type == XmlPullParser.START_TAG && parser.getName().equals("alt")) {
for (int i = 0; i < parser.getAttributeCount(); ++i) {
if (parser.getAttributeName(i).startsWith("value") && key != null) {
key.getSymbolsAlt().add(parser.getAttributeValue(i));
}
}
// We encountered an alt value.
if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TAG_ALT)) {
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; ++i) {
if (parser.getAttributeName(i).startsWith(ATTR_VALUE) && key != null) {
key.symbolsAlt.add(parser.getAttributeValue(i));
}
}
}
try {
parser.next();
} catch (XmlPullParserException | IOException e) {
break;
}
} catch (XmlPullParserException | IOException e) {
throw new RuntimeException(e);
}
}
public ArrayList<String> getSymbols(int keyCode, MetaKeyManager metaKeyManager) {
ArrayList<String> alt = getCurrentKeymap().getSymbolsAlt(keyCode);
ArrayList<String> shift = getCurrentKeymap().getSymbolsShift(keyCode);
ArrayList<String> alt = mCurrentKeymap.getSymbolsAlt(keyCode);
ArrayList<String> shift = mCurrentKeymap.getSymbolsShift(keyCode);
if (metaKeyManager.getAlt().isActive() && alt != null && !alt.isEmpty()) {
return alt;
@@ -127,86 +158,83 @@ public class KeymapManager {
return shift;
}
return getCurrentKeymap().getSymbolsBase(keyCode);
return mCurrentKeymap.getSymbolsBase(keyCode);
}
public void checkCurrentKeymap(ArrayList<String> selectedKeymaps) {
if (!selectedKeymaps.contains(mCurrentKeymap.mCode)) {
cycleKeymaps(selectedKeymaps);
public void adjustCurrentKeymap(List<String> selectedKeymaps) {
if (!selectedKeymaps.contains(mCurrentKeymap.code)) {
nextKeymap(selectedKeymaps);
}
}
public void cycleKeymaps(ArrayList<String> selectedKeymaps) {
if (selectedKeymaps.isEmpty() || mCurrentKeymap == null) {
mCurrentKeymap = mKeymaps.get(mContext.getResources().getString(R.string.keymap_default));
return;
public void nextKeymap(List<String> selectedKeymaps) {
if (!selectedKeymaps.isEmpty() && mCurrentKeymap != null) {
int index = selectedKeymaps.indexOf(mCurrentKeymap.code);
index++;
if (index == selectedKeymaps.size()) {
index = 0;
}
mCurrentKeymap = mKeymaps.get(selectedKeymaps.get(index));
} else {
mCurrentKeymap =
mKeymaps.get(mContext.getResources().getString(R.string.keymap_default));
}
int index = selectedKeymaps.indexOf(mCurrentKeymap.mCode);
index++;
if (index == selectedKeymaps.size()) {
index = 0;
}
mCurrentKeymap = mKeymaps.get(selectedKeymaps.get(index));
}
public Keymap getCurrentKeymap() {
return mCurrentKeymap;
public String getCurrentLabel() {
return mCurrentKeymap.code;
}
public static class Keymap extends HashMap<Integer, Key> {
private String mCode; // A two-letter code for when the keyboard is lowercase.
private String mLatchedCode; // Above, but when the keyboard is shift latched.
private String mLockedCode; // Above, but when the shift is locked/pressed.
public String getCurrentLatchedLabel() {
return mCurrentKeymap.latchedCode;
}
public String getCode() {
return mCode;
}
public String getCurrentLockedLabel() {
return mCurrentKeymap.lockedCode;
}
public String getLatchedCode() {
return mLatchedCode;
}
public String getLockedCode() {
return mLockedCode;
}
public void setStrings(String code, String latched, String locked) {
mCode = code;
mLatchedCode = latched;
mLockedCode = locked;
}
static class Keymap {
private final static int INITIAL_KEYMAP_SIZE = 64; // Enough for just about any QWERTY.
private final HashMap<Integer, Key> mMap = new HashMap<>(INITIAL_KEYMAP_SIZE);
String code;
String latchedCode;
String lockedCode;
/**
* @noinspection ReturnOfNull
*/
public ArrayList<String> getSymbolsBase(int keyCode) {
return get(keyCode) != null ? Objects.requireNonNull(get(keyCode)).getSymbolsBase() : null;
Key key = mMap.get(keyCode);
return key == null ? null : key.symbolsBase;
}
/**
* @noinspection ReturnOfNull
*/
public ArrayList<String> getSymbolsAlt(int keyCode) {
return get(keyCode) != null ? Objects.requireNonNull(get(keyCode)).getSymbolsAlt() : null;
Key key = mMap.get(keyCode);
return key == null ? null : key.symbolsAlt;
}
/**
* @noinspection ReturnOfNull
*/
public ArrayList<String> getSymbolsShift(int keyCode) {
return get(keyCode) != null ? Objects.requireNonNull(get(keyCode)).getSymbolsShift() : null;
Key key = mMap.get(keyCode);
return key == null ? null : key.symbolsShift;
}
public void put(Integer key, Key value) {
mMap.put(key, value);
}
}
public static class Key {
private final ArrayList<String> mSymbolsBase = new ArrayList<>();
private final ArrayList<String> mSymbolsShift = new ArrayList<>();
private final ArrayList<String> mSymbolsAlt = new ArrayList<>();
public ArrayList<String> getSymbolsBase() {
return mSymbolsBase;
}
public ArrayList<String> getSymbolsShift() {
return mSymbolsShift;
}
public ArrayList<String> getSymbolsAlt() {
return mSymbolsAlt;
}
static class Key {
private final static int INITIAL_SIZE = 6;
final ArrayList<String> symbolsBase = new ArrayList<>(INITIAL_SIZE);
final ArrayList<String> symbolsShift = new ArrayList<>(INITIAL_SIZE);
final ArrayList<String> symbolsAlt = new ArrayList<>(INITIAL_SIZE);
}
}
@@ -0,0 +1,65 @@
package eur.cfpifpsi.titanboard;
import android.view.KeyEvent;
public class MetaKey {
private static final long TOGGLE_DURATION = 400L;
private boolean mPressed;
private boolean mLatched;
private boolean mLocked;
private boolean mUsed;
private long mKeyUpTime;
public void onDown() {
mUsed = false;
mPressed = true;
// It's not true that mLocked is always true...
//noinspection ConstantValue
mLatched = mLocked ? mLocked : mLatched;
mLocked = false;
}
public void onUp(KeyEvent event) {
if (event.getEventTime() - mKeyUpTime < TOGGLE_DURATION) {
mLocked = true;
}
mKeyUpTime = event.getEventTime();
mPressed = false;
mLatched = !mLatched && !mLocked && !mUsed;
}
public void afterKeypress() {
if (mLatched) {
mLatched = false;
mUsed = false;
}
if (mPressed) {
mUsed = true;
}
}
public void resetState() {
mPressed = false;
mLatched = false;
mLocked = false;
mUsed = false;
}
public boolean isPressed() {
return mPressed;
}
public boolean isLatched() {
return mLatched;
}
public boolean isLocked() {
return mLocked;
}
public boolean isActive() {
return mPressed || mLocked || mLatched;
}
}
@@ -22,6 +22,7 @@ import android.os.Vibrator;
import android.view.KeyEvent;
public class MetaKeyManager {
private static final int TOGGLE_VIBRATION_TIME = 30;
public final MetaKey mShift = new MetaKey();
public final MetaKey mAlt = new MetaKey();
@@ -35,66 +36,44 @@ public class MetaKeyManager {
switch (keyCode) {
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
updateMetaKeyDown(mAlt);
mAlt.onDown();
break;
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_SHIFT_RIGHT:
updateMetaKeyDown(mShift);
mShift.onDown();
break;
default:
break;
}
}
/**
* @noinspection FeatureEnvy
*/
public void updateMetaStateUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
updateMetaKeyUp(mAlt, event);
mAlt.onUp(event);
if (mAlt.isLocked()) {
mVibrator.vibrate(TOGGLE_VIBRATION_TIME);
}
break;
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_SHIFT_RIGHT:
updateMetaKeyUp(mShift, event);
mShift.onUp(event);
if (mShift.isLocked()) {
mVibrator.vibrate(TOGGLE_VIBRATION_TIME);
}
break;
default:
break;
}
}
void updateMetaKeyDown(MetaKey key) {
// Perhaps this function should be renamed to something...
key.setUsed(false);
key.setPressed(true);
key.setLatched(key.isLocked() ? key.isLocked() : key.isLatched());
key.setLocked(false);
}
public void updateMetaKeyUp(MetaKey key, KeyEvent event) {
if (event.getEventTime() - key.getKeyUpTime() < 400) {
key.setLocked(true);
mVibrator.vibrate(30);
}
key.setKeyUpTime(event.getEventTime());
key.setPressed(false);
key.setLatched(!key.isLatched() && !key.isLocked() && !key.isUsed());
}
public void adjustMetaStateAfterKeypress() {
adjustMetaStateHelper(mShift);
adjustMetaStateHelper(mAlt);
}
public void adjustMetaStateHelper(MetaKey key) {
if (key.isLatched()) {
key.setLatched(false);
key.setUsed(false);
}
if (key.isPressed()) {
key.setUsed(true);
}
mShift.afterKeypress();
mAlt.afterKeypress();
}
public void resetMetaKeyState() {
@@ -109,63 +88,4 @@ public class MetaKeyManager {
public MetaKey getAlt() {
return mAlt;
}
public static class MetaKey {
private boolean mPressed = false;
private boolean mLatched = false;
private boolean mLocked = false;
private boolean mUsed = false;
private long mKeyUpTime;
public void resetState() {
setPressed(false);
setLatched(false);
setLocked(false);
setUsed(false);
}
public boolean isPressed() {
return mPressed;
}
public void setPressed(boolean state) {
mPressed = state;
}
public boolean isLatched() {
return mLatched;
}
public void setLatched(boolean state) {
mLatched = state;
}
public boolean isLocked() {
return mLocked;
}
public void setLocked(boolean state) {
mLocked = state;
}
public boolean isUsed() {
return mUsed;
}
public void setUsed(boolean state) {
mUsed = state;
}
public boolean isActive() {
return isPressed() || isLocked() || isLatched();
}
public long getKeyUpTime() {
return mKeyUpTime;
}
public void setKeyUpTime(long time) {
mKeyUpTime = time;
}
}
}
@@ -33,7 +33,7 @@ public class PreferenceHolder implements SharedPreferences.OnSharedPreferenceCha
private final Map<String, Object> mPreferenceValues;
private final Context mContext;
@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") //NON-NLS
public PreferenceHolder(Context context) {
mContext = context;
SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -55,13 +55,6 @@ public class PreferenceHolder implements SharedPreferences.OnSharedPreferenceCha
return value == null ? defaultValue : value;
}
// --Commented out by Inspection START (05.11.2024, 10:28):
// public boolean getFloatingInterface() {
// return (boolean) getOrDefault(mContext.getString(R.string.preferences_key_floating_interface),
// mContext.getResources().getBoolean(R.bool.floating_ui_default));
// }
// --Commented out by Inspection STOP (05.11.2024, 10:28)
public int getCycleSpeed() {
return Integer.parseInt((String)
getOrDefault(mContext.getString(R.string.preferences_key_cycle_speed),
@@ -74,11 +67,12 @@ public class PreferenceHolder implements SharedPreferences.OnSharedPreferenceCha
mContext.getString(R.string.clipboard_size_default)));
}
@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") //NON-NLS
public ArrayList<String> getSelectedKeymaps() {
Set<String> selectedKeymaps = (Set<String>)
getOrDefault(mContext.getString(R.string.preferences_key_keymaps),
mContext.getResources().getStringArray(R.array.preferences_keymap_defaults));
mContext.getResources()
.getStringArray(R.array.preferences_keymap_defaults));
return new ArrayList<>(selectedKeymaps);
}
}
@@ -19,11 +19,12 @@
package eur.cfpifpsi.titanboard;
import android.os.Bundle;
import android.preference.PreferenceActivity;
/**
* @noinspection deprecation
*/
public class PreferenceActivity extends android.preference.PreferenceActivity {
public class SettingsActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+2 -4
View File
@@ -16,13 +16,11 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<item app:state_toggled="true">
<shape android:shape="rectangle">
<solid android:color="@color/button_toggled" />
<corners android:bottomLeftRadius="@dimen/default_border_radius"
android:bottomRightRadius="@dimen/default_border_radius" />
<corners android:bottomLeftRadius="@dimen/default_border_radius" android:bottomRightRadius="@dimen/default_border_radius" />
</shape>
</item>
<item android:state_pressed="false">
+35 -22
View File
@@ -17,55 +17,68 @@
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <item android:top="11dp"-->
<!-- android:bottom="11dp"-->
<!-- android:right="14dp"-->
<!-- android:left="14dp">-->
<!-- <shape android:shape="rectangle">-->
<!-- <solid android:color="@android:color/darker_gray" />-->
<!-- </shape>-->
<!-- </item>-->
<!-- <item android:top="11dp"-->
<!-- android:bottom="11dp"-->
<!-- android:right="14dp"-->
<!-- android:left="14dp">-->
<!-- <shape android:shape="rectangle">-->
<!-- <solid android:color="@android:color/darker_gray" />-->
<!-- </shape>-->
<!-- </item>-->
<!-- Main board -->
<item android:top="12.5dp"
<item
android:bottom="11dp"
android:left="14dp"
android:right="14dp"
android:bottom="11dp">
android:top="12.5dp">
<shape android:shape="rectangle">
<stroke android:color="@color/text_color" android:width="2dp" />
<stroke
android:width="2dp"
android:color="@color/text_color" />
<corners android:radius="2dp" />
</shape>
</item>
<!-- The clip -->
<item android:top="11dp"
<item
android:bottom="32dp"
android:left="20dp"
android:right="20dp"
android:left="20dp">
android:top="11dp">
<shape android:shape="rectangle">
<solid android:color="@color/text_color"/>
<solid android:color="@color/text_color" />
<corners android:radius="2dp" />
</shape>
</item>
<!-- Squiggles -->
<item android:left="18dp"
android:right="18dp"
android:bottom="7dp">
<item
android:bottom="7dp"
android:left="18dp"
android:right="18dp">
<shape android:shape="line">
<stroke android:width="2dp" android:color="@color/text_color" />
<stroke
android:width="2dp"
android:color="@color/text_color" />
</shape>
</item>
<item android:left="18dp"
<item
android:left="18dp"
android:right="18dp"
android:top="2dp">
<shape android:shape="line">
<stroke android:width="2dp" android:color="@color/text_color" />
<stroke
android:width="2dp"
android:color="@color/text_color" />
</shape>
</item>
<item android:left="18dp"
<item
android:left="18dp"
android:right="22dp"
android:top="11dp">
<shape android:shape="line">
<stroke android:width="2dp" android:color="@color/text_color" />
<stroke
android:width="2dp"
android:color="@color/text_color" />
</shape>
</item>
</layer-list>
+1 -2
View File
@@ -27,9 +27,8 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button_transparent"
android:ellipsize="marquee"
android:ellipsize="end"
android:gravity="center_vertical"
android:marqueeRepeatLimit="marquee_forever"
android:padding="@dimen/default_space"
android:singleLine="true"
android:text="@string/clip_default_text"
+27 -27
View File
@@ -21,9 +21,9 @@
<device-keymap-list>
<!-- English -->
<keymap
name="en"
latched-name="En"
locked-name="EN">
label="en"
latched-label="En"
locked-label="EN">
<key code="29"> <!-- A -->
<base value="a" />
<shift value="A" />
@@ -182,9 +182,9 @@
</keymap>
<!-- German -->
<keymap
name="de"
latched-name="De"
locked-name="DE">
label="de"
latched-label="De"
locked-label="DE">
<key code="29"> <!-- A -->
<base value="a" />
<base value="ä" />
@@ -355,9 +355,9 @@
</keymap>
<!-- French -->
<keymap
name="fr"
latched-name="Fr"
locked-name="FR">
label="fr"
latched-label="Fr"
locked-label="FR">
<key code="29"> <!-- A -->
<base value="a" />
<base value="à" />
@@ -552,9 +552,9 @@
</keymap>
<!-- Italian -->
<keymap
name="it"
latched-name="It"
locked-name="IT">
label="it"
latched-label="It"
locked-label="IT">
<key code="29"> <!-- A -->
<base value="a" />
<base value="à" />
@@ -735,9 +735,9 @@
</keymap>
<!-- Spanish -->
<keymap
name="es"
latched-name="Es"
locked-name="ES">
label="es"
latched-label="Es"
locked-label="ES">
<key code="29"> <!-- A -->
<base value="a" />
<base value="á" />
@@ -916,9 +916,9 @@
</keymap>
<!-- Polish -->
<keymap
name="pl"
latched-name="Pl"
locked-name="PL">
label="pl"
latched-label="Pl"
locked-label="PL">
<key code="29"> <!-- A -->
<base value="a" />
<base value="ą" />
@@ -1098,9 +1098,9 @@
<!-- Russian (Phonetic) -->
<!-- https://en.wikipedia.org/wiki/Phonetic_keyboard_layout#Russian_Phonetic_Keyboard_2.0 -->
<keymap
name="ru"
latched-name="Ru"
locked-name="RU">
label="ru"
latched-label="Ru"
locked-label="RU">
<key code="29"> <!-- A -->
<base value="a" />
<shift value="A" />
@@ -1278,9 +1278,9 @@
<!-- This differs a bit from the above layout, because the Titan keyboard already has : under
the alt key. The Q key is reused for diacritics. -->
<keymap
name="el"
latched-name="El"
locked-name="EL">
label="el"
latched-label="El"
locked-label="EL">
<key code="29"> <!-- A -->
<base value="α" />
<base value="ά" />
@@ -1471,9 +1471,9 @@
others are incomplete or not for *just* alphabetic QWERTY without extra keys. The characters
are sorted by their frequency in the language. https://en.wikipedia.org/wiki/Arabic_letter_frequency -->
<keymap
name="ar"
latched-name="Ar"
locked-name="AR">
label="ar"
latched-label="Ar"
locked-label="AR">
<key code="29"> <!-- A -->
<base value="ا" />
<base value="أ" />
+1 -1
View File
@@ -19,7 +19,7 @@
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:settingsActivity="eur.cfpifpsi.titanboard.PreferenceActivity">
android:settingsActivity="eur.cfpifpsi.titanboard.SettingsActivity">
<!-- Needed for higher Android version so it can be the only used input method.
None of these subtypes are actually used. -->
<subtype
+1 -1
View File
@@ -1,4 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.application) apply false
}
+4 -4
View File
@@ -2,9 +2,9 @@ pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
includeGroupByRegex('com\\.android.*')
includeGroupByRegex('com\\.google.*')
includeGroupByRegex('androidx.*')
}
}
mavenCentral()
@@ -19,5 +19,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "Titanboard"
rootProject.name = 'Titanboard'
include ':app'