ANDROID: Use a in-app keyboard instead of system

Keyboard was ported over from our SDL port which used https://github.com/pelya/commandergenius/tree/sdl_android/project

Pending optimizations, floatable/draggable implementation and a few bug fixes
We are using a local copy and slightly modified version of KeyboardView and Keyboard (and related resources).
since the android KeyboardView widget will be deprecated in API 29.
The copies are taken from the AOSP, as per the recommendation from Android Developers.
This commit is contained in:
antoniou 2020-10-15 13:21:08 +03:00
parent 4f0fa0005e
commit 4d9ed8351b
76 changed files with 3751 additions and 118 deletions

View File

@ -0,0 +1,902 @@
/*
* Copyright (C) 2008-2009 Google Inc.
*
* 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 org.scummvm.scummvm;
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 androidx.annotation.XmlRes;
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>
* &lt;Keyboard
* android:keyWidth="%10p"
* android:keyHeight="50px"
* android:horizontalGap="2px"
* android:verticalGap="2px" &gt;
* &lt;Row android:keyWidth="32px" &gt;
* &lt;Key android:keyLabel="A" /&gt;
* ...
* &lt;/Row&gt;
* ...
* &lt;/Keyboard&gt;
* </pre>
*/
public class CustomKeyboard {
static final String LOG_TAG = "CustomKeyboard";
// Keyboard XML Tags
private static final String TAG_KEYBOARD = "CustomKeyboard";
private static final String TAG_ROW = "CustomRow";
private static final String TAG_KEY = "CustomKey";
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;
/** Keyboard label **/
private CharSequence mLabel;
/** 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 CustomKey[] mShiftKeys = { null, null };
/** Key index for the shift key, if present */
private int[] mShiftKeyIndices = {-1, -1};
/** Current key width, while loading the keyboard */
private int mKeyWidth;
/** Current key height, while loading the keyboard */
private int mKeyHeight;
/** Total height of the keyboard, including the padding and keys */
// @UnsupportedAppUsage
private int mTotalHeight;
/**
* Total width of the keyboard, including left side gaps and keys, but not any gaps on the
* right side.
*/
// @UnsupportedAppUsage
private int mTotalWidth;
/** List of keys in this keyboard */
private List<CustomKey> mKeys;
/** List of modifier keys such as Shift & Alt, if any */
// @UnsupportedAppUsage
private List<CustomKey> 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<CustomRow> rows = new ArrayList<CustomRow>();
/**
* 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 CustomKeyboard}
* defines.
*/
public static class CustomRow {
/** 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<CustomKey> mKeys = new ArrayList<>();
/**
* Edge flags for this row of keys. Possible values that can be assigned are
* {@link CustomKeyboard#EDGE_TOP EDGE_TOP} and {@link CustomKeyboard#EDGE_BOTTOM EDGE_BOTTOM}
*/
public int rowEdgeFlags;
/** The keyboard mode for this row */
public int mode;
private CustomKeyboard parent;
public CustomRow(CustomKeyboard parent) {
this.parent = parent;
}
public CustomRow(Resources res, CustomKeyboard parent, XmlResourceParser parser) {
this.parent = parent;
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.CustomKeyboard);
defaultWidth = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_keyWidth,
parent.mDisplayWidth, parent.mDefaultWidth);
defaultHeight = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_keyHeight,
parent.mDisplayHeight, parent.mDefaultHeight);
defaultHorizontalGap = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_horizontalGap,
parent.mDisplayWidth, parent.mDefaultHorizontalGap);
verticalGap = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_verticalGap,
parent.mDisplayHeight, parent.mDefaultVerticalGap);
a.recycle();
a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.CustomKeyboard_CustomRow);
rowEdgeFlags = a.getInt(R.styleable.CustomKeyboard_CustomRow_rowEdgeFlags, 0);
mode = a.getResourceId(R.styleable.CustomKeyboard_CustomRow_keyboardMode,
0);
a.recycle();
}
}
/**
* Class for describing the position and characteristics of a single key in the keyboard.
*
*/
public static class CustomKey {
/**
* 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 CustomKeyboard#EDGE_LEFT}, {@link CustomKeyboard#EDGE_RIGHT}, {@link CustomKeyboard#EDGE_TOP} and
* {@link CustomKeyboard#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 CustomKeyboard 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 CustomKey(CustomRow 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 CustomKeyboard}.
* @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 CustomKey(Resources res, CustomRow parent, int x, int y, XmlResourceParser parser) {
this(parent);
this.x = x;
this.y = y;
// obtainAttributes (AttributeSet set, int[] attrs)
// Retrieve a set of basic attribute values from an AttributeSet, not performing styling of them using a theme and/or style resources.
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.CustomKeyboard);
width = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_keyWidth,
keyboard.mDisplayWidth, parent.defaultWidth);
height = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_keyHeight,
keyboard.mDisplayHeight, parent.defaultHeight);
gap = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_horizontalGap,
keyboard.mDisplayWidth, parent.defaultHorizontalGap);
// Log.d(LOG_TAG, "from CustomKeyboard: wid: " +width + " heigh: " + height + " gap: " + gap );
a.recycle();
a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.CustomKeyboard_CustomKey);
this.x += gap;
TypedValue codesValue = new TypedValue();
a.getValue(R.styleable.CustomKeyboard_CustomKey_codes,
codesValue);
if (codesValue.type == TypedValue.TYPE_INT_DEC || codesValue.type == TypedValue.TYPE_INT_HEX) {
// Log.d(LOG_TAG, "Key codes is INT or HEX");
codes = new int[] { codesValue.data };
} else if (codesValue.type == TypedValue.TYPE_STRING) {
// Log.d(LOG_TAG, "Key codes is String");
codes = parseCSV(codesValue.string.toString());
}
iconPreview = a.getDrawable(R.styleable.CustomKeyboard_CustomKey_iconPreview);
if (iconPreview != null) {
iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
iconPreview.getIntrinsicHeight());
}
popupCharacters = a.getText(
R.styleable.CustomKeyboard_CustomKey_popupCharacters);
popupResId = a.getResourceId(
R.styleable.CustomKeyboard_CustomKey_popupKeyboard, 0);
repeatable = a.getBoolean(
R.styleable.CustomKeyboard_CustomKey_isRepeatable, false);
modifier = a.getBoolean(
R.styleable.CustomKeyboard_CustomKey_isModifier, false);
sticky = a.getBoolean(
R.styleable.CustomKeyboard_CustomKey_isSticky, false);
edgeFlags = a.getInt(R.styleable.CustomKeyboard_CustomKey_keyEdgeFlags, 0);
edgeFlags |= parent.rowEdgeFlags;
icon = a.getDrawable(
R.styleable.CustomKeyboard_CustomKey_keyIcon);
if (icon != null) {
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
}
label = a.getText(R.styleable.CustomKeyboard_CustomKey_keyLabel);
Log.d(LOG_TAG, "Key label is " + label);
text = a.getText(R.styleable.CustomKeyboard_CustomKey_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(LOG_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 CustomKeyboard(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 CustomKeyboard(Context context, @XmlRes int xmlLayoutResId, int modeId, int width,
int height) {
mDisplayWidth = width;
mDisplayHeight = height;
mDefaultHorizontalGap = 0;
mDefaultWidth = mDisplayWidth / 10;
mDefaultVerticalGap = 0;
mDefaultHeight = mDefaultWidth;
mKeys = new ArrayList<CustomKey>();
mModifierKeys = new ArrayList<CustomKey>();
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 CustomKeyboard(Context context, @XmlRes int xmlLayoutResId, int modeId) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
mDisplayWidth = dm.widthPixels;
mDisplayHeight = dm.heightPixels;
Log.v(LOG_TAG, "keyboard's display metrics:" + dm);
mDefaultHorizontalGap = 0;
mDefaultWidth = mDisplayWidth / 10;
mDefaultVerticalGap = 0;
mDefaultHeight = mDefaultWidth;
mKeys = new ArrayList<CustomKey>();
mModifierKeys = new ArrayList<CustomKey>();
mKeyboardMode = modeId;
// Log.v(LOG_TAG, "Resource ID is " + xmlLayoutResId + " and parser is null?" + ((context.getResources().getXml(xmlLayoutResId) == null) ? "yes" : "no"));
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 CustomKeyboard(Context context, int layoutTemplateResId,
CharSequence characters, int columns, int horizontalPadding) {
this(context, layoutTemplateResId);
int x = 0;
int y = 0;
int column = 0;
mTotalWidth = 0;
CustomRow row = new CustomRow(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 CustomKey key = new CustomKey(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);
}
// @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final void resize(int newWidth, int newHeight) {
int numRows = rows.size();
for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
CustomRow row = rows.get(rowIndex);
int numKeys = row.mKeys.size();
int totalGap = 0;
int totalWidth = 0;
for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
CustomKey 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) {
CustomKey key = row.mKeys.get(keyIndex);
key.width *= scaleFactor;
key.x = x;
x += key.width + key.gap;
}
}
}
mTotalWidth = newWidth;
// TODO: This does not adjust the vertical placement according to the new size.
// The main problem in the previous code was horizontal placement/size, but we should
// also recalculate the vertical sizes/positions when we get this resize call.
}
public List<CustomKey> getKeys() {
return mKeys;
}
public List<CustomKey> getModifierKeys() {
return mModifierKeys;
}
protected int getHorizontalGap() {
return mDefaultHorizontalGap;
}
protected void setHorizontalGap(int gap) {
mDefaultHorizontalGap = gap;
}
protected int getVerticalGap() {
return mDefaultVerticalGap;
}
protected void setVerticalGap(int gap) {
mDefaultVerticalGap = gap;
}
protected int getKeyHeight() {
return mDefaultHeight;
}
protected void setKeyHeight(int height) {
mDefaultHeight = height;
}
protected int getKeyWidth() {
return mDefaultWidth;
}
protected void setKeyWidth(int width) {
mDefaultWidth = width;
}
/**
* 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 (CustomKey shiftKey : mShiftKeys) {
if (shiftKey != null) {
shiftKey.on = shiftState;
}
}
if (mShifted != shiftState) {
mShifted = shiftState;
return true;
}
return false;
}
public boolean isShifted() {
return mShifted;
}
/**
*/
public int[] getShiftKeyIndices() {
return mShiftKeyIndices;
}
public int getShiftKeyIndex() {
return mShiftKeyIndices[0];
}
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 CustomKey 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 CustomRow createRowFromXml(Resources res, XmlResourceParser parser) {
return new CustomRow(res, this, parser);
}
protected CustomKey createKeyFromXml(Resources res, CustomRow parent, int x, int y,
XmlResourceParser parser) {
return new CustomKey(res, parent, x, y, parser);
}
private void loadKeyboard(Context context, XmlResourceParser parser) {
boolean inKey = false;
boolean inRow = false;
boolean leftMostKey = false;
int row = 0;
int x = 0;
int y = 0;
CustomKey key = null;
CustomRow currentRow = null;
Resources res = context.getResources();
boolean skipRow = false;
try {
int event;
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
if (event == XmlResourceParser.START_TAG) {
String tag = parser.getName();
if (TAG_ROW.equals(tag)) {
//Log.d(LOG_TAG, "TAG ROW");
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)) {
//Log.d(LOG_TAG, "TAG KEY");
inKey = true;
key = createKeyFromXml(res, currentRow, x, y, parser);
mKeys.add(key);
if (key.codes != null) {
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;
row++;
} else {
// TODO: error or extend?
}
}
}
} catch (Exception e) {
Log.e(LOG_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.CustomKeyboard);
mDefaultWidth = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_keyWidth,
mDisplayWidth, mDisplayWidth / 10);
mDefaultHeight = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_keyHeight,
mDisplayHeight, 50);
mDefaultHorizontalGap = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_horizontalGap,
mDisplayWidth, 0);
mDefaultVerticalGap = getDimensionOrFraction(a,
R.styleable.CustomKeyboard_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

View File

@ -8,7 +8,7 @@ public class EditableAccommodatingLatinIMETypeNullIssues extends SpannableString
}
//This character must be ignored by your onKey() code.
public static final CharSequence ONE_UNPROCESSED_CHARACTER = "/";
public static final CharSequence ONE_UNPROCESSED_CHARACTER = "\\";
@Override
public SpannableStringBuilder replace(final int spannableStringStart, final int spannableStringEnd, CharSequence replacementSequence, int replacementStart, int replacementEnd) {
@ -22,7 +22,7 @@ public class EditableAccommodatingLatinIMETypeNullIssues extends SpannableString
super.replace(0, length(), "", 0, 0);
//We DO care about preserving the new stuff that is replacing the stuff in the
// editable, because this stuff might be sent to us as a keydown event. So, we
// editable, because this stuff might be sent to us as a keyDown event. So, we
// insert the new stuff (typically, a single character) into the now-empty editable,
// and return the result to the caller.
return super.replace(0, 0, replacementSequence, replacementStart, replacementEnd);

View File

@ -6,6 +6,7 @@ import android.text.Editable;
import android.text.InputType;
import android.text.Selection;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
@ -35,6 +36,89 @@ public class EditableSurfaceView extends SurfaceView {
_context = context;
}
@Override
public boolean onKeyDown(int keyCode, final KeyEvent event) {
Log.d(ScummVM.LOG_TAG, "onKeyDown - EditableSurface!!!"); // Called
if( keyCode == KeyEvent.KEYCODE_BACK ) {
if( ScummVMActivity.keyboardWithoutTextInputShown ) {
return true;
}
}
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
// Don't handle these
return false;
}
// Let our event manager handle it (ScummVMEventsBase class)
return super.dispatchKeyEvent(event);
//return false;
// This did not work
//return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, final KeyEvent event) {
Log.d(ScummVM.LOG_TAG, "onKeyUp - EditableSurface!!!");
if( keyCode == KeyEvent.KEYCODE_BACK ) {
if( ScummVMActivity.keyboardWithoutTextInputShown ) {
((ScummVMActivity) _context).showScreenKeyboardWithoutTextInputField(0); // Hide keyboard
return true;
}
}
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
// Don't handle these
return false;
}
// Let our event manager handle it (ScummVMEventsBase class)
return super.dispatchKeyEvent(event);
//return false;
// This did not work
//return super.onKeyUp(keyCode, event);
}
@Override
public boolean onTouchEvent(final MotionEvent event) {
if( ScummVMActivity.keyboardWithoutTextInputShown && ((ScummVMActivity) _context)._screenKeyboard != null &&
((ScummVMActivity) _context)._screenKeyboard.getY() <= event.getY() ) {
event.offsetLocation(-((ScummVMActivity) _context)._screenKeyboard.getX(), -((ScummVMActivity) _context)._screenKeyboard.getY());
((ScummVMActivity) _context)._screenKeyboard.onTouchEvent(event);
return true;
}
if( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH ) {
if (getX() != 0) {
event.offsetLocation(-getX(), -getY());
}
}
// TODO what is this for?
//DifferentTouchInput.touchInput.process(event);
// // Despite the LINT warning "ScummVMEvents#onTouch should call View#performClick when a click is detected"
// // we deal with this in our touch event handler (in ScummVMEventsBase class
// switch (event.getAction()) {
// case MotionEvent.ACTION_UP:
// performClick();
// break;
// case MotionEvent.ACTION_DOWN:
// // fall through
// default:
// break;
// }
// super.onTouchEvent(event);
return true;
// This causes a crash if we dispatch to super
// Let our event manager handle it (ScummVMEvents class)
// return super.dispatchTouchEvent(event);
}
// Deal with LINT warning: Custom view `SurfaceView` has setOnTouchListener called on it but does not override performClick (in ScummVMActivity.java)
@Override
public boolean performClick() {
@ -307,4 +391,32 @@ public class EditableSurfaceView extends SurfaceView {
public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) {
return PointerIcon.getSystemIcon(_context, PointerIcon.TYPE_NULL);
}
public void captureMouse(boolean capture) {
final boolean bGlobalsHideSystemMousePointer = true;
if (capture) {
setFocusableInTouchMode(true);
setFocusable(true);
requestFocus();
if (bGlobalsHideSystemMousePointer && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ) {
postDelayed( new Runnable() {
public void run()
{
Log.v(ScummVM.LOG_TAG, "captureMouse::requestPointerCapture() delayed");
requestPointerCapture();
}
}, 50 );
}
} else {
if (bGlobalsHideSystemMousePointer && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ) {
postDelayed( new Runnable() {
public void run() {
Log.v(ScummVM.LOG_TAG, "captureMouse::releasePointerCapture()");
releasePointerCapture();
}
}, 50 );
}
}
}
}

