Ver código fonte

Merge branch 'BUG-2' of isundil/pass into master

isundil 7 anos atrás
pai
commit
a7c48c2f52

+ 1 - 3
app/src/main/java/info/knacki/pass/git/HttpGitProtocol.java

@@ -487,7 +487,7 @@ class HttpGitProtocol implements GitInterface {
                             @Override
                             public void onResponse(byte[] result) {
                                 Logger.getAnonymousLogger().severe("git-receive-pack " +new String(result, Charset.defaultCharset()));
-                                //response.onResponse(null);
+                                response.onResponse(null);
                             }
 
                             @Override
@@ -524,8 +524,6 @@ class HttpGitProtocol implements GitInterface {
                         response.onError(e.getMessage(), e);
                         return;
                     }
-                    // FIXME
-                    response.onResponse(null);
                 } else {
                     response.onError("Branch " +fConfig.GetBranch() + " not found on remote for pushing", null);
                 }

+ 1 - 1
app/src/main/java/info/knacki/pass/git/entities/GitCommit.java

@@ -39,7 +39,7 @@ public class GitCommit implements GitPackable {
             else if (line.startsWith("author"))
                 fAuthor = Util.RemoveHead(line);
             else if (line.startsWith("committer"))
-                fCommitter = Util.RemoveHead(line); // FIXME time
+                fCommitter = Util.RemoveHead(line);
         }
         fHash = hash;
         fMessage = message == null ? "" : message.toString();

+ 12 - 10
app/src/main/java/info/knacki/pass/input/InputService.java

@@ -19,14 +19,15 @@ import info.knacki.pass.R;
 import info.knacki.pass.git.GitInterface;
 import info.knacki.pass.io.FileInterfaceFactory;
 import info.knacki.pass.io.PathUtils;
+import info.knacki.pass.ui.MainActivity;
 import info.knacki.pass.ui.passwordList.PasswordClickListener;
 import info.knacki.pass.ui.passwordList.PasswordListView;
-import info.knacki.pass.ui.MainActivity;
-import info.knacki.pass.ui.PasswordPicker;
+import info.knacki.pass.ui.passwordPicker.ServicePasswordPicker;
 
 public class InputService extends InputMethodService implements PasswordClickListener {
     protected PasswordListView fPasswordListView;
     private static final Logger log = Logger.getLogger(InputService.class.getName());
+    protected View fInputView;
 
     @Override
     public void onStartInputView(EditorInfo info, boolean restarting) {
@@ -36,12 +37,13 @@ public class InputService extends InputMethodService implements PasswordClickLis
         }
     }
 
+    @SuppressLint("InflateParams")
     @Override
     public View onCreateInputView() {
-        @SuppressLint("InflateParams") View view = LayoutInflater.from(this).inflate(R.layout.input, null, false);
+        fInputView = LayoutInflater.from(this).inflate(R.layout.input, null, false);
         fPasswordListView = new PasswordListView<>(this, PathUtils.GetPassDir(this));
-        ((ScrollView)view.findViewById(R.id.passwordListContainer)).addView(fPasswordListView);
-        view.findViewById(R.id.prevButton).setOnClickListener(new View.OnClickListener() {
+        ((ScrollView)fInputView.findViewById(R.id.passwordListContainer)).addView(fPasswordListView);
+        fInputView.findViewById(R.id.prevButton).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 InputMethodManager service = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -53,18 +55,18 @@ public class InputService extends InputMethodService implements PasswordClickLis
                 service.showInputMethodPicker();
             }
         });
-        view.findViewById(R.id.openAppButton).setOnClickListener(new View.OnClickListener() {
+        fInputView.findViewById(R.id.openAppButton).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 startActivity(new Intent(InputService.this, MainActivity.class));
             }
         });
