mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
First KeyboardView implementation
This commit is contained in:
@@ -0,0 +1,906 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Google Inc. 2022 J-Jamet
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.kunzisoft.keepass.magikeyboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
||||
/**
|
||||
* Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
|
||||
* consists of rows of keys.
|
||||
* <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
|
||||
* <pre>
|
||||
* <Keyboard
|
||||
* android:keyWidth="%10p"
|
||||
* android:keyHeight="50px"
|
||||
* android:horizontalGap="2px"
|
||||
* android:verticalGap="2px" >
|
||||
* <Row android:keyWidth="32px" >
|
||||
* <Key android:keyLabel="A" />
|
||||
* ...
|
||||
* </Row>
|
||||
* ...
|
||||
* </Keyboard>
|
||||
* </pre>
|
||||
*
|
||||
* @attr ref android.R.styleable#Keyboard_keyWidth
|
||||
* @attr ref android.R.styleable#Keyboard_keyHeight
|
||||
* @attr ref android.R.styleable#Keyboard_horizontalGap
|
||||
* @attr ref android.R.styleable#Keyboard_verticalGap
|
||||
*/
|
||||
public class Keyboard {
|
||||
|
||||
static final String TAG = "Keyboard";
|
||||
|
||||
// Keyboard XML Tags
|
||||
private static final String TAG_KEYBOARD = "Keyboard";
|
||||
private static final String TAG_ROW = "Row";
|
||||
private static final String TAG_KEY = "Key";
|
||||
|
||||
public static final int EDGE_LEFT = 0x01;
|
||||
public static final int EDGE_RIGHT = 0x02;
|
||||
public static final int EDGE_TOP = 0x04;
|
||||
public static final int EDGE_BOTTOM = 0x08;
|
||||
|
||||
public static final int KEYCODE_SHIFT = -1;
|
||||
public static final int KEYCODE_MODE_CHANGE = -2;
|
||||
public static final int KEYCODE_CANCEL = -3;
|
||||
public static final int KEYCODE_DONE = -4;
|
||||
public static final int KEYCODE_DELETE = -5;
|
||||
public static final int KEYCODE_ALT = -6;
|
||||
|
||||
/**
|
||||
* Horizontal gap default for all rows
|
||||
*/
|
||||
private int mDefaultHorizontalGap;
|
||||
|
||||
/**
|
||||
* Default key width
|
||||
*/
|
||||
private int mDefaultWidth;
|
||||
|
||||
/**
|
||||
* Default key height
|
||||
*/
|
||||
private int mDefaultHeight;
|
||||
|
||||
/**
|
||||
* Default gap between rows
|
||||
*/
|
||||
private int mDefaultVerticalGap;
|
||||
|
||||
/**
|
||||
* Is the keyboard in the shifted state
|
||||
*/
|
||||
private boolean mShifted;
|
||||
|
||||
/**
|
||||
* Key instance for the shift key, if present
|
||||
*/
|
||||
private Key[] mShiftKeys = {null, null};
|
||||
|
||||
/**
|
||||
* Key index for the shift key, if present
|
||||
*/
|
||||
private int[] mShiftKeyIndices = {-1, -1};
|
||||
|
||||
/**
|
||||
* Total height of the keyboard, including the padding and keys
|
||||
*/
|
||||
private int mTotalHeight;
|
||||
|
||||
/**
|
||||
* Total width of the keyboard, including left side gaps and keys, but not any gaps on the
|
||||
* right side.
|
||||
*/
|
||||
private int mTotalWidth;
|
||||
|
||||
/**
|
||||
* List of keys in this keyboard
|
||||
*/
|
||||
private List<Key> mKeys;
|
||||
|
||||
/**
|
||||
* List of modifier keys such as Shift & Alt, if any
|
||||
*/
|
||||
private List<Key> mModifierKeys;
|
||||
|
||||
/**
|
||||
* Width of the screen available to fit the keyboard
|
||||
*/
|
||||
private int mDisplayWidth;
|
||||
|
||||
/**
|
||||
* Height of the screen
|
||||
*/
|
||||
private int mDisplayHeight;
|
||||
|
||||
/**
|
||||
* Keyboard mode, or zero, if none.
|
||||
*/
|
||||
private int mKeyboardMode;
|
||||
|
||||
// Variables for pre-computing nearest keys.
|
||||
|
||||
private static final int GRID_WIDTH = 10;
|
||||
private static final int GRID_HEIGHT = 5;
|
||||
private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
|
||||
private int mCellWidth;
|
||||
private int mCellHeight;
|
||||
private int[][] mGridNeighbors;
|
||||
private int mProximityThreshold;
|
||||
/**
|
||||
* Number of key widths from current touch point to search for nearest keys.
|
||||
*/
|
||||
private static float SEARCH_DISTANCE = 1.8f;
|
||||
|
||||
private ArrayList<Row> rows = new ArrayList<Row>();
|
||||
|
||||
/**
|
||||
* Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
|
||||
* Some of the key size defaults can be overridden per row from what the {@link Keyboard}
|
||||
* defines.
|
||||
*
|
||||
* @attr ref android.R.styleable#Keyboard_keyWidth
|
||||
* @attr ref android.R.styleable#Keyboard_keyHeight
|
||||
* @attr ref android.R.styleable#Keyboard_horizontalGap
|
||||
* @attr ref android.R.styleable#Keyboard_verticalGap
|
||||
* @attr ref android.R.styleable#Keyboard_Row_rowEdgeFlags
|
||||
* @attr ref android.R.styleable#Keyboard_Row_keyboardMode
|
||||
*/
|
||||
public static class Row {
|
||||
/**
|
||||
* Default width of a key in this row.
|
||||
*/
|
||||
public int defaultWidth;
|
||||
/**
|
||||
* Default height of a key in this row.
|
||||
*/
|
||||
public int defaultHeight;
|
||||
/**
|
||||
* Default horizontal gap between keys in this row.
|
||||
*/
|
||||
public int defaultHorizontalGap;
|
||||
/**
|
||||
* Vertical gap following this row.
|
||||
*/
|
||||
public int verticalGap;
|
||||
|
||||
ArrayList<Key> mKeys = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Edge flags for this row of keys. Possible values that can be assigned are
|
||||
* {@link Keyboard#EDGE_TOP EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
|
||||
*/
|
||||
public int rowEdgeFlags;
|
||||
|
||||
/**
|
||||
* The keyboard mode for this row
|
||||
*/
|
||||
public int mode;
|
||||
|
||||
private Keyboard parent;
|
||||
|
||||
public Row(Keyboard parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Row(Resources res, Keyboard parent, XmlResourceParser parser) {
|
||||
this.parent = parent;
|
||||
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||
R.styleable.Keyboard);
|
||||
defaultWidth = getDimensionOrFraction(a, R.styleable.Keyboard_keyWidth,
|
||||
parent.mDisplayWidth, parent.mDefaultWidth);
|
||||
defaultHeight = getDimensionOrFraction(a, R.styleable.Keyboard_keyHeight,
|
||||
parent.mDisplayHeight, parent.mDefaultHeight);
|
||||
defaultHorizontalGap = getDimensionOrFraction(a, R.styleable.Keyboard_horizontalGap,
|
||||
parent.mDisplayWidth, parent.mDefaultHorizontalGap);
|
||||
verticalGap = getDimensionOrFraction(a, R.styleable.Keyboard_verticalGap,
|
||||
parent.mDisplayHeight, parent.mDefaultVerticalGap);
|
||||
a.recycle();
|
||||
a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Row);
|
||||
rowEdgeFlags = a.getInt(R.styleable.Keyboard_Row_rowEdgeFlags, 0);
|
||||
mode = a.getResourceId(R.styleable.Keyboard_Row_keyboardMode, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for describing the position and characteristics of a single key in the keyboard.
|
||||
*
|
||||
* @attr ref R.styleable#Keyboard_keyWidth
|
||||
* @attr ref R.styleable#Keyboard_keyHeight
|
||||
* @attr ref R.styleable#Keyboard_horizontalGap
|
||||
* @attr ref R.styleable#Keyboard_Key_codes
|
||||
* @attr ref R.styleable#Keyboard_Key_keyIcon
|
||||
* @attr ref R.styleable#Keyboard_Key_keyLabel
|
||||
* @attr ref R.styleable#Keyboard_Key_iconPreview
|
||||
* @attr ref R.styleable#Keyboard_Key_isSticky
|
||||
* @attr ref R.styleable#Keyboard_Key_isRepeatable
|
||||
* @attr ref R.styleable#Keyboard_Key_isModifier
|
||||
* @attr ref R.styleable#Keyboard_Key_popupKeyboard
|
||||
* @attr ref R.styleable#Keyboard_Key_popupCharacters
|
||||
* @attr ref R.styleable#Keyboard_Key_keyOutputText
|
||||
* @attr ref R.styleable#Keyboard_Key_keyEdgeFlags
|
||||
*/
|
||||
public static class Key {
|
||||
/**
|
||||
* All the key codes (unicode or custom code) that this key could generate, zero'th
|
||||
* being the most important.
|
||||
*/
|
||||
public int[] codes;
|
||||
|
||||
/**
|
||||
* Label to display
|
||||
*/
|
||||
public CharSequence label;
|
||||
|
||||
/**
|
||||
* Icon to display instead of a label. Icon takes precedence over a label
|
||||
*/
|
||||
public Drawable icon;
|
||||
/**
|
||||
* Preview version of the icon, for the preview popup
|
||||
*/
|
||||
public Drawable iconPreview;
|
||||
/**
|
||||
* Width of the key, not including the gap
|
||||
*/
|
||||
public int width;
|
||||
/**
|
||||
* Height of the key, not including the gap
|
||||
*/
|
||||
public int height;
|
||||
/**
|
||||
* The horizontal gap before this key
|
||||
*/
|
||||
public int gap;
|
||||
/**
|
||||
* Whether this key is sticky, i.e., a toggle key
|
||||
*/
|
||||
public boolean sticky;
|
||||
/**
|
||||
* X coordinate of the key in the keyboard layout
|
||||
*/
|
||||
public int x;
|
||||
/**
|
||||
* Y coordinate of the key in the keyboard layout
|
||||
*/
|
||||
public int y;
|
||||
/**
|
||||
* The current pressed state of this key
|
||||
*/
|
||||
public boolean pressed;
|
||||
/**
|
||||
* If this is a sticky key, is it on?
|
||||
*/
|
||||
public boolean on;
|
||||
/**
|
||||
* Text to output when pressed. This can be multiple characters, like ".com"
|
||||
*/
|
||||
public CharSequence text;
|
||||
/**
|
||||
* Popup characters
|
||||
*/
|
||||
public CharSequence popupCharacters;
|
||||
|
||||
/**
|
||||
* Flags that specify the anchoring to edges of the keyboard for detecting touch events
|
||||
* that are just out of the boundary of the key. This is a bit mask of
|
||||
* {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT}, {@link Keyboard#EDGE_TOP} and
|
||||
* {@link Keyboard#EDGE_BOTTOM}.
|
||||
*/
|
||||
public int edgeFlags;
|
||||
/**
|
||||
* Whether this is a modifier key, such as Shift or Alt
|
||||
*/
|
||||
public boolean modifier;
|
||||
/**
|
||||
* The keyboard that this key belongs to
|
||||
*/
|
||||
private Keyboard keyboard;
|
||||
/**
|
||||
* If this key pops up a mini keyboard, this is the resource id for the XML layout for that
|
||||
* keyboard.
|
||||
*/
|
||||
public int popupResId;
|
||||
/**
|
||||
* Whether this key repeats itself when held down
|
||||
*/
|
||||
public boolean repeatable;
|
||||
|
||||
|
||||
private final static int[] KEY_STATE_NORMAL_ON = {
|
||||
android.R.attr.state_checkable,
|
||||
android.R.attr.state_checked
|
||||
};
|
||||
|
||||
private final static int[] KEY_STATE_PRESSED_ON = {
|
||||
android.R.attr.state_pressed,
|
||||
android.R.attr.state_checkable,
|
||||
android.R.attr.state_checked
|
||||
};
|
||||
|
||||
private final static int[] KEY_STATE_NORMAL_OFF = {
|
||||
android.R.attr.state_checkable
|
||||
};
|
||||
|
||||
private final static int[] KEY_STATE_PRESSED_OFF = {
|
||||
android.R.attr.state_pressed,
|
||||
android.R.attr.state_checkable
|
||||
};
|
||||
|
||||
private final static int[] KEY_STATE_NORMAL = {
|
||||
};
|
||||
|
||||
private final static int[] KEY_STATE_PRESSED = {
|
||||
android.R.attr.state_pressed
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an empty key with no attributes.
|
||||
*/
|
||||
public Key(Row parent) {
|
||||
keyboard = parent.parent;
|
||||
height = parent.defaultHeight;
|
||||
width = parent.defaultWidth;
|
||||
gap = parent.defaultHorizontalGap;
|
||||
edgeFlags = parent.rowEdgeFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a key with the given top-left coordinate and extract its attributes from
|
||||
* the XML parser.
|
||||
*
|
||||
* @param res resources associated with the caller's context
|
||||
* @param parent the row that this key belongs to. The row must already be attached to
|
||||
* a {@link Keyboard}.
|
||||
* @param x the x coordinate of the top-left
|
||||
* @param y the y coordinate of the top-left
|
||||
* @param parser the XML parser containing the attributes for this key
|
||||
*/
|
||||
public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser) {
|
||||
this(parent);
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard);
|
||||
|
||||
width = getDimensionOrFraction(a, R.styleable.Keyboard_keyWidth,
|
||||
keyboard.mDisplayWidth, parent.defaultWidth);
|
||||
height = getDimensionOrFraction(a, R.styleable.Keyboard_keyHeight,
|
||||
keyboard.mDisplayHeight, parent.defaultHeight);
|
||||
gap = getDimensionOrFraction(a, R.styleable.Keyboard_horizontalGap,
|
||||
keyboard.mDisplayWidth, parent.defaultHorizontalGap);
|
||||
a.recycle();
|
||||
a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
|
||||
this.x += gap;
|
||||
TypedValue codesValue = new TypedValue();
|
||||
a.getValue(R.styleable.Keyboard_Key_codes, codesValue);
|
||||
if (codesValue.type == TypedValue.TYPE_INT_DEC
|
||||
|| codesValue.type == TypedValue.TYPE_INT_HEX) {
|
||||
codes = new int[]{codesValue.data};
|
||||
} else if (codesValue.type == TypedValue.TYPE_STRING) {
|
||||
codes = parseCSV(codesValue.string.toString());
|
||||
}
|
||||
|
||||
iconPreview = a.getDrawable(R.styleable.Keyboard_Key_iconPreview);
|
||||
if (iconPreview != null) {
|
||||
iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
|
||||
iconPreview.getIntrinsicHeight());
|
||||
}
|
||||
popupCharacters = a.getText(R.styleable.Keyboard_Key_popupCharacters);
|
||||
popupResId = a.getResourceId(R.styleable.Keyboard_Key_popupKeyboard, 0);
|
||||
repeatable = a.getBoolean(R.styleable.Keyboard_Key_isRepeatable, false);
|
||||
modifier = a.getBoolean(R.styleable.Keyboard_Key_isModifier, false);
|
||||
sticky = a.getBoolean(R.styleable.Keyboard_Key_isSticky, false);
|
||||
edgeFlags = a.getInt(R.styleable.Keyboard_Key_keyEdgeFlags, 0);
|
||||
edgeFlags |= parent.rowEdgeFlags;
|
||||
|
||||
icon = a.getDrawable(R.styleable.Keyboard_Key_keyIcon);
|
||||
if (icon != null) {
|
||||
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
|
||||
}
|
||||
label = a.getText(R.styleable.Keyboard_Key_keyLabel);
|
||||
text = a.getText(R.styleable.Keyboard_Key_keyOutputText);
|
||||
|
||||
if (codes == null && !TextUtils.isEmpty(label)) {
|
||||
codes = new int[]{label.charAt(0)};
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the key that it has been pressed, in case it needs to change its appearance or
|
||||
* state.
|
||||
*
|
||||
* @see #onReleased(boolean)
|
||||
*/
|
||||
public void onPressed() {
|
||||
pressed = !pressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the pressed state of the key.
|
||||
*
|
||||
* <p>Toggled state of the key will be flipped when all the following conditions are
|
||||
* fulfilled:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>This is a sticky key, that is, {@link #sticky} is {@code true}.
|
||||
* <li>The parameter {@code inside} is {@code true}.
|
||||
* <li>{@link android.os.Build.VERSION#SDK_INT} is greater than
|
||||
* {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}.
|
||||
* </ul>
|
||||
*
|
||||
* @param inside whether the finger was released inside the key. Works only on Android M and
|
||||
* later. See the method document for details.
|
||||
* @see #onPressed()
|
||||
*/
|
||||
public void onReleased(boolean inside) {
|
||||
pressed = !pressed;
|
||||
if (sticky && inside) {
|
||||
on = !on;
|
||||
}
|
||||
}
|
||||
|
||||
int[] parseCSV(String value) {
|
||||
int count = 0;
|
||||
int lastIndex = 0;
|
||||
if (value.length() > 0) {
|
||||
count++;
|
||||
while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
int[] values = new int[count];
|
||||
count = 0;
|
||||
StringTokenizer st = new StringTokenizer(value, ",");
|
||||
while (st.hasMoreTokens()) {
|
||||
try {
|
||||
values[count++] = Integer.parseInt(st.nextToken());
|
||||
} catch (NumberFormatException nfe) {
|
||||
Log.e(TAG, "Error parsing keycodes " + value);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if a point falls inside this key.
|
||||
*
|
||||
* @param x the x-coordinate of the point
|
||||
* @param y the y-coordinate of the point
|
||||
* @return whether or not the point falls inside the key. If the key is attached to an edge,
|
||||
* it will assume that all points between the key and the edge are considered to be inside
|
||||
* the key.
|
||||
*/
|
||||
public boolean isInside(int x, int y) {
|
||||
boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
|
||||
boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
|
||||
boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
|
||||
boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
|
||||
if ((x >= this.x || (leftEdge && x <= this.x + this.width))
|
||||
&& (x < this.x + this.width || (rightEdge && x >= this.x))
|
||||
&& (y >= this.y || (topEdge && y <= this.y + this.height))
|
||||
&& (y < this.y + this.height || (bottomEdge && y >= this.y))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the square of the distance between the center of the key and the given point.
|
||||
*
|
||||
* @param x the x-coordinate of the point
|
||||
* @param y the y-coordinate of the point
|
||||
* @return the square of the distance of the point from the center of the key
|
||||
*/
|
||||
public int squaredDistanceFrom(int x, int y) {
|
||||
int xDist = this.x + width / 2 - x;
|
||||
int yDist = this.y + height / 2 - y;
|
||||
return xDist * xDist + yDist * yDist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the drawable state for the key, based on the current state and type of the key.
|
||||
*
|
||||
* @return the drawable state of the key.
|
||||
* @see android.graphics.drawable.StateListDrawable#setState(int[])
|
||||
*/
|
||||
public int[] getCurrentDrawableState() {
|
||||
int[] states = KEY_STATE_NORMAL;
|
||||
|
||||
if (on) {
|
||||
if (pressed) {
|
||||
states = KEY_STATE_PRESSED_ON;
|
||||
} else {
|
||||
states = KEY_STATE_NORMAL_ON;
|
||||
}
|
||||
} else {
|
||||
if (sticky) {
|
||||
if (pressed) {
|
||||
states = KEY_STATE_PRESSED_OFF;
|
||||
} else {
|
||||
states = KEY_STATE_NORMAL_OFF;
|
||||
}
|
||||
} else {
|
||||
if (pressed) {
|
||||
states = KEY_STATE_PRESSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
return states;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a keyboard from the given xml key layout file.
|
||||
*
|
||||
* @param context the application or service context
|
||||
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
|
||||
*/
|
||||
public Keyboard(Context context, int xmlLayoutResId) {
|
||||
this(context, xmlLayoutResId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a keyboard from the given xml key layout file. Weeds out rows
|
||||
* that have a keyboard mode defined but don't match the specified mode.
|
||||
*
|
||||
* @param context the application or service context
|
||||
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
|
||||
* @param modeId keyboard mode identifier
|
||||
* @param width sets width of keyboard
|
||||
* @param height sets height of keyboard
|
||||
*/
|
||||
public Keyboard(Context context, int xmlLayoutResId, int modeId, int width, int height) {
|
||||
mDisplayWidth = width;
|
||||
mDisplayHeight = height;
|
||||
|
||||
mDefaultHorizontalGap = 0;
|
||||
mDefaultWidth = mDisplayWidth / 10;
|
||||
mDefaultVerticalGap = 0;
|
||||
mDefaultHeight = mDefaultWidth;
|
||||
mKeys = new ArrayList<>();
|
||||
mModifierKeys = new ArrayList<>();
|
||||
mKeyboardMode = modeId;
|
||||
loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a keyboard from the given xml key layout file. Weeds out rows
|
||||
* that have a keyboard mode defined but don't match the specified mode.
|
||||
*
|
||||
* @param context the application or service context
|
||||
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
|
||||
* @param modeId keyboard mode identifier
|
||||
*/
|
||||
public Keyboard(Context context, int xmlLayoutResId, int modeId) {
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
mDisplayWidth = dm.widthPixels;
|
||||
mDisplayHeight = dm.heightPixels;
|
||||
//Log.v(TAG, "keyboard's display metrics:" + dm);
|
||||
|
||||
mDefaultHorizontalGap = 0;
|
||||
mDefaultWidth = mDisplayWidth / 10;
|
||||
mDefaultVerticalGap = 0;
|
||||
mDefaultHeight = mDefaultWidth;
|
||||
mKeys = new ArrayList<>();
|
||||
mModifierKeys = new ArrayList<>();
|
||||
mKeyboardMode = modeId;
|
||||
loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a blank keyboard from the given resource file and populates it with the specified
|
||||
* characters in left-to-right, top-to-bottom fashion, using the specified number of columns.
|
||||
* </p>
|
||||
* <p>If the specified number of columns is -1, then the keyboard will fit as many keys as
|
||||
* possible in each row.</p>
|
||||
*
|
||||
* @param context the application or service context
|
||||
* @param layoutTemplateResId the layout template file, containing no keys.
|
||||
* @param characters the list of characters to display on the keyboard. One key will be created
|
||||
* for each character.
|
||||
* @param columns the number of columns of keys to display. If this number is greater than the
|
||||
* number of keys that can fit in a row, it will be ignored. If this number is -1, the
|
||||
* keyboard will fit as many keys as possible in each row.
|
||||
*/
|
||||
public Keyboard(Context context, int layoutTemplateResId,
|
||||
CharSequence characters, int columns, int horizontalPadding) {
|
||||
this(context, layoutTemplateResId);
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int column = 0;
|
||||
mTotalWidth = 0;
|
||||
|
||||
Row row = new Row(this);
|
||||
row.defaultHeight = mDefaultHeight;
|
||||
row.defaultWidth = mDefaultWidth;
|
||||
row.defaultHorizontalGap = mDefaultHorizontalGap;
|
||||
row.verticalGap = mDefaultVerticalGap;
|
||||
row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
|
||||
final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
|
||||
for (int i = 0; i < characters.length(); i++) {
|
||||
char c = characters.charAt(i);
|
||||
if (column >= maxColumns
|
||||
|| x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
|
||||
x = 0;
|
||||
y += mDefaultVerticalGap + mDefaultHeight;
|
||||
column = 0;
|
||||
}
|
||||
final Key key = new Key(row);
|
||||
key.x = x;
|
||||
key.y = y;
|
||||
key.label = String.valueOf(c);
|
||||
key.codes = new int[]{c};
|
||||
column++;
|
||||
x += key.width + key.gap;
|
||||
mKeys.add(key);
|
||||
row.mKeys.add(key);
|
||||
if (x > mTotalWidth) {
|
||||
mTotalWidth = x;
|
||||
}
|
||||
}
|
||||
mTotalHeight = y + mDefaultHeight;
|
||||
rows.add(row);
|
||||
}
|
||||
|
||||
final void resize(int newWidth, int newHeight) {
|
||||
int numRows = rows.size();
|
||||
for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
|
||||
Row row = rows.get(rowIndex);
|
||||
int numKeys = row.mKeys.size();
|
||||
int totalGap = 0;
|
||||
int totalWidth = 0;
|
||||
for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
|
||||
Key key = row.mKeys.get(keyIndex);
|
||||
if (keyIndex > 0) {
|
||||
totalGap += key.gap;
|
||||
}
|
||||
totalWidth += key.width;
|
||||
}
|
||||
if (totalGap + totalWidth > newWidth) {
|
||||
int x = 0;
|
||||
float scaleFactor = (float) (newWidth - totalGap) / totalWidth;
|
||||
for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
|
||||
Key key = row.mKeys.get(keyIndex);
|
||||
key.width *= scaleFactor;
|
||||
key.x = x;
|
||||
x += key.width + key.gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
mTotalWidth = newWidth;
|
||||
}
|
||||
|
||||
public List<Key> getKeys() {
|
||||
return mKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total height of the keyboard
|
||||
*
|
||||
* @return the total height of the keyboard
|
||||
*/
|
||||
public int getHeight() {
|
||||
return mTotalHeight;
|
||||
}
|
||||
|
||||
public int getMinWidth() {
|
||||
return mTotalWidth;
|
||||
}
|
||||
|
||||
public boolean setShifted(boolean shiftState) {
|
||||
for (Key shiftKey : mShiftKeys) {
|
||||
if (shiftKey != null) {
|
||||
shiftKey.on = shiftState;
|
||||
}
|
||||
}
|
||||
if (mShifted != shiftState) {
|
||||
mShifted = shiftState;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isShifted() {
|
||||
return mShifted;
|
||||
}
|
||||
|
||||
private void computeNearestNeighbors() {
|
||||
// Round-up so we don't have any pixels outside the grid
|
||||
mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
|
||||
mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
|
||||
mGridNeighbors = new int[GRID_SIZE][];
|
||||
int[] indices = new int[mKeys.size()];
|
||||
final int gridWidth = GRID_WIDTH * mCellWidth;
|
||||
final int gridHeight = GRID_HEIGHT * mCellHeight;
|
||||
for (int x = 0; x < gridWidth; x += mCellWidth) {
|
||||
for (int y = 0; y < gridHeight; y += mCellHeight) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < mKeys.size(); i++) {
|
||||
final Key key = mKeys.get(i);
|
||||
if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
|
||||
key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
|
||||
key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
|
||||
< mProximityThreshold ||
|
||||
key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) {
|
||||
indices[count++] = i;
|
||||
}
|
||||
}
|
||||
int[] cell = new int[count];
|
||||
System.arraycopy(indices, 0, cell, 0, count);
|
||||
mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indices of the keys that are closest to the given point.
|
||||
*
|
||||
* @param x the x-coordinate of the point
|
||||
* @param y the y-coordinate of the point
|
||||
* @return the array of integer indices for the nearest keys to the given point. If the given
|
||||
* point is out of range, then an array of size zero is returned.
|
||||
*/
|
||||
public int[] getNearestKeys(int x, int y) {
|
||||
if (mGridNeighbors == null) computeNearestNeighbors();
|
||||
if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
|
||||
int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
|
||||
if (index < GRID_SIZE) {
|
||||
return mGridNeighbors[index];
|
||||
}
|
||||
}
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
|
||||
return new Row(res, this, parser);
|
||||
}
|
||||
|
||||
protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
|
||||
XmlResourceParser parser) {
|
||||
return new Key(res, parent, x, y, parser);
|
||||
}
|
||||
|
||||
private void loadKeyboard(Context context, XmlResourceParser parser) {
|
||||
boolean inKey = false;
|
||||
boolean inRow = false;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
Key key = null;
|
||||
Row currentRow = null;
|
||||
Resources res = context.getResources();
|
||||
boolean skipRow;
|
||||
|
||||
try {
|
||||
int event;
|
||||
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
|
||||
if (event == XmlResourceParser.START_TAG) {
|
||||
String tag = parser.getName();
|
||||
if (TAG_ROW.equals(tag)) {
|
||||
inRow = true;
|
||||
x = 0;
|
||||
currentRow = createRowFromXml(res, parser);
|
||||
rows.add(currentRow);
|
||||
skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;
|
||||
if (skipRow) {
|
||||
skipToEndOfRow(parser);
|
||||
inRow = false;
|
||||
}
|
||||
} else if (TAG_KEY.equals(tag)) {
|
||||
inKey = true;
|
||||
key = createKeyFromXml(res, currentRow, x, y, parser);
|
||||
mKeys.add(key);
|
||||
if (key.codes[0] == KEYCODE_SHIFT) {
|
||||
// Find available shift key slot and put this shift key in it
|
||||
for (int i = 0; i < mShiftKeys.length; i++) {
|
||||
if (mShiftKeys[i] == null) {
|
||||
mShiftKeys[i] = key;
|
||||
mShiftKeyIndices[i] = mKeys.size() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mModifierKeys.add(key);
|
||||
} else if (key.codes[0] == KEYCODE_ALT) {
|
||||
mModifierKeys.add(key);
|
||||
}
|
||||
currentRow.mKeys.add(key);
|
||||
} else if (TAG_KEYBOARD.equals(tag)) {
|
||||
parseKeyboardAttributes(res, parser);
|
||||
}
|
||||
} else if (event == XmlResourceParser.END_TAG) {
|
||||
if (inKey) {
|
||||
inKey = false;
|
||||
x += key.gap + key.width;
|
||||
if (x > mTotalWidth) {
|
||||
mTotalWidth = x;
|
||||
}
|
||||
} else if (inRow) {
|
||||
inRow = false;
|
||||
y += currentRow.verticalGap;
|
||||
y += currentRow.defaultHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Parse error:" + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
mTotalHeight = y - mDefaultVerticalGap;
|
||||
}
|
||||
|
||||
private void skipToEndOfRow(XmlResourceParser parser)
|
||||
throws XmlPullParserException, IOException {
|
||||
int event;
|
||||
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
|
||||
if (event == XmlResourceParser.END_TAG
|
||||
&& parser.getName().equals(TAG_ROW)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {
|
||||
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard);
|
||||
|
||||
mDefaultWidth = getDimensionOrFraction(a, R.styleable.Keyboard_keyWidth,
|
||||
mDisplayWidth, mDisplayWidth / 10);
|
||||
mDefaultHeight = getDimensionOrFraction(a, R.styleable.Keyboard_keyHeight,
|
||||
mDisplayHeight, 50);
|
||||
mDefaultHorizontalGap = getDimensionOrFraction(a, R.styleable.Keyboard_horizontalGap,
|
||||
mDisplayWidth, 0);
|
||||
mDefaultVerticalGap = getDimensionOrFraction(a, R.styleable.Keyboard_verticalGap,
|
||||
mDisplayHeight, 0);
|
||||
mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
|
||||
mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
|
||||
TypedValue value = a.peekValue(index);
|
||||
if (value == null) return defValue;
|
||||
if (value.type == TypedValue.TYPE_DIMENSION) {
|
||||
return a.getDimensionPixelOffset(index, defValue);
|
||||
} else if (value.type == TypedValue.TYPE_FRACTION) {
|
||||
// Round it to avoid values like 47.9999 from getting truncated
|
||||
return Math.round(a.getFraction(index, base, base, defValue));
|
||||
}
|
||||
return defValue;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,15 +17,12 @@
|
||||
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.kunzisoft.keepass.magikeyboard
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.inputmethodservice.InputMethodService
|
||||
import android.inputmethodservice.Keyboard
|
||||
import android.inputmethodservice.KeyboardView
|
||||
import android.media.AudioManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
@@ -130,8 +127,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
removeEntryInfo()
|
||||
}
|
||||
assignKeyboardView()
|
||||
keyboardView?.setOnKeyboardActionListener(this)
|
||||
keyboardView?.isPreviewEnabled = false
|
||||
keyboardView?.onKeyboardActionListener = this
|
||||
|
||||
return rootKeyboardView
|
||||
}
|
||||
@@ -199,6 +195,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun switchToPreviousKeyboard() {
|
||||
var imeManager: InputMethodManager? = null
|
||||
try {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX 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.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.kunzisoft.keepass.view
|
||||
|
||||
import android.content.Context
|
||||
import android.inputmethodservice.Keyboard
|
||||
import android.inputmethodservice.KeyboardView
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import android.util.AttributeSet
|
||||
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService.Companion.KEY_BACK_KEYBOARD
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService.Companion.KEY_CHANGE_KEYBOARD
|
||||
|
||||
class MagikeyboardView : KeyboardView {
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
override fun onLongPress(key: Keyboard.Key): Boolean {
|
||||
return if (key.codes[0] == KEY_BACK_KEYBOARD) {
|
||||
onKeyboardActionListener.onKey(KEY_CHANGE_KEYBOARD, IntArray(0))
|
||||
true
|
||||
} else {
|
||||
//Log.d("LatinKeyboardView", "KEY: " + key.codes[0]);
|
||||
super.onLongPress(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="true"
|
||||
@@ -33,13 +32,9 @@
|
||||
android:background="@color/grey_blue_dark"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/white"/>
|
||||
<com.kunzisoft.keepass.view.MagikeyboardView
|
||||
<com.kunzisoft.keepass.magikeyboard.KeyboardView
|
||||
android:id="@+id/magikeyboard_view"
|
||||
style="@style/KeepassDXStyle.Keyboard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="4dp"
|
||||
android:background="@color/grey_blue_dark"
|
||||
android:keyBackground="@drawable/key_background"
|
||||
android:keyTextColor="@color/white"
|
||||
android:popupLayout="@layout/keyboard_popup_fields" />
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
@@ -36,4 +36,95 @@
|
||||
<declare-styleable name="explanationDialog">
|
||||
<attr name="explanations" format="string" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- Specific keyboard attributes -->
|
||||
<declare-styleable name="KeyboardView">
|
||||
<!-- Default KeyboardView style. -->
|
||||
<attr name="keyboardViewStyle" format="reference" />
|
||||
<!-- Image for the key. This image needs to be a StateListDrawable, with the following
|
||||
possible states: normal, pressed, checkable, checkable+pressed, checkable+checked,
|
||||
checkable+checked+pressed. -->
|
||||
<attr name="keyBackground" format="reference" />
|
||||
<!-- Size of the text for character keys. -->
|
||||
<attr name="keyTextSize" format="dimension" />
|
||||
<!-- Size of the text for custom keys with some text and no icon. -->
|
||||
<attr name="labelTextSize" format="dimension" />
|
||||
<!-- Color to use for the label in a key. -->
|
||||
<attr name="keyTextColor" format="color" />
|
||||
<!-- Layout resource for key press feedback. -->
|
||||
<attr name="keyPreviewLayout" format="reference" />
|
||||
<!-- Vertical offset of the key press feedback from the key. -->
|
||||
<attr name="keyPreviewOffset" format="dimension" />
|
||||
<!-- Height of the key press feedback popup. -->
|
||||
<attr name="keyPreviewHeight" format="dimension" />
|
||||
<!-- Amount to offset the touch Y coordinate by, for bias correction. -->
|
||||
<attr name="verticalCorrection" format="dimension" />
|
||||
<!-- Layout resource for popup keyboards. -->
|
||||
<attr name="popupLayout" format="reference" />
|
||||
<!-- Text shadow color -->
|
||||
<attr name="shadowColor" format="color" />
|
||||
<!-- Text shadow radius -->
|
||||
<attr name="shadowRadius" format="float" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="KeyboardViewPreviewState">
|
||||
<!-- State for {@link com.kunzisoft.keepass.magikeyboard.KeyboardView KeyboardView}
|
||||
key preview background. -->
|
||||
<attr name="state_long_pressable" format="boolean" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="Keyboard">
|
||||
<!-- Default width of a key, in pixels or percentage of display width. -->
|
||||
<attr name="keyWidth" format="dimension|fraction" />
|
||||
<!-- Default height of a key, in pixels or percentage of display width. -->
|
||||
<attr name="keyHeight" format="dimension|fraction" />
|
||||
<!-- Default horizontal gap between keys. -->
|
||||
<attr name="horizontalGap" format="dimension|fraction" />
|
||||
<!-- Default vertical gap between rows of keys. -->
|
||||
<attr name="verticalGap" format="dimension|fraction" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="Keyboard_Row">
|
||||
<!-- Row edge flags. -->
|
||||
<attr name="rowEdgeFlags">
|
||||
<!-- Row is anchored to the top of the keyboard. -->
|
||||
<flag name="top" value="4" />
|
||||
<!-- Row is anchored to the bottom of the keyboard.-->
|
||||
<flag name="bottom" value="8" />
|
||||
</attr>
|
||||
<!-- Mode of the keyboard. If the mode doesn't match the
|
||||
requested keyboard mode, the row will be skipped. -->
|
||||
<attr name="keyboardMode" format="reference" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="Keyboard_Key">
|
||||
<!-- The unicode value or comma-separated values that this key outputs. -->
|
||||
<attr name="codes" format="integer|string" />
|
||||
<!-- The XML keyboard layout of any popup keyboard -->
|
||||
<attr name="popupKeyboard" format="reference" />
|
||||
<!-- The characters to display in the popup keyboard -->
|
||||
<attr name="popupCharacters" format="string" />
|
||||
<!-- Key edge flags. -->
|
||||
<attr name="keyEdgeFlags">
|
||||
<!-- Key is anchored to the left of the keyboard.
|
||||
{@deprecated Copy this definition into your own application project.} -->
|
||||
<flag name="left" value="1" />
|
||||
<!-- Key is anchored to the right of the keyboard.
|
||||
{@deprecated Copy this definition into your own application project.} -->
|
||||
<flag name="right" value="2" />
|
||||
</attr>
|
||||
<!-- Whether this is a modifier key such as Alt or Shift. -->
|
||||
<attr name="isModifier" format="boolean" />
|
||||
<!-- Whether this is a toggle key. -->
|
||||
<attr name="isSticky" format="boolean" />
|
||||
<!-- Whether long-pressing on this key will make it repeat. -->
|
||||
<attr name="isRepeatable" format="boolean" />
|
||||
<!-- The icon to show in the popup preview. -->
|
||||
<attr name="iconPreview" format="reference" />
|
||||
<!-- The string of characters to output when this key is pressed. -->
|
||||
<attr name="keyOutputText" format="string" />
|
||||
<!-- The label to display on the key. -->
|
||||
<attr name="keyLabel" format="string" />
|
||||
<!-- The icon to display on the key instead of the label. -->
|
||||
<attr name="keyIcon" format="reference" />
|
||||
<!-- Mode of the keyboard. If the mode doesn't match the
|
||||
requested keyboard mode, the key will be skipped. -->
|
||||
<attr name="keyboardMode" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -522,4 +522,16 @@
|
||||
<item name="android:backgroundDimEnabled">false</item>
|
||||
</style>
|
||||
|
||||
<!-- Keyboard -->
|
||||
<style name="KeepassDXStyle.Keyboard" parent="Theme.AppCompat">
|
||||
<item name="android:paddingTop">4dp</item>
|
||||
<item name="background">@color/grey_blue_dark</item>
|
||||
<item name="keyBackground">@drawable/key_background</item>
|
||||
<item name="keyTextSize">22sp</item>
|
||||
<item name="keyTextColor">@color/white</item>
|
||||
<item name="labelTextSize">14sp</item>
|
||||
<item name="verticalCorrection">-10dip</item>
|
||||
<item name="popupLayout">@layout/keyboard_popup_fields</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -16,44 +16,44 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<Keyboard
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:targetApi="o"
|
||||
android:verticalGap="8dp"
|
||||
android:horizontalGap="1%p"
|
||||
android:keyHeight="38dp">
|
||||
app:verticalGap="8dp"
|
||||
app:horizontalGap="1%p"
|
||||
app:keyHeight="38dp">
|
||||
<Row
|
||||
android:rowEdgeFlags="bottom">
|
||||
app:rowEdgeFlags="bottom">
|
||||
<Key
|
||||
android:codes="600"
|
||||
android:keyIcon="@drawable/ic_keyboard_white_24dp"
|
||||
android:tooltipText="@string/back_to_previous_keyboard"
|
||||
android:keyWidth="17%p"
|
||||
android:horizontalGap="1%p"
|
||||
android:keyEdgeFlags="left"
|
||||
android:isRepeatable="false" />
|
||||
app:codes="600"
|
||||
app:keyIcon="@drawable/ic_keyboard_white_24dp"
|
||||
app:tooltipText="@string/back_to_previous_keyboard"
|
||||
app:keyWidth="17%p"
|
||||
app:horizontalGap="1%p"
|
||||
app:keyEdgeFlags="left"
|
||||
app:isRepeatable="false" />
|
||||
<Key
|
||||
android:codes="620"
|
||||
android:keyIcon="@drawable/ic_key_white_24dp"
|
||||
android:tooltipText="@string/select_entry"
|
||||
android:keyWidth="45%p"
|
||||
android:horizontalGap="2%p"
|
||||
android:isRepeatable="false"/>
|
||||
app:codes="620"
|
||||
app:keyIcon="@drawable/ic_key_white_24dp"
|
||||
app:tooltipText="@string/select_entry"
|
||||
app:keyWidth="45%p"
|
||||
app:horizontalGap="2%p"
|
||||
app:isRepeatable="false"/>
|
||||
<Key
|
||||
android:codes="-5"
|
||||
android:keyIcon="@drawable/ic_backspace_white_24dp"
|
||||
android:tooltipText="@string/backspace"
|
||||
android:keyWidth="14%p"
|
||||
android:horizontalGap="2%p"
|
||||
android:isRepeatable="true"/>
|
||||
app:codes="-5"
|
||||
app:keyIcon="@drawable/ic_backspace_white_24dp"
|
||||
app:tooltipText="@string/backspace"
|
||||
app:keyWidth="14%p"
|
||||
app:horizontalGap="2%p"
|
||||
app:isRepeatable="true"/>
|
||||
<Key
|
||||
android:codes="-4"
|
||||
android:keyIcon="@drawable/ic_keyboard_return_white_24dp"
|
||||
android:tooltipText="@string/enter"
|
||||
android:keyWidth="17%p"
|
||||
android:horizontalGap="1%p"
|
||||
android:isRepeatable="false"
|
||||
android:keyEdgeFlags="right"/>
|
||||
app:codes="-4"
|
||||
app:keyIcon="@drawable/ic_keyboard_return_white_24dp"
|
||||
app:tooltipText="@string/enter"
|
||||
app:keyWidth="17%p"
|
||||
app:horizontalGap="1%p"
|
||||
app:isRepeatable="false"
|
||||
app:keyEdgeFlags="right"/>
|
||||
</Row>
|
||||
</Keyboard>
|
||||
@@ -17,89 +17,89 @@
|
||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<Keyboard
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:targetApi="o"
|
||||
android:verticalGap="8dp"
|
||||
android:horizontalGap="1%p"
|
||||
android:keyHeight="38dp">
|
||||
app:verticalGap="8dp"
|
||||
app:horizontalGap="1%p"
|
||||
app:keyHeight="38dp">
|
||||
<Row>
|
||||
<Key
|
||||
android:codes="520"
|
||||
android:keyEdgeFlags="left"
|
||||
android:keyIcon="@drawable/ic_link_black_24dp"
|
||||
android:tooltipText="@string/entry_url"
|
||||
android:keyWidth="15%p"
|
||||
android:horizontalGap="1%p"
|
||||
android:isRepeatable="false"/>
|
||||
app:codes="520"
|
||||
app:keyEdgeFlags="left"
|
||||
app:keyIcon="@drawable/ic_link_black_24dp"
|
||||
app:tooltipText="@string/entry_url"
|
||||
app:keyWidth="15%p"
|
||||
app:horizontalGap="1%p"
|
||||
app:isRepeatable="false"/>
|
||||
<Key
|
||||
android:codes="500"
|
||||
android:keyIcon="@drawable/ic_person_white_24dp"
|
||||
android:tooltipText="@string/entry_user_name"
|
||||
android:keyWidth="17%p"
|
||||
android:horizontalGap="2%p"
|
||||
android:isRepeatable="false" />
|
||||
app:codes="500"
|
||||
app:keyIcon="@drawable/ic_person_white_24dp"
|
||||
app:tooltipText="@string/entry_user_name"
|
||||
app:keyWidth="17%p"
|
||||
app:horizontalGap="2%p"
|
||||
app:isRepeatable="false" />
|
||||
<Key
|
||||
android:codes="510"
|
||||
android:keyIcon="@drawable/ic_password_white_24dp"
|
||||
android:tooltipText="@string/entry_password"
|
||||
android:keyWidth="28%p"
|
||||
android:horizontalGap="1%p"
|
||||
android:isRepeatable="false"/>
|
||||
app:codes="510"
|
||||
app:keyIcon="@drawable/ic_password_white_24dp"
|
||||
app:tooltipText="@string/entry_password"
|
||||
app:keyWidth="28%p"
|
||||
app:horizontalGap="1%p"
|
||||
app:isRepeatable="false"/>
|
||||
<Key
|
||||
android:codes="515"
|
||||
android:keyIcon="@drawable/ic_otp_white_24dp"
|
||||
android:tooltipText="@string/entry_otp"
|
||||
android:keyWidth="17%p"
|
||||
android:horizontalGap="2%p"
|
||||
android:isRepeatable="false"/>
|
||||
app:codes="515"
|
||||
app:keyIcon="@drawable/ic_otp_white_24dp"
|
||||
app:tooltipText="@string/entry_otp"
|
||||
app:keyWidth="17%p"
|
||||
app:horizontalGap="2%p"
|
||||
app:isRepeatable="false"/>
|
||||
<Key
|
||||
android:codes="530"
|
||||
android:keyIcon="@drawable/ic_list_white_24dp"
|
||||
android:tooltipText="@string/custom_fields"
|
||||
android:keyWidth="15%p"
|
||||
android:horizontalGap="1%p"
|
||||
android:isRepeatable="false"
|
||||
android:keyEdgeFlags="right"/>
|
||||
app:codes="530"
|
||||
app:keyIcon="@drawable/ic_list_white_24dp"
|
||||
app:tooltipText="@string/custom_fields"
|
||||
app:keyWidth="15%p"
|
||||
app:horizontalGap="1%p"
|
||||
app:isRepeatable="false"
|
||||
app:keyEdgeFlags="right"/>
|
||||
</Row>
|
||||
<Row
|
||||
android:rowEdgeFlags="bottom">
|
||||
app:rowEdgeFlags="bottom">
|
||||
<Key
|
||||
android:codes="600"
|
||||
android:keyIcon="@drawable/ic_keyboard_white_24dp"
|
||||
android:tooltipText="@string/back_to_previous_keyboard"
|
||||
android:keyWidth="17%p"
|
||||
android:horizontalGap="1%p"
|
||||
android:keyEdgeFlags="left"
|
||||
android:isRepeatable="false"/>
|
||||
app:codes="600"
|
||||
app:keyIcon="@drawable/ic_keyboard_white_24dp"
|
||||
app:tooltipText="@string/back_to_previous_keyboard"
|
||||
app:keyWidth="17%p"
|
||||
app:horizontalGap="1%p"
|
||||
app:keyEdgeFlags="left"
|
||||
app:isRepeatable="false"/>
|
||||
<Key
|
||||
android:codes="611"
|
||||
android:keyIcon="@drawable/ic_lock_white_24dp"
|
||||
android:tooltipText="@string/lock"
|
||||
android:keyWidth="26%p"
|
||||
android:horizontalGap="2%p"
|
||||
android:isRepeatable="false" />
|
||||
app:codes="611"
|
||||
app:keyIcon="@drawable/ic_lock_white_24dp"
|
||||
app:tooltipText="@string/lock"
|
||||
app:keyWidth="26%p"
|
||||
app:horizontalGap="2%p"
|
||||
app:isRepeatable="false" />
|
||||
<Key
|
||||
android:codes="620"
|
||||
android:keyIcon="@drawable/ic_key_white_24dp"
|
||||
android:tooltipText="@string/select_entry"
|
||||
android:keyWidth="18%p"
|
||||
android:horizontalGap="1%p"
|
||||
android:isRepeatable="false"/>
|
||||
app:codes="620"
|
||||
app:keyIcon="@drawable/ic_key_white_24dp"
|
||||
app:tooltipText="@string/select_entry"
|
||||
app:keyWidth="18%p"
|
||||
app:horizontalGap="1%p"
|
||||
app:isRepeatable="false"/>
|
||||
<Key
|
||||
android:codes="-5"
|
||||
android:keyIcon="@drawable/ic_backspace_white_24dp"
|
||||
android:tooltipText="@string/backspace"
|
||||
android:keyWidth="14%p"
|
||||
android:horizontalGap="2%p"
|
||||
android:isRepeatable="true"/>
|
||||
app:codes="-5"
|
||||
app:keyIcon="@drawable/ic_backspace_white_24dp"
|
||||
app:tooltipText="@string/backspace"
|
||||
app:keyWidth="14%p"
|
||||
app:horizontalGap="2%p"
|
||||
app:isRepeatable="true"/>
|
||||
<Key
|
||||
android:codes="-4"
|
||||
android:keyIcon="@drawable/ic_keyboard_return_white_24dp"
|
||||
android:tooltipText="@string/enter"
|
||||
android:keyWidth="17%p"
|
||||
android:horizontalGap="1%p"
|
||||
android:isRepeatable="false"
|
||||
android:keyEdgeFlags="right"/>
|
||||
app:codes="-4"
|
||||
app:keyIcon="@drawable/ic_keyboard_return_white_24dp"
|
||||
app:tooltipText="@string/enter"
|
||||
app:keyWidth="17%p"
|
||||
app:horizontalGap="1%p"
|
||||
app:isRepeatable="false"
|
||||
app:keyEdgeFlags="right"/>
|
||||
</Row>
|
||||
</Keyboard>
|
||||
Reference in New Issue
Block a user