View File

@ -11,8 +11,8 @@ import android.view.View;
* Contains helper methods for mouse/hover events that were introduced in Android 4.0.
*/
public class MouseHelper {
private View.OnHoverListener _listener;
private ScummVM _scummvm;
private final View.OnHoverListener _listener;
private final ScummVM _scummvm;
private boolean _rmbPressed;
private boolean _lmbPressed;
private boolean _mmbPressed;
@ -110,7 +110,7 @@ public class MouseHelper {
boolean lmbDown = (buttonState & MotionEvent.BUTTON_PRIMARY) == MotionEvent.BUTTON_PRIMARY;
if (!hover && e.getAction() != MotionEvent.ACTION_UP && buttonState == 0) {
// On some device types, ButtonState is 0 even when tapping on the touchpad or using the stylus on the screen etc.
// On some device types, ButtonState is 0 even when tapping on the touch-pad or using the stylus on the screen etc.
lmbDown = true;
}

View File

@ -11,6 +11,8 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.graphics.Rect;
//import android.inputmethodservice.Keyboard;
//import android.inputmethodservice.KeyboardView;
import android.media.AudioManager;
import android.net.Uri;
import android.net.wifi.WifiInfo;
@ -21,14 +23,17 @@ import android.os.Environment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;
@ -49,6 +54,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;
//import android.os.Environment;
//import java.util.List;
@ -92,12 +98,280 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
}
}
//
// --------------------------------------------------------------------------------------------------------------------------------------------
// Code for emulated in-app keyboard largely copied
// from https://github.com/pelya/commandergenius/tree/sdl_android/project
//
FrameLayout _videoLayout = null;
private EditableSurfaceView _main_surface = null;
private ImageView _toggleKeyboardBtnIcon = null;
public View _screenKeyboard = null;
static boolean keyboardWithoutTextInputShown = false;
// boolean _isPaused = false;
private InputMethodManager _inputManager = null;
private final int[][] TextInputKeyboardList =
{
{ 0, R.xml.qwerty },
{ 0, R.xml.qwerty_shift },
{ 0, R.xml.qwerty_alt },
{ 0, R.xml.qwerty_alt_shift }
};
public void showScreenKeyboardWithoutTextInputField(final int keyboard) {
if (_main_surface != null) {
_inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (!keyboardWithoutTextInputShown) {
keyboardWithoutTextInputShown = true;
runOnUiThread(new Runnable() {
public void run() {
_main_surface.captureMouse(false);
if (keyboard == 0) {
// TODO do we need SHOW_FORCED HERE?
//_inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
//_inputManager.showSoftInput(_main_surface, InputMethodManager.SHOW_FORCED);
_inputManager.toggleSoftInputFromWindow(_main_surface.getWindowToken(), InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_IMPLICIT_ONLY);
_inputManager.showSoftInput(_main_surface, InputMethodManager.SHOW_IMPLICIT);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} else {
if (_screenKeyboard != null) {
return;
}
class BuiltInKeyboardView extends CustomKeyboardView {
public boolean shift = false;
public boolean alt = false;
public final TreeSet<Integer> stickyKeys = new TreeSet<>();
public BuiltInKeyboardView(Context context, android.util.AttributeSet attrs) {
super(context, attrs);
}
public boolean onKeyDown(int key, final KeyEvent event) {
Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - onKeyDown()" );
return false;
}
public boolean onKeyUp(int key, final KeyEvent event) {
Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - onKeyUp()" );
return false;
}
public void ChangeKeyboard() {
// Called when bringing up the keyboard
// or pressing one of the special keyboard keys that change the layout (eg "123...")
//
int idx = (shift ? 1 : 0) + (alt ? 2 : 0);
setKeyboard(new CustomKeyboard(ScummVMActivity.this, TextInputKeyboardList[idx][keyboard]));
setPreviewEnabled(false);
setProximityCorrectionEnabled(false);
for (CustomKeyboard.CustomKey k: getKeyboard().getKeys()) {
if (stickyKeys.contains(k.codes[0])) {
k.on = true;
invalidateAllKeys();
}
}
}
}
final BuiltInKeyboardView builtinKeyboard = new BuiltInKeyboardView(ScummVMActivity.this, null);
builtinKeyboard.setAlpha(0.7f);
builtinKeyboard.ChangeKeyboard();
builtinKeyboard.setOnKeyboardActionListener(new CustomKeyboardView.OnKeyboardActionListener() {
public void onPress(int key) {
Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - onPress key: " + key ); // CALLED
if (key == KeyEvent.KEYCODE_BACK) {
return;
}
if (key < 0) {
return;
}
for (CustomKeyboard.CustomKey k: builtinKeyboard.getKeyboard().getKeys()) {
if (k.sticky && key == k.codes[0])
return;
}
if (key > 100000) {
key -= 100000;
_main_surface.onKeyDown(KeyEvent.KEYCODE_SHIFT_LEFT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT));
}
_main_surface.onKeyDown(key, new KeyEvent(KeyEvent.ACTION_DOWN, key)); // calls onKeyDown - EditableSurface!!!
}
public void onRelease(int key) {
Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - onRelease key: " + key );
if (key == KeyEvent.KEYCODE_BACK) {
builtinKeyboard.setOnKeyboardActionListener(null);
showScreenKeyboardWithoutTextInputField(0); // Hide keyboard
return;
}
if (key == CustomKeyboard.KEYCODE_SHIFT) {
builtinKeyboard.shift = ! builtinKeyboard.shift;
if (builtinKeyboard.shift && !builtinKeyboard.alt)
_main_surface.onKeyDown(KeyEvent.KEYCODE_SHIFT_LEFT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT));
else
_main_surface.onKeyUp(KeyEvent.KEYCODE_SHIFT_LEFT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT));
builtinKeyboard.ChangeKeyboard();
return;
}
if (key == CustomKeyboard.KEYCODE_ALT) {
builtinKeyboard.alt = ! builtinKeyboard.alt;
if (builtinKeyboard.alt)
_main_surface.onKeyUp(KeyEvent.KEYCODE_SHIFT_LEFT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT));
else
builtinKeyboard.shift = false;
builtinKeyboard.ChangeKeyboard();
return;
}
if (key < 0) {
return;
}
for (CustomKeyboard.CustomKey k: builtinKeyboard.getKeyboard().getKeys()) {
if (k.sticky && key == k.codes[0]) {
if (k.on) {
builtinKeyboard.stickyKeys.add(key);
_main_surface.onKeyDown(key, new KeyEvent(KeyEvent.ACTION_DOWN, key));
} else {
builtinKeyboard.stickyKeys.remove(key);
_main_surface.onKeyUp(key, new KeyEvent(KeyEvent.ACTION_UP, key));
}
return;
}
}
boolean shifted = false;
if (key > 100000) {
key -= 100000;
shifted = true;
}
_main_surface.onKeyUp(key, new KeyEvent(KeyEvent.ACTION_UP, key));
if (shifted) {
_main_surface.onKeyUp(KeyEvent.KEYCODE_SHIFT_LEFT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT));
builtinKeyboard.stickyKeys.remove(KeyEvent.KEYCODE_SHIFT_LEFT);
for (CustomKeyboard.CustomKey k: builtinKeyboard.getKeyboard().getKeys())
{
if (k.sticky && k.codes[0] == KeyEvent.KEYCODE_SHIFT_LEFT && k.on)
{
k.on = false;
builtinKeyboard.invalidateAllKeys();
}
}
}
}
public void onText(CharSequence p1) {}
public void swipeLeft() {}
public void swipeRight() {}
public void swipeDown() {}
public void swipeUp() {}
public void onKey(int p1, int[] p2) {}
});
_screenKeyboard = builtinKeyboard;
// TODO better to have specific dimensions in dp and not adjusted to parent
// it may resolve the issue of resizing the keyboard wrongly (smaller) when returning to the suspended Activity in low resolution
FrameLayout.LayoutParams sKeyboardLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
_videoLayout.addView(_screenKeyboard, sKeyboardLayout);
_videoLayout.bringChildToFront(_screenKeyboard);
Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 005" );
}
}
});
} else {
keyboardWithoutTextInputShown = false;
runOnUiThread(new Runnable() {
public void run() {
if (_screenKeyboard != null ) {
_videoLayout.removeView(_screenKeyboard);
_screenKeyboard = null;
}
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
// TODO do we need this instead?
// _inputManager.hideSoftInputFromWindow(_main_surface.getWindowToken(), 0);
_inputManager.hideSoftInputFromWindow(_main_surface.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
DimSystemStatusBar.get().dim(_videoLayout);
//DimSystemStatusBar.get().dim(_main_surface);
_main_surface.captureMouse(true);
}
});
}
// TODO Do we need to inform native ScummVM code of keyboard shown state?
// _main_surface.nativeScreenKeyboardShown( keyboardWithoutTextInputShown ? 1 : 0 );
}
}
public void showScreenKeyboard() {
final boolean bGlobalsCompatibilityHacksTextInputEmulatesHwKeyboard = true;
final int dGlobalsTextInputKeyboard = 1;
if (_main_surface != null) {
if (bGlobalsCompatibilityHacksTextInputEmulatesHwKeyboard) {
showScreenKeyboardWithoutTextInputField(dGlobalsTextInputKeyboard);
Log.d(ScummVM.LOG_TAG, "showScreenKeyboard - showScreenKeyboardWithoutTextInputField()");
_main_surface.captureMouse(false);
return;
}
Log.d(ScummVM.LOG_TAG, "showScreenKeyboard: YOU SHOULD NOT SEE ME!!!");
// // TODO redundant ?
// if (_screenKeyboard != null) {
// return;
// }
//
}
}
public void hideScreenKeyboard() {
final int dGlobalsTextInputKeyboard = 1;
if (_main_surface != null) {
if (keyboardWithoutTextInputShown) {
showScreenKeyboardWithoutTextInputField(dGlobalsTextInputKeyboard);
_main_surface.captureMouse(true);
}
}
}
public void toggleScreenKeyboard() {
if (isScreenKeyboardShown()) {
hideScreenKeyboard();
} else {
showScreenKeyboard();
}
}
public boolean isScreenKeyboardShown()
{
return _screenKeyboard != null;
}
//
// END OF new screenKeyboardCode
// ---------------------------------------------------------------------------------------------------------------------------
//
public final View.OnClickListener keyboardBtnOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
runOnUiThread(new Runnable() {
public void run() {
toggleKeyboard();
toggleScreenKeyboard();
}
});
}
@ -188,7 +462,12 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
protected void showVirtualKeyboard(final boolean enable) {
runOnUiThread(new Runnable() {
public void run() {
showKeyboard(enable);
//showKeyboard(enable);
if (enable) {
showScreenKeyboard();
} else {
hideScreenKeyboard();
}
}
});
}
@ -197,7 +476,7 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
protected void showKeyboardControl(final boolean enable) {
runOnUiThread(new Runnable() {
public void run() {
showKeyboardView(enable);
showToggleKeyboardBtnIcon(enable);
}
});
}
@ -254,14 +533,41 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
hideSystemUI();
_videoLayout = new FrameLayout(this);
SetLayerType.get().setLayerType(_videoLayout);
setContentView(_videoLayout);
_videoLayout.setFocusable(true);
_videoLayout.setFocusableInTouchMode(true);
_videoLayout.requestFocus();
_main_surface = new EditableSurfaceView(this);
SetLayerType.get().setLayerType(_main_surface);
_videoLayout.addView(_main_surface, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
_toggleKeyboardBtnIcon = new ImageView(this);
_toggleKeyboardBtnIcon.setImageResource(R.drawable.ic_action_keyboard);
FrameLayout.LayoutParams keybrdBtnlayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.TOP | Gravity.END);
keybrdBtnlayout.setMarginEnd(15);
keybrdBtnlayout.topMargin = 15;
keybrdBtnlayout.rightMargin = 15;
_videoLayout.addView(_toggleKeyboardBtnIcon, keybrdBtnlayout);
_videoLayout.bringChildToFront(_toggleKeyboardBtnIcon);
_main_surface.captureMouse(true);
// REDUNDANT?
if ( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N ) {
_main_surface.setPointerIcon(android.view.PointerIcon.getSystemIcon(this, android.view.PointerIcon.TYPE_NULL));
}
// TODO is this redundant since we call hideSystemUI() ?
DimSystemStatusBar.get().dim(_videoLayout);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
setContentView(R.layout.main);
// TODO needed?
takeKeyEvents(true);
EditableSurfaceView main_surface = findViewById(R.id.main_surface);
main_surface.requestFocus();
_clipboardManager = (android.content.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
_currentScummVMVersion = new Version(BuildConfig.VERSION_NAME);
Log.d(ScummVM.LOG_TAG, "Current ScummVM version running is: " + _currentScummVMVersion.getDescription() + " (" + _currentScummVMVersion.get() + ")");
@ -279,7 +585,7 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
// so app's internal space (which would be deleted on uninstall) was set as WORLD_READABLE which is no longer supported in newer versions of Android API
// In newer APIs we can set that path as Context.MODE_PRIVATE which is the default - but this makes the files inaccessible to other apps
_scummvm = new MyScummVM(main_surface.getHolder());
_scummvm = new MyScummVM(_main_surface.getHolder());
//
// seekAndInitScummvmConfiguration() returns false if something went wrong
@ -308,7 +614,7 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
Log.d(ScummVM.LOG_TAG, "Hover available: " + _hoverAvailable);
if (_hoverAvailable) {
_mouseHelper = new MouseHelper(_scummvm);
_mouseHelper.attach(main_surface);
_mouseHelper.attach(_main_surface);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
@ -318,13 +624,14 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
}
// On screen button listener
findViewById(R.id.show_keyboard).setOnClickListener(keyboardBtnOnClickListener);
//findViewById(R.id.show_keyboard).setOnClickListener(keyboardBtnOnClickListener);
_toggleKeyboardBtnIcon.setOnClickListener(keyboardBtnOnClickListener);
// Keyboard visibility listener
// Keyboard visibility listener - mainly to hide system UI if keyboard is shown and we return from Suspend to the Activity
setKeyboardVisibilityListener(this);
main_surface.setOnKeyListener(_events);
main_surface.setOnTouchListener(_events);
_main_surface.setOnKeyListener(_events);
_main_surface.setOnTouchListener(_events);
_scummvm_thread = new Thread(_scummvm, "ScummVM");
_scummvm_thread.start();
@ -342,6 +649,8 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
public void onResume() {
Log.d(ScummVM.LOG_TAG, "onResume");
// _isPaused = false;
super.onResume();
if (_scummvm != null)
@ -353,6 +662,8 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
public void onPause() {
Log.d(ScummVM.LOG_TAG, "onPause");
// _isPaused = true;
super.onPause();
if (_scummvm != null)
@ -387,7 +698,10 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
_scummvm = null;
}
showKeyboardView(false);
if (isScreenKeyboardShown()) {
hideScreenKeyboard();
}
showToggleKeyboardBtnIcon(false);
}
@ -463,7 +777,6 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
}
}
// TODO setSystemUiVisibility is introduced in API 11 and deprecated in API 30 - When we move to API 30 we will have to replace this code
// https://developer.android.com/training/system-ui/immersive.html#java
//
@ -499,53 +812,61 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
// | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
// }
// Show or hide the Android keyboard.
// Called by the override of showVirtualKeyboard()
@TargetApi(Build.VERSION_CODES.CUPCAKE)
private void showKeyboard(boolean show) {
SurfaceView main_surface = findViewById(R.id.main_surface);
InputMethodManager imm = (InputMethodManager)
getSystemService(INPUT_METHOD_SERVICE);
if (show) {
imm.showSoftInput(main_surface, InputMethodManager.SHOW_IMPLICIT);
} else {
imm.hideSoftInputFromWindow(main_surface.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
}
}
// Toggle showing or hiding the virtual keyboard.
// Called by keyboardBtnOnClickListener()
@TargetApi(Build.VERSION_CODES.CUPCAKE)
private void toggleKeyboard() {
SurfaceView main_surface = findViewById(R.id.main_surface);
InputMethodManager imm = (InputMethodManager)
getSystemService(INPUT_METHOD_SERVICE);
imm.toggleSoftInputFromWindow(main_surface.getWindowToken(),
InputMethodManager.SHOW_IMPLICIT,
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
// // Show or hide the Android keyboard.
// // Called by the override of showVirtualKeyboard()
// @TargetApi(Build.VERSION_CODES.CUPCAKE)
// private void showKeyboard(boolean show) {
// //SurfaceView main_surface = findViewById(R.id.main_surface);
// if (_main_surface != null) {
//
// InputMethodManager imm = (InputMethodManager)
// getSystemService(INPUT_METHOD_SERVICE);
//
// if (show) {
// imm.showSoftInput(_main_surface, InputMethodManager.SHOW_IMPLICIT);
// } else {
// imm.hideSoftInputFromWindow(_main_surface.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
// }
// }
// }
//
// // Toggle showing or hiding the virtual keyboard.
// // Called by keyboardBtnOnClickListener()
// @TargetApi(Build.VERSION_CODES.CUPCAKE)
// private void toggleKeyboard() {
// //SurfaceView main_surface = findViewById(R.id.main_surface);
// if (_main_surface != null ) {
// InputMethodManager imm = (InputMethodManager)
// getSystemService(INPUT_METHOD_SERVICE);
//
// imm.toggleSoftInputFromWindow(_main_surface.getWindowToken(),
// InputMethodManager.SHOW_IMPLICIT,
// InputMethodManager.HIDE_IMPLICIT_ONLY);
// }
// }
// Show or hide the semi-transparent keyboard btn (which is used to explicitly bring up the android keyboard).
// Called by the override of showKeyboardControl()
private void showKeyboardView(boolean show) {
ImageView keyboardBtn = findViewById(R.id.show_keyboard);
if (show) {
keyboardBtn.setVisibility(View.VISIBLE);
} else {
keyboardBtn.setVisibility(View.GONE);
private void showToggleKeyboardBtnIcon(boolean show) {
//ImageView keyboardBtn = findViewById(R.id.show_keyboard);
if (_toggleKeyboardBtnIcon != null ) {
if (show) {
_toggleKeyboardBtnIcon.setVisibility(View.VISIBLE);
} else {
_toggleKeyboardBtnIcon.setVisibility(View.GONE);
}
}
}
private void showMouseCursor(boolean show) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// Android N (Nougat) is Android 7.0
SurfaceView main_surface = findViewById(R.id.main_surface);
int type = show ? PointerIcon.TYPE_DEFAULT : PointerIcon.TYPE_NULL;
// https://stackoverflow.com/a/55482761
main_surface.setPointerIcon(PointerIcon.getSystemIcon(this, type));
//SurfaceView main_surface = findViewById(R.id.main_surface);
if (_main_surface != null) {
int type = show ? PointerIcon.TYPE_DEFAULT : PointerIcon.TYPE_NULL;
// https://stackoverflow.com/a/55482761
_main_surface.setPointerIcon(PointerIcon.getSystemIcon(this, type));
}
} else {
/* Currently hiding the system mouse cursor is only
supported on OUYA. If other systems provide similar
@ -562,29 +883,31 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
// https://stackoverflow.com/a/36259261
private void setKeyboardVisibilityListener(final OnKeyboardVisibilityListener onKeyboardVisibilityListener) {
final View parentView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
if (parentView != null) {
parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
private boolean alreadyOpen;
private final int defaultKeyboardHeightDP = 100;
private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
private final Rect rect = new Rect();
private boolean alreadyOpen;
private final int defaultKeyboardHeightDP = 100;
private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
private final Rect rect = new Rect();
@TargetApi(Build.VERSION_CODES.CUPCAKE)
@Override
public void onGlobalLayout() {
int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
parentView.getWindowVisibleDisplayFrame(rect);
int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
boolean isShown = heightDiff >= estimatedKeyboardHeight;
@TargetApi(Build.VERSION_CODES.CUPCAKE)
@Override
public void onGlobalLayout() {
int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
parentView.getWindowVisibleDisplayFrame(rect);
int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
boolean isShown = heightDiff >= estimatedKeyboardHeight;
if (isShown == alreadyOpen) {
Log.i("Keyboard state", "Ignoring global layout change...");
return;
if (isShown == alreadyOpen) {
Log.i(ScummVM.LOG_TAG, "Keyboard state:: ignoring global layout change...");
return;
}
alreadyOpen = isShown;
onKeyboardVisibilityListener.onVisibilityChanged(isShown);
}
alreadyOpen = isShown;
onKeyboardVisibilityListener.onVisibilityChanged(isShown);
}
});
});
}
}
@Override
@ -699,7 +1022,7 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
//
// If the dir can't be reached, it will print a warning!
//
// Log.w(TAG, "Failed to ensure directory: " + dir);
// Log.w(ScummVM.LOG_TAG, "Failed to ensure directory: " + dir);
// dir = null;
//
// So, if your device has two sdcard paths, it will produce two dirs. If one is not available, the warning will come up.
@ -1384,5 +1707,76 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
}
}
}
} // end of ScummVMActivity
// *** HONEYCOMB / ICS FIX FOR FULLSCREEN MODE, by lmak ***
// TODO DimSystemStatusBar may be redundant for us
abstract class DimSystemStatusBar {
final boolean bGlobalsImmersiveMode = true;
public static DimSystemStatusBar get() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
return DimSystemStatusBarHoneycomb.Holder.sInstance;
} else {
return DimSystemStatusBarDummy.Holder.sInstance;
}
}
public abstract void dim(final View view);
private static class DimSystemStatusBarHoneycomb extends DimSystemStatusBar {
private static class Holder {
private static final DimSystemStatusBarHoneycomb sInstance = new DimSystemStatusBarHoneycomb();
}
public void dim(final View view) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && bGlobalsImmersiveMode) {
// Immersive mode, I already hear curses when system bar reappears mid-game from the slightest swipe at the bottom of the screen
view.setSystemUiVisibility(android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | android.view.View.SYSTEM_UI_FLAG_FULLSCREEN);
} else {
view.setSystemUiVisibility(android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
}
}
private static class DimSystemStatusBarDummy extends DimSystemStatusBar {
private static class Holder {
private static final DimSystemStatusBarDummy sInstance = new DimSystemStatusBarDummy();
}
public void dim(final View view) { }
}
}
abstract class SetLayerType {
public static SetLayerType get() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
return SetLayerTypeHoneycomb.Holder.sInstance;
} else {
return SetLayerTypeDummy.Holder.sInstance;
}
}
public abstract void setLayerType(final View view);
private static class SetLayerTypeHoneycomb extends SetLayerType {
private static class Holder {
private static final SetLayerTypeHoneycomb sInstance = new SetLayerTypeHoneycomb();
}
public void setLayerType(final View view) {
view.setLayerType(android.view.View.LAYER_TYPE_NONE, null);
//view.setLayerType(android.view.View.LAYER_TYPE_HARDWARE, null);
}
}
private static class SetLayerTypeDummy extends SetLayerType {
private static class Holder {
private static final SetLayerTypeDummy sInstance = new SetLayerTypeDummy();
}
public void setLayerType(final View view) { }
}
}

View File

@ -4,6 +4,7 @@ import android.os.Handler;
import android.os.Message;
import android.content.Context;
//import android.util.Log;
import android.util.Log;
import android.view.KeyEvent;
import android.view.KeyCharacterMap;
import android.view.MotionEvent;
@ -11,7 +12,7 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodManager;
//import android.view.inputmethod.InputMethodManager;
import androidx.annotation.NonNull;
@ -103,13 +104,14 @@ public class ScummVMEventsBase implements
private void handleEVHMessage(final Message msg) {
if (msg.what == MSG_SMENU_LONG_PRESS) {
// this displays the android keyboard (see showVirtualKeyboard() in ScummVMActivity.java)
// this toggles the android keyboard (see showVirtualKeyboard() in ScummVMActivity.java)
// when menu key is long-pressed
InputMethodManager imm = (InputMethodManager)
_context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null)
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
// InputMethodManager imm = (InputMethodManager)
// _context.getSystemService(Context.INPUT_METHOD_SERVICE);
//
// if (imm != null)
// imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
((ScummVMActivity) _context).toggleScreenKeyboard();
} else if (msg.what == MSG_SBACK_LONG_PRESS) {
_scummvm.pushEvent(JE_SYS_KEY, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU, 0, 0, 0, 0);
_scummvm.pushEvent(JE_SYS_KEY, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU, 0, 0, 0, 0);
@ -142,6 +144,7 @@ public class ScummVMEventsBase implements
// OnKeyListener
@Override
final public boolean onKey(View v, int keyCode, KeyEvent e) {
//Log.d(ScummVM.LOG_TAG, "SCUMMV-EVENTS-BASE - onKEY");
final int action = e.getAction();
if (e.getUnicodeChar() == (int)EditableAccommodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.charAt(0)) {
@ -287,6 +290,7 @@ public class ScummVMEventsBase implements
// OnTouchListener
@Override
final public boolean onTouch(View v, MotionEvent e) {
//Log.d(ScummVM.LOG_TAG, "SCUMMV-EVENTS-BASE - onTOUCH");
if (_mouseHelper != null) {
boolean isMouse = MouseHelper.isMouse(e);
@ -324,6 +328,7 @@ public class ScummVMEventsBase implements
// OnGestureListener
@Override
final public boolean onDown(MotionEvent e) {
//Log.d(ScummVM.LOG_TAG, "SCUMMV-EVENTS-BASE - onDONW");
_scummvm.pushEvent(JE_DOWN, (int)e.getX(), (int)e.getY(), 0, 0, 0, 0);
return true;
}

View File

@ -54,8 +54,8 @@ public class SplashActivity extends Activity {
int numOfReqPermsGranted = 0;
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0) {
for (int iterGrantResult: grantResults) {
if (iterGrantResult == PackageManager.PERMISSION_GRANTED) {
for (int iterateGrantResult: grantResults) {
if (iterateGrantResult == PackageManager.PERMISSION_GRANTED) {
Log.i(ScummVM.LOG_TAG, permissions[0] + " permission was granted at Runtime");
++numOfReqPermsGranted;
} else {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1015 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:state_focused="false"
android:drawable="@drawable/btn_close_normal" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_close_pressed" />
<item android:state_focused="true"
android:drawable="@drawable/btn_close_selected" />
</selector>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Toggle keys. Use checkable/checked state. -->
<item android:state_checkable="true" android:state_checked="true"
android:state_pressed="true"
android:drawable="@drawable/btn_keyboard_key_pressed_on" />
<item android:state_checkable="true" android:state_pressed="true"
android:drawable="@drawable/btn_keyboard_key_pressed_off" />
<item android:state_checkable="true" android:state_checked="true"
android:drawable="@drawable/btn_keyboard_key_normal_on" />
<item android:state_checkable="true"
android:drawable="@drawable/btn_keyboard_key_normal_off" />
<!-- Normal keys -->
<item android:state_pressed="true"
android:drawable="@drawable/btn_keyboard_key_pressed" />
<item
android:drawable="@drawable/btn_keyboard_key_normal" />
</selector>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_long_pressable="true"
android:drawable="@drawable/keyboard_key_feedback_more_background" />
<item android:drawable="@drawable/keyboard_key_feedback_background" />
</selector>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** 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.
*/
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="80sp"
android:textSize="40sp"
android:textColor="?android:attr/textColorPrimaryInverse"
android:minWidth="32dip"
android:gravity="center"
android:background="@drawable/keyboard_key_feedback"
/>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** 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.
*/
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/keyboard_popup_panel_background"
>
<!-- Excluded attribute due to error: (layout should not include itself) android:popupLayout="@layout/keyboard_popup_keyboard" -->
<!-- Removed attribute due to invalid for LinearLayout android:layout_alignParentBottom="true" -->
<org.scummvm.scummvm.CustomKeyboardView
android:id="@android:id/keyboardView"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:keyPreviewLayout="@layout/keyboard_key_preview"
android:keyTextSize="22sp"
/>
<ImageButton android:id="@android:id/closeButton"
android:background="@android:color/transparent"
android:src="@drawable/btn_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:clickable="true"
android:layout_marginLeft="8dp"
android:focusable="true"
android:contentDescription="@string/customkeyboardview_popup_close" />
</LinearLayout>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.scummvm.scummvm.EditableSurfaceView
android:id="@+id/main_surface"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:keepScreenOn="true"
android:focusable="true"
android:focusableInTouchMode="true"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_keyboard"
android:id="@+id/show_keyboard"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="15dp"
android:layout_marginEnd="15dp"
android:layout_marginTop="15dp"
android:contentDescription="@string/keyboard_toggle_btn_desc" />
</RelativeLayout>

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project
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.
-->
<!-- Formatting note: terminate all comments with a period, to avoid breaking
the documentation output. To suppress comment lines from the documentation
output, insert an eat-comment element after the comment lines.
-->
<resources>
<!-- These are the standard attributes that make up a complete theme. -->
<declare-styleable name="CustomKeyboardView">
<!-- Default background dim amount when a menu, dialog, or something similar pops up. -->
<attr name="backgroundDimAmount" format="float" />
<!-- 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" />
<attr name="shadowColor" format="color" />
<attr name="shadowRadius" format="float" />
</declare-styleable>
<declare-styleable name="CustomKeyboardViewPreviewState">
<!-- State for {@link android.inputmethodservice.KeyboardView KeyboardView}
key preview background. -->
<attr name="state_long_pressable" format="boolean" />
</declare-styleable>
<declare-styleable name="CustomKeyboard">
<!-- 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="CustomKeyboard_CustomRow">
<!-- 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="CustomKeyboard_CustomKey">
<!-- 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. -->
<flag name="left" value="1" />
<!-- Key is anchored to the right of the keyboard. -->
<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>

View File

@ -11,5 +11,6 @@
<!-- <color name="colorAccent">#FFCC5500</color> -->
<color name="colorBackground">#FFCC6600</color>
<!-- <color name="blurb">#FF000000</color> -->
</resources>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2009, The Android Open Source Project
**
** 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.
*
* Copy from AOSP
*/
-->
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. Do not translate.
NOTE: The naming convention is "config_camelCaseValue". Some legacy
entries do not follow the convention, but all new entries should. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Enables swipe versus poly-finger touch disambiguation in the KeyboardView -->
<bool name="config_swipeDisambiguation">true</bool>
</resources>

View File

@ -34,4 +34,23 @@
<string name="keyboard_toggle_btn_desc">Toggle virtual keyboard</string>
<!-- <string name="title_activity_splash">ScummVM Logo Activity</string> -->
<!-- <string name="title_activity_main">ScummVM Main Activity</string> -->
<!-- Copy from AOSP (Android Open Source Project) -->
<!-- CustomKeyboardView - accessibility support -->
<!-- Description of the Alt button in a KeyboardView. [CHAR LIMIT=NONE] -->
<string name="customkeyboardview_keycode_alt">Alt</string>
<!-- Description of the Cancel button in a KeyboardView. [CHAR LIMIT=NONE] -->
<string name="customkeyboardview_keycode_cancel">Cancel</string>
<!-- Description of the Delete button in a KeyboardView. [CHAR LIMIT=NONE] -->
<string name="customkeyboardview_keycode_delete">Delete</string>
<!-- Description of the Done button in a KeyboardView. [CHAR LIMIT=NONE] -->
<string name="customkeyboardview_keycode_done">Done</string>
<!-- Description of the Mode change button in a KeyboardView. [CHAR LIMIT=NONE] -->
<string name="customkeyboardview_keycode_mode_change">Mode change</string>
<!-- Description of the Shift button in a KeyboardView. [CHAR LIMIT=NONE] -->
<string name="customkeyboardview_keycode_shift">Shift</string>
<!-- Description of the Enter button in a KeyboardView. [CHAR LIMIT=NONE] -->
<string name="customkeyboardview_keycode_enter">Enter</string>
<!-- End of copy from AOSP -->
<string name="customkeyboardview_popup_close">Close popup</string>
</resources>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project
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.
-->
<resources>
<!-- Style largely taken from AOSP (frameworks/base/core/res/values) for Widget.KeyboardView -->
<style name="View.CustomKeyboard" parent="android:Widget" >
<!-- View's background attribute (reference (a drawable) or color) -->
<!-- A drawable to use as the background. This can be either a reference
to a full drawable resource (such as a PNG image, 9-patch,
XML state list description, etc), or a solid color such as "#ff000000"
(black). -->
<!-- drawables for keyboard taken from AOSP (frameworks/base/core/res) -->
<item name="android:background">@drawable/keyboard_background</item>
<item name="keyBackground">@drawable/btn_keyboard_key</item>
<item name="keyTextSize">22sp</item>
<item name="keyTextColor">#FFFFFFFF</item>
<item name="keyPreviewLayout">@layout/keyboard_key_preview</item>
<item name="keyPreviewOffset">-12dip</item>
<item name="keyPreviewHeight">80dip</item>
<item name="labelTextSize">14sp</item>
<item name="popupLayout">@layout/keyboard_popup_keyboard</item>
<item name="verticalCorrection">-10dip</item>
<item name="shadowColor">#BB000000</item>
<item name="shadowRadius">2.75</item>
</style>
</resources>

View File

@ -3,9 +3,11 @@
<style name="AppTheme" parent="@android:style/Theme.NoTitleBar.Fullscreen">
<item name="android:colorBackground">@color/colorBackground</item>
<item name="keyboardViewStyle">@style/View.CustomKeyboard</item>
</style>
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowBackground">@drawable/splash</item>
</style>
</resources>

View File

@ -0,0 +1,53 @@
<?xml version="1.0"?>
<!-- When creating new keyboard layout, add it to TextInputKeyboardList array in ScummVMActivity.java -->
<CustomKeyboard xmlns:scummvm="http://schemas.android.com/apk/res-auto"
scummvm:keyWidth="10%p"
scummvm:horizontalGap="0px"
scummvm:verticalGap="0px"
scummvm:keyHeight="10%p">
<CustomRow>
<CustomKey scummvm:codes="45" scummvm:keyLabel="q" scummvm:keyEdgeFlags="left" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="51" scummvm:keyLabel="w" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="33" scummvm:keyLabel="e" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="46" scummvm:keyLabel="r" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="48" scummvm:keyLabel="t" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="53" scummvm:keyLabel="y" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="49" scummvm:keyLabel="u" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="37" scummvm:keyLabel="i" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="43" scummvm:keyLabel="o" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="44" scummvm:keyLabel="p" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow>
<CustomKey scummvm:codes="29" scummvm:keyLabel="a" scummvm:keyEdgeFlags="left" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="47" scummvm:keyLabel="s" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="32" scummvm:keyLabel="d" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="34" scummvm:keyLabel="f" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="35" scummvm:keyLabel="g" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="36" scummvm:keyLabel="h" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="38" scummvm:keyLabel="j" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="39" scummvm:keyLabel="k" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="40" scummvm:keyLabel="l" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="74" scummvm:keyLabel=";" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow>
<CustomKey scummvm:codes="-1" scummvm:keyLabel="⇪" scummvm:keyEdgeFlags="left"/>
<CustomKey scummvm:codes="54" scummvm:keyLabel="z" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="52" scummvm:keyLabel="x" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="31" scummvm:keyLabel="c" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="50" scummvm:keyLabel="v" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="30" scummvm:keyLabel="b" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="42" scummvm:keyLabel="n" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="41" scummvm:keyLabel="m" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="67" scummvm:keyLabel="⬅" scummvm:keyWidth="20%p" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow rowEdgeFlags="bottom">
<CustomKey scummvm:codes="-6" scummvm:keyLabel="123…" scummvm:keyEdgeFlags="left"/>
<CustomKey scummvm:codes="71" scummvm:keyLabel="[" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="72" scummvm:keyLabel="]" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="76" scummvm:keyLabel="/" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="62" scummvm:keyLabel="Space" scummvm:keyWidth="20%p" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="55" scummvm:keyLabel="," scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="56" scummvm:keyLabel="." scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="66" scummvm:keyLabel="Enter" scummvm:keyWidth="20%p" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
</CustomKeyboard>

View File

@ -0,0 +1,56 @@
<?xml version="1.0"?>
<!-- When creating new keyboard layout, add it to TextInputKeyboardList array in ScummVMActivity.java -->
<CustomKeyboard xmlns:scummvm="http://schemas.android.com/apk/res-auto"
scummvm:keyWidth="10%p"
scummvm:horizontalGap="0px"
scummvm:verticalGap="0px"
scummvm:keyHeight="10%p">
<CustomRow>
<CustomKey scummvm:codes="8" scummvm:keyLabel="1" scummvm:keyEdgeFlags="left" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="9" scummvm:keyLabel="2" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="10" scummvm:keyLabel="3" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="11" scummvm:keyLabel="4" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="12" scummvm:keyLabel="5" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="13" scummvm:keyLabel="6" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="14" scummvm:keyLabel="7" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="15" scummvm:keyLabel="8" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="16" scummvm:keyLabel="9" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="7" scummvm:keyLabel="0" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow>
<CustomKey scummvm:codes="111" scummvm:keyLabel="Esc" scummvm:keyEdgeFlags="left" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="68" scummvm:keyLabel="`" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="69" scummvm:keyLabel="-" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="70" scummvm:keyLabel="=" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="73" scummvm:keyLabel="\\" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="124" scummvm:keyLabel="Ins" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="92" scummvm:keyLabel="PgUp" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="122" scummvm:keyLabel="Home" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="19" scummvm:keyLabel="↑" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="123" scummvm:keyLabel="End" scummvm:isRepeatable="true" scummvm:keyEdgeFlags="right" />
</CustomRow>
<CustomRow>
<CustomKey scummvm:codes="-1" scummvm:keyLabel="!@#…" scummvm:keyEdgeFlags="left"/>
<CustomKey scummvm:codes="75" scummvm:keyLabel="'" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100075" scummvm:keyLabel="&quot;" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="61" scummvm:keyLabel="Tab" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="115" scummvm:keyLabel="CapsLk" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="112" scummvm:keyLabel="Del" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="93" scummvm:keyLabel="PgDn" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="21" scummvm:keyLabel="←" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="20" scummvm:keyLabel="↓" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="22" scummvm:keyLabel="→" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow rowEdgeFlags="bottom">
<CustomKey scummvm:codes="-6" scummvm:keyLabel="abc…" scummvm:keyEdgeFlags="left"/>
<CustomKey scummvm:codes="59" scummvm:keyLabel="Shift" scummvm:isSticky="true"/>
<CustomKey scummvm:codes="113" scummvm:keyLabel="Ctrl" scummvm:isSticky="true"/>
<CustomKey scummvm:codes="117" scummvm:keyLabel="Meta" scummvm:isSticky="true"/>
<CustomKey scummvm:codes="57" scummvm:keyLabel="Alt" scummvm:isSticky="true"/>
<CustomKey scummvm:codes="58" scummvm:keyLabel="Alt" scummvm:isSticky="true"/>
<CustomKey scummvm:codes="118" scummvm:keyLabel="Meta" scummvm:isSticky="true"/>
<CustomKey scummvm:codes="226" scummvm:keyLabel="Menu" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="114" scummvm:keyLabel="Ctrl" scummvm:isSticky="true"/>
<CustomKey scummvm:codes="60" scummvm:keyLabel="Shift" scummvm:isSticky="true"/>
</CustomRow>
</CustomKeyboard>

View File

@ -0,0 +1,56 @@
<?xml version="1.0"?>
<!-- When creating new keyboard layout, add it to TextInputKeyboardList array in ScummVMActivity.java -->
<CustomKeyboard xmlns:scummvm="http://schemas.android.com/apk/res-auto"
scummvm:keyWidth="10%p"
scummvm:horizontalGap="0px"
scummvm:verticalGap="0px"
scummvm:keyHeight="10%p">
<CustomRow>
<CustomKey scummvm:codes="100008" scummvm:keyLabel="!" scummvm:keyEdgeFlags="left" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100009" scummvm:keyLabel="\@" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100010" scummvm:keyLabel="#" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100011" scummvm:keyLabel="$" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100012" scummvm:keyLabel="%" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100013" scummvm:keyLabel="^" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100014" scummvm:keyLabel="&amp;" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100015" scummvm:keyLabel="*" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100016" scummvm:keyLabel="(" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100007" scummvm:keyLabel=")" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow>
<CustomKey scummvm:codes="111" scummvm:keyLabel="Esc" scummvm:keyEdgeFlags="left" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100068" scummvm:keyLabel="~" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100069" scummvm:keyLabel="_" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100070" scummvm:keyLabel="+" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100073" scummvm:keyLabel="|" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="100075" scummvm:keyLabel="&quot;" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="131" scummvm:keyLabel="F1" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="132" scummvm:keyLabel="F2" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="133" scummvm:keyLabel="F3" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="134" scummvm:keyLabel="F4" scummvm:isRepeatable="true" scummvm:keyEdgeFlags="right" />
</CustomRow>
<CustomRow>
<CustomKey scummvm:codes="-1" scummvm:keyLabel="123…" scummvm:keyEdgeFlags="left"/>
<CustomKey scummvm:codes="143" scummvm:keyLabel="NumLk" scummvm:isSticky="true"/>
<CustomKey scummvm:codes="120" scummvm:keyLabel="Print" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="116" scummvm:keyLabel="ScrollLk" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="121" scummvm:keyLabel="Pause" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="158" scummvm:keyLabel="Kp ." scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="135" scummvm:keyLabel="F5" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="136" scummvm:keyLabel="F6" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="137" scummvm:keyLabel="F7" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="138" scummvm:keyLabel="F8" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow rowEdgeFlags="bottom">
<CustomKey scummvm:codes="-6" scummvm:keyLabel="abc…" scummvm:keyEdgeFlags="left"/>
<CustomKey scummvm:codes="154" scummvm:keyLabel="Kp /" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="155" scummvm:keyLabel="Kp *" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="156" scummvm:keyLabel="Kp -" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="157" scummvm:keyLabel="Kp +" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="160" scummvm:keyLabel="Kp ↵" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="139" scummvm:keyLabel="F9" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="140" scummvm:keyLabel="F10" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="141" scummvm:keyLabel="F11" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="142" scummvm:keyLabel="F12" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
</CustomKeyboard>

View File

@ -0,0 +1,53 @@
<?xml version="1.0"?>
<!-- When creating new keyboard layout, add it to TextInputKeyboardList array in ScummVMActivity.java -->
<CustomKeyboard xmlns:scummvm="http://schemas.android.com/apk/res-auto"
scummvm:keyWidth="10%p"
scummvm:horizontalGap="0px"
scummvm:verticalGap="0px"
scummvm:keyHeight="10%p">
<CustomRow>
<CustomKey scummvm:codes="45" scummvm:keyLabel="Q" scummvm:keyEdgeFlags="left" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="51" scummvm:keyLabel="W" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="33" scummvm:keyLabel="E" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="46" scummvm:keyLabel="R" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="48" scummvm:keyLabel="T" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="53" scummvm:keyLabel="Y" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="49" scummvm:keyLabel="U" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="37" scummvm:keyLabel="I" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="43" scummvm:keyLabel="O" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="44" scummvm:keyLabel="P" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow>
<CustomKey scummvm:codes="29" scummvm:keyLabel="A" scummvm:keyEdgeFlags="left" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="47" scummvm:keyLabel="S" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="32" scummvm:keyLabel="D" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="34" scummvm:keyLabel="F" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="35" scummvm:keyLabel="G" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="36" scummvm:keyLabel="H" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="38" scummvm:keyLabel="J" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="39" scummvm:keyLabel="K" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="40" scummvm:keyLabel="L" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="74" scummvm:keyLabel=":" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow>
<CustomKey scummvm:codes="-1" scummvm:keyLabel="⇫" scummvm:keyEdgeFlags="left"/>
<CustomKey scummvm:codes="54" scummvm:keyLabel="Z" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="52" scummvm:keyLabel="X" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="31" scummvm:keyLabel="C" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="50" scummvm:keyLabel="V" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="30" scummvm:keyLabel="B" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="42" scummvm:keyLabel="N" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="41" scummvm:keyLabel="M" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="67" scummvm:keyLabel="⬅" scummvm:keyWidth="20%p" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
<CustomRow rowEdgeFlags="bottom">
<CustomKey scummvm:codes="-6" scummvm:keyLabel="!@#…" scummvm:keyEdgeFlags="left"/>
<CustomKey scummvm:codes="71" scummvm:keyLabel="{" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="72" scummvm:keyLabel="}" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="76" scummvm:keyLabel="\?" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="62" scummvm:keyLabel="Space" scummvm:keyWidth="20%p" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="55" scummvm:keyLabel="&lt;" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="56" scummvm:keyLabel="&gt;" scummvm:isRepeatable="true"/>
<CustomKey scummvm:codes="66" scummvm:keyLabel="Enter" scummvm:keyWidth="20%p" scummvm:keyEdgeFlags="right" scummvm:isRepeatable="true"/>
</CustomRow>
</CustomKeyboard>