-        return view;
+        return fInputView;
     }
 
     @Override
     public void OnPasswordClicked(File f) {
-        FileInterfaceFactory.GetFileInterface(this, new PasswordPicker(this), f).ReadFile(new GitInterface.OnResponseListener<String>() {
+        FileInterfaceFactory.GetFileInterface(this, new ServicePasswordPicker(this, fInputView), f).ReadFile(new GitInterface.OnResponseListener<String>() {
             @Override
             public void onResponse(String passwordContent) {
                 for (char i: passwordContent.toCharArray()) {
@@ -74,8 +76,8 @@ public class InputService extends InputMethodService implements PasswordClickLis
 
             @Override
             public void onError(String msg, Throwable e) {
-                Toast.makeText(InputService.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
-                log.log(Level.SEVERE, e.getMessage(), e);
+                Toast.makeText(InputService.this, "Error: " + msg, Toast.LENGTH_LONG).show();
+                log.log(Level.SEVERE, msg, e);
             }
         });
         onFinishInput();

+ 3 - 7
app/src/main/java/info/knacki/pass/io/GPGFileInterface.java

@@ -124,14 +124,10 @@ class GPGFileInterface implements IFileInterface {
                     PGPEncryptedData ped = (PGPEncryptedData) it.next();
 
                     if (ped instanceof PGPPublicKeyEncryptedData) {
-                        PGPPublicKeyEncryptedData pked = (PGPPublicKeyEncryptedData) ped;
+                        InputStream ret = getFirstLiteralDataInputStream(((PGPPublicKeyEncryptedData) ped).getDataStream(new BcPublicKeyDataDecryptorFactory(pKey.key)), pKey);
 
-                        InputStream ret = getFirstLiteralDataInputStream(pked.getDataStream(new BcPublicKeyDataDecryptorFactory(pKey.key)), pKey);
-
-                        if (ret != null) return ret;
-                        // TODO: To verify integrity,
-                        //       we need to keep the
-                        //       pked reference.
+                        if (ret != null)
+                            return ret;
                     } else if (ped instanceof PGPPBEEncryptedData) {
                         PGPPBEEncryptedData pped = (PGPPBEEncryptedData) ped;
 

+ 1 - 0
app/src/main/java/info/knacki/pass/ui/EditPasswordActivity.java

@@ -17,6 +17,7 @@ import java.util.logging.Logger;
 import info.knacki.pass.R;
 import info.knacki.pass.git.GitInterface;
 import info.knacki.pass.io.FileInterfaceFactory;
+import info.knacki.pass.ui.passwordPicker.PasswordPicker;
 
 public class EditPasswordActivity extends AppCompatActivity {
     private static final Logger log = Logger.getLogger(EditPasswordActivity.class.getName());

+ 1 - 0
app/src/main/java/info/knacki/pass/ui/MainActivity.java

@@ -35,6 +35,7 @@ import info.knacki.pass.ui.alertPrompt.views.TextEditAndCheckbox;
 import info.knacki.pass.ui.alertPrompt.views.TextView;
 import info.knacki.pass.ui.passwordList.EditablePasswordListView;
 import info.knacki.pass.ui.passwordList.PasswordEditListener;
+import info.knacki.pass.ui.passwordPicker.PasswordPicker;
 
 public class MainActivity extends AppCompatActivity implements PasswordEditListener {
     private final Logger log = Logger.getLogger(MainActivity.class.getName());

+ 3 - 0
app/src/main/java/info/knacki/pass/ui/alertPrompt/AlertPrompt.java

@@ -71,8 +71,11 @@ public class AlertPrompt {
         return this;
     }
 
+    protected void OnBeforeShow() {}
+
     public AlertPrompt show() {
         fDialog = fAlertBuilder.create();
+        OnBeforeShow();
         fDialog.show();
         return this;
     }

+ 25 - 0
app/src/main/java/info/knacki/pass/ui/alertPrompt/ServiceAlertPrompt.java

@@ -0,0 +1,25 @@
+package info.knacki.pass.ui.alertPrompt;
+
+import android.content.Context;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+public class ServiceAlertPrompt extends AlertPrompt {
+    protected final View fInputView;
+
+    public ServiceAlertPrompt(Context c, View inputView) {
+        super(c);
+        fInputView = inputView;
+    }
+
+    @Override
+    protected void OnBeforeShow() {
+        final Window window = fDialog.getWindow();
+        WindowManager.LayoutParams lay = window.getAttributes();
+        lay.token = fInputView.getWindowToken();
+        lay.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+        window.setAttributes(lay);
+        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+    }
+}

+ 16 - 0
app/src/main/java/info/knacki/pass/ui/alertPrompt/integratedKeyboard/KeyboardUtils.java

@@ -0,0 +1,16 @@
+package info.knacki.pass.ui.alertPrompt.integratedKeyboard;
+
+import android.content.Context;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+public class KeyboardUtils {
+    public static String GetFormat(Context ctx) {
+        InputMethodManager im = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
+        if (null != im) {
+            InputMethodSubtype ims = im.getCurrentInputMethodSubtype();
+            return ims.getLocale();
+        }
+        return null;
+    }
+}

+ 105 - 0
app/src/main/java/info/knacki/pass/ui/alertPrompt/integratedKeyboard/KeyboardWidget.java

@@ -0,0 +1,105 @@
+package info.knacki.pass.ui.alertPrompt.integratedKeyboard;
+
+import android.content.Context;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.util.Xml;
+
+import info.knacki.pass.R;
+
+public class KeyboardWidget extends KeyboardView implements KeyboardView.OnKeyboardActionListener {
+    private final Keyboard fKeyboard;
+    private Keyboard fNumberKeyboard;
+    private Keyboard fSymbsKeyboard;
+    private OnKeyListener fListener;
+    private final Context fContext;
+    private boolean fIsDisplayingLetters;
+
+
+    public KeyboardWidget(Context c) {
+        super(c, Xml.asAttributeSet(c.getResources().getXml(R.xml.keyboard_attributes)));
+        fContext = c;
+        fKeyboard = new Keyboard(c, R.xml.qwerty_keyboard);
+        setKeyboard(fKeyboard);
+        setOnKeyboardActionListener(this);
+        setPreviewEnabled(false);
+        fIsDisplayingLetters = true;
+    }
+
+    public KeyboardWidget SetListener(OnKeyListener listener) {
+        fListener = listener;
+        return this;
+    }
+
+    @Override
+    public void onPress(int primaryCode) {
+    }
+
+    @Override
+    public void onRelease(int primaryCode) {
+    }
+
+    @Override
+    public void onKey(int primaryCode, int[] keyCodes) {
+        if (primaryCode == fContext.getResources().getInteger(R.integer.id_keyboard_numbers)) {
+            if (fIsDisplayingLetters) {
+                if (fNumberKeyboard == null)
+                    fNumberKeyboard = new Keyboard(fContext, R.xml.qwerty_symbols);
+                setKeyboard(fNumberKeyboard);
+                setShifted(false);
+                fIsDisplayingLetters = false;
+            } else {
+                setKeyboard(fKeyboard);
+                fIsDisplayingLetters = true;
+            }
+        } else if (primaryCode == fContext.getResources().getInteger(R.integer.id_keyboard_shift)) {
+            if (!fIsDisplayingLetters) {
+                if (isShifted()) {
+                    setKeyboard(fNumberKeyboard);
+                } else {
+                    if (fSymbsKeyboard == null)
+                        fSymbsKeyboard = new Keyboard(fContext, R.xml.qwerty_symbol_shift);
+                    setKeyboard(fSymbsKeyboard);
+                }
+
+            }
+            setShifted(!isShifted());
+        } else if (primaryCode == fContext.getResources().getInteger(R.integer.id_keyboard_delete)) {
+            fListener.OnBackspace();
+        } else {
+            final String text = new String(Character.toChars(primaryCode));
+            if (text.length() > 0)
+                fListener.OnKeyReceived(text);
+        }
+    }
+
+    @Override
+    public void onText(CharSequence text) {
+    }
+
+    @Override
+    public void swipeLeft() {
+
+    }
+
+    @Override
+    public void swipeRight() {
+
+    }
+
+    @Override
+    public void swipeDown() {
+
+    }
+
+    @Override
+    public void swipeUp() {
+
+    }
+
+    public interface OnKeyListener {
+        void OnKeyReceived(CharSequence c);
+
+        void OnBackspace();
+    }
+}

+ 50 - 0
app/src/main/java/info/knacki/pass/ui/alertPrompt/views/PasswordTextEdit.java

@@ -0,0 +1,50 @@
+package info.knacki.pass.ui.alertPrompt.views;
+
+import android.content.Context;
+import android.inputmethodservice.KeyboardView;
+import android.support.v7.widget.AppCompatEditText;
+import android.text.Editable;
+import android.text.InputType;
+import android.widget.LinearLayout;
+
+import info.knacki.pass.ui.alertPrompt.integratedKeyboard.KeyboardWidget;
+
+public class PasswordTextEdit extends LinearLayout implements KeyboardWidget.OnKeyListener {
+    private final AppCompatEditText fTextEdit;
+    private final KeyboardView fKeyboard;
+
+    public PasswordTextEdit(Context c) {
+        super(c);
+        fTextEdit = new SimpleTextEdit(c);
+        fTextEdit.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+        fTextEdit.setFocusable(false);
+        addView(fTextEdit);
+        fKeyboard = new KeyboardWidget(c).SetListener(this);
+        addView(fKeyboard);
+        setOrientation(VERTICAL);
+    }
+
+    public PasswordTextEdit setText(String str) {
+        fTextEdit.setText(str);
+        fTextEdit.setSelection(str.length());
+        return this;
+    }
+
+    public String getStr() {
+        Editable text = fTextEdit.getText();
+        return null == text ? "" : text.toString().trim();
+    }
+
+    @Override
+    public void OnKeyReceived(CharSequence c) {
+        final int selectionStart = fTextEdit.getSelectionStart();
+        final int selectionEnd = fTextEdit.getSelectionEnd();
+        fTextEdit.getText().replace(selectionStart, selectionEnd, c);
+    }
+
+    public void OnBackspace() {
+        final int selectionEnd = fTextEdit.getSelectionEnd();
+        final int selectionStart = Math.max(0, fTextEdit.getSelectionStart() == fTextEdit.getSelectionEnd() ? fTextEdit.getSelectionStart() - 1 : fTextEdit.getSelectionStart());
+        fTextEdit.getText().replace(selectionStart, selectionEnd, "");
+    }
+}

+ 0 - 6
app/src/main/java/info/knacki/pass/ui/alertPrompt/views/SimpleTextEdit.java

@@ -3,7 +3,6 @@ package info.knacki.pass.ui.alertPrompt.views;
 import android.content.Context;
 import android.support.v7.widget.AppCompatEditText;
 import android.text.Editable;
-import android.text.InputType;
 
 public class SimpleTextEdit extends AppCompatEditText {
     public SimpleTextEdit(Context c) {
@@ -21,9 +20,4 @@ public class SimpleTextEdit extends AppCompatEditText {
         Editable text = super.getText();
         return null == text ? "" : text.toString().trim();
     }
-
-    public SimpleTextEdit SetPassword() {
-        setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
-        return this;
-    }
 }

+ 10 - 6
app/src/main/java/info/knacki/pass/ui/PasswordPicker.java → app/src/main/java/info/knacki/pass/ui/passwordPicker/PasswordPicker.java

@@ -1,4 +1,4 @@
-package info.knacki.pass.ui;
+package info.knacki.pass.ui.passwordPicker;
 
 import android.content.Context;
 import android.content.DialogInterface;
@@ -9,24 +9,28 @@ import info.knacki.pass.R;
 import info.knacki.pass.git.GitInterface;
 import info.knacki.pass.io.FileInterfaceFactory;
 import info.knacki.pass.ui.alertPrompt.AlertPrompt;
-import info.knacki.pass.ui.alertPrompt.views.SimpleTextEdit;
+import info.knacki.pass.ui.alertPrompt.views.PasswordTextEdit;
 
 public class PasswordPicker implements FileInterfaceFactory.PasswordGetter {
-    private final Context fContext;
+    protected final Context fContext;
 
     public PasswordPicker(Context context) {
         fContext = context;
     }
 
+    protected AlertPrompt AlertFactory() {
+        return new AlertPrompt(fContext);
+    }
+
     @Override
     public void GetPassword(final GitInterface.OnResponseListener<String> onPassword) {
-        new AlertPrompt(fContext)
+        AlertFactory()
             .setCancelable(true)
-            .setView(new SimpleTextEdit(fContext).SetPassword())
+                .setView(new PasswordTextEdit(fContext))
             .setPositiveButton(R.string.ok, new AlertPrompt.OnClickListener() {
                 @Override
                 public void onClick(DialogInterface dialogInterface, View view) {
-                    onPassword.onResponse(((SimpleTextEdit) view).getStr());
+                    onPassword.onResponse(((PasswordTextEdit) view).getStr());
                 }
             })
             .setNegativeButton(R.string.cancel, new AlertPrompt.OnClickListener() {

+ 21 - 0
app/src/main/java/info/knacki/pass/ui/passwordPicker/ServicePasswordPicker.java

@@ -0,0 +1,21 @@
+package info.knacki.pass.ui.passwordPicker;
+
+import android.content.Context;
+import android.view.View;
+
+import info.knacki.pass.ui.alertPrompt.AlertPrompt;
+import info.knacki.pass.ui.alertPrompt.ServiceAlertPrompt;
+
+public class ServicePasswordPicker extends PasswordPicker {
+    protected final View fInputView;
+
+    public ServicePasswordPicker(Context context, View inputView) {
+        super(context);
+        fInputView = inputView;
+    }
+
+    @Override
+    protected AlertPrompt AlertFactory() {
+        return new ServiceAlertPrompt(fContext, fInputView);
+    }
+}

+ 4 - 0
app/src/main/res/values/dimens.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <dimen name="keyboard_key_height">32dp</dimen>
+</resources>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -24,4 +24,7 @@
     <string name="id_vcs_git_authcategory">id_vcs_git_authcategory</string>
     <string name="id_gpg_keyfile">id_gpg_keyfile</string>
     <string name="id_removeall">id_removeall</string>
+    <integer name="id_keyboard_numbers">-2</integer>
+    <integer name="id_keyboard_shift">-1</integer>
+    <integer name="id_keyboard_delete">-5</integer>
 </resources>

+ 4 - 0
app/src/main/res/xml/keyboard_attributes.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen>
+
+</PreferenceScreen>

+ 139 - 0
app/src/main/res/xml/qwerty_keyboard.xml

@@ -0,0 +1,139 @@
+<?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.
+*/
+-->
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/keyboard_key_height">
+    <Row>
+        <Key
+            android:codes="113"
+            android:keyLabel="q"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="119"
+            android:keyLabel="w" />
+        <Key
+            android:codes="101"
+            android:keyLabel="e" />
+        <Key
+            android:codes="114"
+            android:keyLabel="r" />
+        <Key
+            android:codes="116"
+            android:keyLabel="t" />
+        <Key
+            android:codes="121"
+            android:keyLabel="y" />
+        <Key
+            android:codes="117"
+            android:keyLabel="u" />
+        <Key
+            android:codes="105"
+            android:keyLabel="i" />
+        <Key
+            android:codes="111"
+            android:keyLabel="o" />
+        <Key
+            android:codes="112"
+            android:keyLabel="p"
+            android:keyEdgeFlags="right" />
+    </Row>
+
+    <Row>
+        <Key
+            android:codes="97"
+            android:keyLabel="a"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="115"
+            android:keyLabel="s" />
+        <Key
+            android:codes="100"
+            android:keyLabel="d" />
+        <Key
+            android:codes="102"
+            android:keyLabel="f" />
+        <Key
+            android:codes="103"
+            android:keyLabel="g" />
+        <Key
+            android:codes="104"
+            android:keyLabel="h" />
+        <Key
+            android:codes="106"
+            android:keyLabel="j" />
+        <Key
+            android:codes="107"
+            android:keyLabel="k" />
+        <Key
+            android:codes="108"
+            android:keyLabel="l"
+            android:keyEdgeFlags="right" />
+    </Row>
+
+    <Row>
+        <Key
+            android:codes="@integer/id_keyboard_shift"
+            android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p"
+            android:isModifier="true"
+            android:isSticky="true"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="122"
+            android:keyLabel="z" />
+        <Key
+            android:codes="120"
+            android:keyLabel="x" />
+        <Key
+            android:codes="99"
+            android:keyLabel="c" />
+        <Key
+            android:codes="118"
+            android:keyLabel="v" />
+        <Key
+            android:codes="98"
+            android:keyLabel="b" />
+        <Key
+            android:codes="110"
+            android:keyLabel="n" />
+        <Key
+            android:codes="109"
+            android:keyLabel="m" />
+        <Key
+            android:codes="@integer/id_keyboard_delete"
+            android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p"
+            android:keyEdgeFlags="right"
+            android:isRepeatable="true" />
+    </Row>
+
+    <Row android:rowEdgeFlags="bottom">
+        <Key
+            android:codes="@integer/id_keyboard_numbers"
+            android:keyLabel="123,!"
+            android:keyWidth="10%p" />
+        <Key
+            android:codes="32"
+            android:keyIcon="@drawable/sym_keyboard_space"
+            android:keyWidth="30%p"
+            android:isRepeatable="true" />
+    </Row>
+</Keyboard>

+ 142 - 0
app/src/main/res/xml/qwerty_symbol_shift.xml

@@ -0,0 +1,142 @@
+<?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.
+*/
+-->
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="9%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/keyboard_key_height">
+    <Row>
+        <Key
+            android:codes="126"
+            android:keyLabel="~"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="177"
+            android:keyLabel="±" />
+        <Key
+            android:codes="215"
+            android:keyLabel="×" />
+        <Key
+            android:codes="247"
+            android:keyLabel="÷" />
+        <Key
+            android:codes="8226"
+            android:keyLabel="•" />
+        <Key
+            android:codes="176"
+            android:keyLabel="°" />
+        <Key
+            android:codes="96"
+            android:keyLabel="`" />
+        <Key
+            android:codes="180"
+            android:keyLabel="´" />
+        <Key
+            android:codes="123"
+            android:keyLabel="{" />
+        <Key
+            android:codes="125"
+            android:keyLabel="}"
+            android:keyEdgeFlags="right" />
+    </Row>
+
+    <Row>
+        <Key
+            android:codes="169"
+            android:keyLabel="©"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="163"
+            android:keyLabel="£" />
+        <Key
+            android:codes="8364"
+            android:keyLabel="€" />
+        <Key
+            android:codes="94"
+            android:keyLabel="^" />
+        <Key
+            android:codes="174"
+            android:keyLabel="®" />
+        <Key
+            android:codes="165"
+            android:keyLabel="¥" />
+        <Key
+            android:codes="95"
+            android:keyLabel="_" />
+        <Key
+            android:codes="43"
+            android:keyLabel="+" />
+        <Key
+            android:codes="91"
+            android:keyLabel="[" />
+        <Key
+            android:codes="93"
+            android:keyLabel="]"
+            android:keyEdgeFlags="right" />
+    </Row>
+
+    <Row>
+        <Key
+            android:codes="@integer/id_keyboard_shift"
+            android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p"
+            android:isModifier="true"
+            android:isSticky="true"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="161"
+            android:keyLabel="¡" />
+        <Key
+            android:codes="60"
+            android:keyLabel="&lt;" />
+        <Key
+            android:codes="62"
+            android:keyLabel="&gt;" />
+        <Key
+            android:codes="162"
+            android:keyLabel="¢" />
+        <Key
+            android:codes="124"
+            android:keyLabel="|" />
+        <Key
+            android:codes="92"
+            android:keyLabel="\\" />
+        <Key
+            android:codes="191"
+            android:keyLabel="¿" />
+        <Key
+            android:codes="@integer/id_keyboard_delete"
+            android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p"
+            android:keyEdgeFlags="right"
+            android:isRepeatable="true" />
+    </Row>
+
+    <Row android:rowEdgeFlags="bottom">
+        <Key
+            android:codes="@integer/id_keyboard_numbers"
+            android:keyLabel="ABC"
+            android:keyWidth="10%p" />
+        <Key
+            android:codes="32"
+            android:keyIcon="@drawable/sym_keyboard_space"
+            android:keyWidth="30%p"
+            android:isRepeatable="true" />
+    </Row>
+</Keyboard>

+ 146 - 0
app/src/main/res/xml/qwerty_symbols.xml

@@ -0,0 +1,146 @@
+<?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.
+*/
+-->
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="9%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/keyboard_key_height">
+    <Row>
+        <Key
+            android:codes="49"
+            android:keyLabel="1"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="50"
+            android:keyLabel="2" />
+        <Key
+            android:codes="51"
+            android:keyLabel="3" />
+        <Key
+            android:codes="52"
+            android:keyLabel="4" />
+        <Key
+            android:codes="53"
+            android:keyLabel="5" />
+        <Key
+            android:codes="54"
+            android:keyLabel="6" />
+        <Key
+            android:codes="55"
+            android:keyLabel="7" />
+        <Key
+            android:codes="56"
+            android:keyLabel="8" />
+        <Key
+            android:codes="57"
+            android:keyLabel="9" />
+        <Key
+            android:codes="48"
+            android:keyLabel="0"
+            android:keyEdgeFlags="right" />
+    </Row>
+
+    <Row>
+        <Key
+            android:codes="64"
+            android:keyLabel="\@"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="35"
+            android:keyLabel="\#" />
+        <Key
+            android:codes="36"
+            android:keyLabel="$" />
+        <Key
+            android:codes="37"
+            android:keyLabel="%" />
+        <Key
+            android:codes="38"
+            android:keyLabel="&amp;" />
+        <Key
+            android:codes="42"
+            android:keyLabel="*" />
+        <Key
+            android:codes="45"
+            android:keyLabel="-" />
+        <Key
+            android:codes="61"
+            android:keyLabel="=" />
+        <Key
+            android:codes="40"
+            android:keyLabel="(" />
+        <Key
+            android:codes="41"
+            android:keyLabel=")"
+            android:keyEdgeFlags="right" />
+    </Row>
+
+    <Row>
+        <Key
+            android:codes="@integer/id_keyboard_shift"
+            android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p"
+            android:isModifier="true"
+            android:isSticky="true"
+            android:keyEdgeFlags="left" />
+        <Key
+            android:codes="33"
+            android:keyLabel="!" />
+        <Key
+            android:codes="34"
+            android:keyLabel="&quot;" />
+        <Key
+            android:codes="39"
+            android:keyLabel="\'" />
+        <Key
+            android:codes="58"
+            android:keyLabel=":" />
+        <Key
+            android:codes="59"
+            android:keyLabel=";" />
+        <Key
+            android:codes="47"
+            android:keyLabel="/" />
+        <Key
+            android:codes="63"
+            android:keyLabel="\?" />
+        <Key
+            android:codes="-5"
+            android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p"
+            android:keyEdgeFlags="right"
+            android:isRepeatable="true" />
+    </Row>
+
+    <Row android:rowEdgeFlags="bottom">
+        <Key
+            android:codes="@integer/id_keyboard_numbers"
+            android:keyLabel="ABC"
+            android:keyWidth="10%p" />
+        <Key
+            android:codes="32"
+            android:keyIcon="@drawable/sym_keyboard_space"
+            android:keyWidth="30%p"
+            android:isRepeatable="true" />
+        <Key
+            android:codes="44"
+            android:keyLabel=","
+            android:keyWidth="15%p" />
+    </Row>
+</Keyboard>