mirror of
https://github.com/moparisthebest/keepass2android
synced 2025-01-10 21:18:18 -05:00
updated input stick api & plugin version
This commit is contained in:
parent
ff525bd655
commit
b65f0bce43
@ -2,5 +2,5 @@
|
||||
package com.inputstick.api;
|
||||
|
||||
public final class BuildConfig {
|
||||
public final static boolean DEBUG = true;
|
||||
public final static boolean DEBUG = false;
|
||||
}
|
@ -10,14 +10,30 @@ package com.inputstick.api;
|
||||
public final class R {
|
||||
public static final class attr {
|
||||
}
|
||||
public static final class dimen {
|
||||
/** Default screen margins, per the Android Design guidelines.
|
||||
|
||||
Customize dimensions originally defined in res/values/dimens.xml (such as
|
||||
screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
|
||||
|
||||
*/
|
||||
public static int activity_horizontal_margin=0x7f030000;
|
||||
public static int activity_vertical_margin=0x7f030001;
|
||||
}
|
||||
public static final class drawable {
|
||||
public static int ic_launcher=0x7f020000;
|
||||
}
|
||||
public static final class id {
|
||||
public static int action_settings=0x7f070000;
|
||||
}
|
||||
public static final class menu {
|
||||
public static int install_utility=0x7f060000;
|
||||
}
|
||||
public static final class string {
|
||||
public static int action_settings=0x7f030002;
|
||||
public static int app_name=0x7f030000;
|
||||
public static int hello_world=0x7f030003;
|
||||
public static int title_activity_install_utility=0x7f030001;
|
||||
public static int action_settings=0x7f040002;
|
||||
public static int app_name=0x7f040000;
|
||||
public static int hello_world=0x7f040003;
|
||||
public static int title_activity_install_utility=0x7f040001;
|
||||
}
|
||||
public static final class style {
|
||||
/**
|
||||
@ -41,10 +57,10 @@ public final class R {
|
||||
|
||||
API 14 theme customizations can go here.
|
||||
*/
|
||||
public static int AppBaseTheme=0x7f040000;
|
||||
public static int AppBaseTheme=0x7f050000;
|
||||
/** Application theme.
|
||||
All customizations that are NOT specific to a particular API-level can go here.
|
||||
*/
|
||||
public static int AppTheme=0x7f040001;
|
||||
public static int AppTheme=0x7f050001;
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,5 @@
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-19
|
||||
target=android-20
|
||||
android.library=true
|
||||
|
@ -19,7 +19,7 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
||||
|
||||
private InitManager mInitManager;
|
||||
private Application mApp;
|
||||
private BTService mBTService;
|
||||
protected BTService mBTService;
|
||||
private PacketManager mPacketManager;
|
||||
private final BTHandler mBTHandler = new BTHandler(this);
|
||||
|
||||
@ -103,8 +103,7 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
||||
connect(false, BTService.DEFAULT_CONNECT_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
public void connect(boolean reflection, int timeout) {
|
||||
public void connect(boolean reflection, int timeout, boolean doNotAsk) {
|
||||
mErrorCode = InputStickError.ERROR_NONE;
|
||||
if (mBTService == null) {
|
||||
mBTService = new BTService(mApp, mBTHandler);
|
||||
@ -113,10 +112,14 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
||||
}
|
||||
mBTService.setConnectTimeout(timeout);
|
||||
mBTService.enableReflection(reflection);
|
||||
mBTService.connect(mMac);
|
||||
mBTService.connect(mMac, doNotAsk);
|
||||
onConnecting();
|
||||
}
|
||||
|
||||
public void connect(boolean reflection, int timeout) {
|
||||
connect(reflection, timeout, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
if (mBTService != null) {
|
||||
|
@ -22,7 +22,14 @@ public abstract class ConnectionManager {
|
||||
public abstract void sendPacket(Packet p);
|
||||
|
||||
protected void stateNotify(int state) {
|
||||
if (mState != state) {
|
||||
stateNotify(state, false);
|
||||
}
|
||||
|
||||
protected void stateNotify(int state, boolean forceNotification) {
|
||||
if (( !forceNotification) && (mState == state )) {
|
||||
//do nothing
|
||||
} else {
|
||||
//notify all listeners
|
||||
mState = state;
|
||||
for (InputStickStateListener listener : mStateListeners) {
|
||||
listener.onStateChanged(state);
|
||||
|
@ -55,7 +55,9 @@ public class PacketManager {
|
||||
byte[] iv = mAes.init(mKey);
|
||||
p.addBytes(iv);
|
||||
|
||||
Util.printHex(iv, "IV: ");
|
||||
//Util.printHex(mKey, "key: "); // TODO prnt
|
||||
|
||||
//Util.printHex(iv, "IV: ");
|
||||
|
||||
byte[] initData = new byte[16];
|
||||
r.nextBytes(initData);
|
||||
@ -72,13 +74,13 @@ public class PacketManager {
|
||||
initData = mAes.encrypt(initData);
|
||||
p.addBytes(initData);
|
||||
|
||||
Util.printHex(initData, "InitData: ");
|
||||
//Util.printHex(initData, "InitData: ");
|
||||
|
||||
cmpData = new byte[16];
|
||||
r.nextBytes(cmpData);
|
||||
p.addBytes(cmpData);
|
||||
|
||||
Util.printHex(cmpData, "CmpData: ");
|
||||
//Util.printHex(cmpData, "CmpData: ");
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -89,11 +91,14 @@ public class PacketManager {
|
||||
//boolean decrypt = false;
|
||||
long crcValue, crcCompare;
|
||||
|
||||
//Util.printHex(data, "RX DATA: "); // TODO prnt
|
||||
|
||||
payload = Arrays.copyOfRange(data, 2, data.length); //remove TAG, info
|
||||
if ((data[1] & Packet.FLAG_ENCRYPTED) != 0) {
|
||||
//Util.log("DECRYPT");
|
||||
if (mAes.isReady()) {
|
||||
payload = mAes.decrypt(payload);
|
||||
//Util.printHex(payload, "RX DECR: "); // TODO prnt
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -110,6 +115,7 @@ public class PacketManager {
|
||||
|
||||
if (crcValue == crcCompare) {
|
||||
payload = Arrays.copyOfRange(payload, 4, payload.length); //remove CRC
|
||||
//Util.printHex(payload, "RX PAYLOAD FINAL: "); // TODO prnt
|
||||
return payload;
|
||||
} else {
|
||||
return null; //TODO
|
||||
@ -156,11 +162,10 @@ public class PacketManager {
|
||||
crcValue >>= 8;
|
||||
result[0] = (byte)crcValue;
|
||||
|
||||
//Util.printHex(result);
|
||||
|
||||
//Util.printHex(result, "TX DATA: "); // TODO prnt
|
||||
if (encrypt) {
|
||||
result = mAes.encrypt(result);
|
||||
//Util.printHex(result);
|
||||
//Util.printHex(result, "ENC DATA: "); // TODO prnt
|
||||
}
|
||||
|
||||
header = new byte[2];
|
||||
@ -172,6 +177,7 @@ public class PacketManager {
|
||||
if (p.getRespond()) {
|
||||
header[1] |= Packet.FLAG_RESPOND;
|
||||
}
|
||||
//Util.printHex(header, "TX HEADER: "); // TODO prnt
|
||||
mBTService.write(header);
|
||||
mBTService.write(result);
|
||||
}
|
||||
|
@ -61,14 +61,20 @@ public class InputStickKeyboard {
|
||||
}
|
||||
|
||||
protected static void setLEDs(boolean numLock, boolean capsLock, boolean scrollLock) {
|
||||
boolean mustUpdate = false;
|
||||
if ((numLock != mNumLock) || (capsLock != mCapsLock) || (scrollLock != mScrollLock)) {
|
||||
mustUpdate = true;
|
||||
}
|
||||
mNumLock = numLock;
|
||||
mCapsLock = capsLock;
|
||||
mScrollLock = scrollLock;
|
||||
|
||||
if (mustUpdate) {
|
||||
for (InputStickKeyboardListener listener : mKeyboardListeners) {
|
||||
listener.onLEDsChanged(mNumLock, mCapsLock, mScrollLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isNumLock() {
|
||||
return mNumLock;
|
||||
|
@ -59,6 +59,20 @@ public class BTService {
|
||||
private boolean disconnecting;
|
||||
private boolean connected;
|
||||
|
||||
//================================================================
|
||||
private static final int RX_TIMEOUT = 3000;
|
||||
|
||||
private long lastRxTime;
|
||||
private int rxState;
|
||||
private int rxPos;
|
||||
private int rxLength;
|
||||
private byte[] rxData;
|
||||
private int rxWdgCnt;
|
||||
|
||||
private static final int RX_TAG = 0;
|
||||
private static final int RX_LENGTH = 1;
|
||||
private static final int RX_DATA = 2;
|
||||
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@ -108,20 +122,25 @@ public class BTService {
|
||||
|
||||
|
||||
|
||||
private void enableBluetooth() {
|
||||
private void enableBluetooth(boolean doNotAsk) {
|
||||
if (mApp != null) {
|
||||
turnBluetoothOn = true;
|
||||
|
||||
if ( !receiverRegistered) {
|
||||
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
mCtx.registerReceiver(mReceiver, filter);
|
||||
receiverRegistered = true;
|
||||
}
|
||||
|
||||
if (doNotAsk) {
|
||||
BluetoothAdapter.getDefaultAdapter().enable();
|
||||
} else {
|
||||
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
||||
enableBtIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
mApp.startActivity(enableBtIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doConnect(boolean reconnecting) {
|
||||
if (reconnecting) {
|
||||
@ -146,6 +165,10 @@ public class BTService {
|
||||
|
||||
|
||||
public synchronized void connect(String mac) {
|
||||
connect(mac, false);
|
||||
}
|
||||
|
||||
public synchronized void connect(String mac, boolean doNotAsk) {
|
||||
Util.log("connect to: " + mac + " REFLECTION: " + mUseReflection);
|
||||
disconnecting = false;
|
||||
connected = false;
|
||||
@ -159,7 +182,7 @@ public class BTService {
|
||||
if (mBluetoothAdapter.isEnabled()) {
|
||||
doConnect(false);
|
||||
} else {
|
||||
enableBluetooth();
|
||||
enableBluetooth(doNotAsk);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -342,38 +365,68 @@ public class BTService {
|
||||
mmOutStream = tmpOut;
|
||||
}
|
||||
|
||||
|
||||
private void rxByte(byte b) {
|
||||
long time = System.currentTimeMillis();
|
||||
if (time > lastRxTime + RX_TIMEOUT) {
|
||||
rxState = RX_TAG;
|
||||
}
|
||||
|
||||
|
||||
switch (rxState) {
|
||||
case RX_TAG:
|
||||
if (b == Packet.START_TAG) {
|
||||
rxState = RX_LENGTH;
|
||||
} else {
|
||||
Util.log("Unexpected RX byte" + b);
|
||||
if (b == 0xAF) {
|
||||
rxWdgCnt++;
|
||||
}
|
||||
if (rxWdgCnt > 1024) {
|
||||
rxWdgCnt = 0;
|
||||
event(EVENT_ERROR, InputStickError.ERROR_HARDWARE_WDG_RESET);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RX_LENGTH:
|
||||
rxLength = b;
|
||||
rxLength &= 0x3F;
|
||||
rxLength *= 16;
|
||||
rxLength += 2;
|
||||
rxPos = 2;
|
||||
|
||||
rxData = new byte[rxLength];
|
||||
rxData[0] = Packet.START_TAG;
|
||||
rxData[1] = (byte)b;
|
||||
|
||||
rxState = RX_DATA;
|
||||
break;
|
||||
case RX_DATA:
|
||||
if (rxPos < rxLength) {
|
||||
rxData[rxPos] = b;
|
||||
rxPos++;
|
||||
if (rxPos == rxLength) {
|
||||
//done!
|
||||
mHandler.obtainMessage(EVENT_DATA, 0, 0, rxData).sendToTarget();
|
||||
rxState = RX_TAG;
|
||||
}
|
||||
} else {
|
||||
//buffer overrun!
|
||||
rxState = RX_TAG;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
lastRxTime = time;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Util.log("BEGIN mConnectedThread");
|
||||
byte[] buffer = null;
|
||||
int rxTmp;
|
||||
int lengthByte;
|
||||
int length;
|
||||
int wdgCnt = 0;
|
||||
while (true) {
|
||||
try {
|
||||
rxTmp = mmInStream.read();
|
||||
if (rxTmp == Packet.START_TAG) {
|
||||
wdgCnt = 0;
|
||||
lengthByte = mmInStream.read();
|
||||
length = lengthByte;
|
||||
length &= 0x3F;
|
||||
length *= 16;
|
||||
buffer = new byte[length + 2];
|
||||
buffer[0] = Packet.START_TAG;
|
||||
buffer[1] = (byte)lengthByte;
|
||||
for (int i = 2; i < length + 2; i++) {
|
||||
buffer[i] = (byte)mmInStream.read();
|
||||
}
|
||||
mHandler.obtainMessage(EVENT_DATA, 0, 0, buffer).sendToTarget();
|
||||
} else {
|
||||
Util.log("Unexpected RX byte" + rxTmp);
|
||||
if (rxTmp == 0xAF) {
|
||||
wdgCnt++;
|
||||
}
|
||||
if (wdgCnt > 1024) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
rxByte((byte)rxTmp);
|
||||
} catch (IOException e) {
|
||||
connectionLost();
|
||||
break;
|
||||
|
@ -8,6 +8,8 @@ import com.inputstick.api.hid.KeyboardReport;
|
||||
|
||||
public abstract class KeyboardLayout {
|
||||
|
||||
public static final int MAX_SCANCODE = 0x60;
|
||||
|
||||
public static final byte[] scanCodeToHID = {
|
||||
/* 0x00 */ 0,
|
||||
/* 0x01 */ HIDKeycodes.KEY_ESCAPE,
|
||||
@ -138,7 +140,7 @@ public abstract class KeyboardLayout {
|
||||
}
|
||||
|
||||
public static int hidToScanCode(byte key) {
|
||||
for (int scanCode = 0; scanCode < 80; scanCode++) {
|
||||
for (int scanCode = 0; scanCode < MAX_SCANCODE; scanCode++) {
|
||||
if (scanCodeToHID[scanCode] == key) {
|
||||
return scanCode;
|
||||
}
|
||||
@ -147,7 +149,7 @@ public abstract class KeyboardLayout {
|
||||
}
|
||||
|
||||
public static char getChar(int[][] lut, int scanCode, boolean capsLock, boolean shift, boolean altGr) {
|
||||
if ((scanCode > 80) || (scanCode < 0)) {
|
||||
if ((scanCode >= MAX_SCANCODE) || (scanCode < 0)) {
|
||||
return (char)0;
|
||||
}
|
||||
|
||||
@ -197,7 +199,7 @@ public abstract class KeyboardLayout {
|
||||
}
|
||||
|
||||
public static int getScanCode(int[][] lut, char c) {
|
||||
for (int scanCode = 0; scanCode < 0x60; scanCode++) {
|
||||
for (int scanCode = 0; scanCode < MAX_SCANCODE; scanCode++) {
|
||||
if (lut[scanCode][0] == -1) {
|
||||
continue;
|
||||
} else {
|
||||
@ -337,6 +339,22 @@ public abstract class KeyboardLayout {
|
||||
return FrenchLayout.getInstance();
|
||||
} else if (locale.equals(SpanishLayout.getInstance().getLocaleName())) {
|
||||
return SpanishLayout.getInstance();
|
||||
} else if (locale.equals(UnitedKingdomLayout.getInstance().getLocaleName())) {
|
||||
return UnitedKingdomLayout.getInstance();
|
||||
} else if (locale.equals(GermanMacLayout.getInstance().getLocaleName())) {
|
||||
return GermanMacLayout.getInstance(); // TODO
|
||||
} else if (locale.equals(ItalianLayout.getInstance().getLocaleName())) {
|
||||
return ItalianLayout.getInstance();
|
||||
} else if (locale.equals(FinnishLayout.getInstance().getLocaleName())) {
|
||||
return FinnishLayout.getInstance();
|
||||
} else if (locale.equals(SwissFrenchLayout.getInstance().getLocaleName())) {
|
||||
return SwissFrenchLayout.getInstance();
|
||||
} else if (locale.equals(SwissGermanLayout.getInstance().getLocaleName())) {
|
||||
return SwissGermanLayout.getInstance();
|
||||
} else if (locale.equals(HebrewLayout.getInstance().getLocaleName())) {
|
||||
return HebrewLayout.getInstance();
|
||||
} else if (locale.equals(DanishLayout.getInstance().getLocaleName())) {
|
||||
return DanishLayout.getInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
||||
/* 0e */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
/* 0f */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
|
||||
|
||||
/* 10 */ { 1 , (int)'q' , (int)'Q' , -1 , -1 , -1 } ,
|
||||
/* 11 */ { 1 , (int)'w' , (int)'W' , -1 , -1 , -1 } ,
|
||||
/* 12 */ { 1 , (int)'e' , (int)'E' , -1 , -1 , -1 } ,
|
||||
@ -40,7 +39,6 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
||||
/* 1e */ { 1 , (int)'a' , (int)'A' , -1 , -1 , -1 } ,
|
||||
/* 1f */ { 1 , (int)'s' , (int)'S' , -1 , -1 , -1 } ,
|
||||
|
||||
|
||||
/* 20 */ { 1 , (int)'d' , (int)'D' , -1 , -1 , -1 } ,
|
||||
/* 21 */ { 1 , (int)'f' , (int)'F' , -1 , -1 , -1 } ,
|
||||
/* 22 */ { 1 , (int)'g' , (int)'G' , -1 , -1 , -1 } ,
|
||||
@ -58,7 +56,6 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
||||
/* 2e */ { 1 , (int)'c' , (int)'C' , -1 , -1 , -1 } ,
|
||||
/* 2f */ { 1 , (int)'v' , (int)'V' , -1 , -1 , -1 } ,
|
||||
|
||||
|
||||
/* 30 */ { 1 , (int)'b' , (int)'B' , -1 , -1 , -1 } ,
|
||||
/* 31 */ { 1 , (int)'n' , (int)'N' , -1 , -1 , -1 } ,
|
||||
/* 32 */ { 1 , (int)'m' , (int)'M' , -1 , -1 , -1 } ,
|
||||
@ -76,7 +73,6 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
||||
/* 3e */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
/* 3f */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
|
||||
|
||||
/* 40 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
/* 41 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
/* 42 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
@ -94,7 +90,6 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
||||
/* 4e */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
/* 4f */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
|
||||
|
||||
/* 50 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
/* 51 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
/* 52 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
|
@ -5,15 +5,22 @@ import com.inputstick.api.Packet;
|
||||
|
||||
public class BasicInitManager extends InitManager {
|
||||
|
||||
private static final int UPDATES_LIMIT = 50;
|
||||
private static final int RETRY_LIMIT = 3;
|
||||
|
||||
|
||||
private int lastStatusParam;
|
||||
private int noInitUpdatesCnt;
|
||||
private int noInitRetryCnt;
|
||||
|
||||
public BasicInitManager(byte[] key) {
|
||||
super(key);
|
||||
lastStatusParam = 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
/*Packet p = new Packet(false, Packet.RAW_OLD_BOOTLOADER); //compatibility with old protocol version
|
||||
sendPacket(p);*/
|
||||
sendPacket(new Packet(true, Packet.CMD_RUN_FW));
|
||||
}
|
||||
|
||||
@ -33,6 +40,8 @@ public class BasicInitManager extends InitManager {
|
||||
case Packet.CMD_INIT:
|
||||
if (respCode == Packet.RESP_OK) {
|
||||
initDone = true;
|
||||
noInitUpdatesCnt = 0;
|
||||
noInitRetryCnt = 0;
|
||||
sendPacket(new Packet(true, Packet.CMD_HID_STATUS_REPORT));
|
||||
} else {
|
||||
mListener.onInitFailure(respCode);
|
||||
@ -43,12 +52,24 @@ public class BasicInitManager extends InitManager {
|
||||
break;
|
||||
case Packet.CMD_HID_STATUS:
|
||||
if (initDone) {
|
||||
if (param != lastStatusParam) {
|
||||
lastStatusParam = param;
|
||||
if (param == 0x05) {
|
||||
mListener.onInitReady();
|
||||
} else {
|
||||
mListener.onInitNotReady();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
noInitUpdatesCnt++;
|
||||
if (noInitUpdatesCnt == UPDATES_LIMIT) {
|
||||
noInitUpdatesCnt = 0;
|
||||
if (noInitRetryCnt < RETRY_LIMIT) {
|
||||
sendPacket(new Packet(true, Packet.CMD_RUN_FW));
|
||||
noInitRetryCnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
package com.inputstick.init;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import com.inputstick.api.InputStickError;
|
||||
import com.inputstick.api.Packet;
|
||||
import com.inputstick.api.PacketManager;
|
||||
@ -17,8 +14,6 @@ public class InitManager {
|
||||
protected DeviceInfo mInfo;
|
||||
protected boolean initDone;
|
||||
|
||||
//private Timer t;
|
||||
|
||||
public InitManager(byte[] key) {
|
||||
mKey = key;
|
||||
}
|
||||
|
Binary file not shown.
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="keepass2android.plugin.inputstick"
|
||||
android:versionCode="3"
|
||||
android:versionName="1.2" >
|
||||
android:versionCode="4"
|
||||
android:versionName="1.3" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
|
Loading…
Reference in New Issue
Block a user