implemented experimental AutoFill functionality

This commit is contained in:
Philipp Crocoll 2014-01-06 11:40:49 +01:00
parent bbb32eda26
commit 856d0a5544
3 changed files with 150 additions and 32 deletions

View File

@ -3,15 +3,15 @@ package keepass2android.kbbridge;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
public class KeyboardData { public class KeyboardData
{
public static List<StringForTyping> availableFields = new ArrayList<StringForTyping>(); public static List<StringForTyping> availableFields = new ArrayList<StringForTyping>();
public static String entryName; public static String entryName;
public static String entryId;
public static void clear() public static void clear()
{ {
availableFields.clear(); availableFields.clear();
entryName = entryId = "";
} }
} }

View File

@ -1,7 +1,20 @@
package keepass2android.kbbridge; package keepass2android.kbbridge;
public class StringForTyping { public class StringForTyping {
public String displayName; public String key; //internal identifier (PwEntry string field key)
public String displayName; //display name for displaying the key (might be translated)
public String value; public String value;
@Override
public StringForTyping clone(){
StringForTyping theClone = new StringForTyping();
theClone.key = key;
theClone.displayName = displayName;
theClone.value = value;
return theClone;
}
} }

View File

@ -17,15 +17,16 @@
package keepass2android.softkeyboard; package keepass2android.softkeyboard;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.inputmethodservice.InputMethodService; import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard; import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView; import android.inputmethodservice.KeyboardView;
import android.provider.Settings;
import android.text.InputType; import android.text.InputType;
import android.util.Log;
import android.util.Printer;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.Window; import android.view.Window;
@ -37,7 +38,6 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import keepass2android.kbbridge.StringForTyping; import keepass2android.kbbridge.StringForTyping;
@ -231,6 +231,92 @@ public class KP2AKeyboard extends InputMethodService implements
// Update the label on the enter key, depending on what the application // Update the label on the enter key, depending on what the application
// says it will do. // says it will do.
mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions); mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions);
tryAutoFillIn(attribute);
}
private boolean tryAutoFillIn(final EditorInfo attribute) {
//auto fill in?
//TODO: make this inside an AsyncTask: getText* might be slow
CharSequence textAfter = getCurrentInputConnection().getTextAfterCursor(1 /*length*/, 0 /*flags*/);
CharSequence textBefore = getCurrentInputConnection().getTextBeforeCursor(1 /*length*/, 0 /*flags*/);
boolean hasTextInField = ((textAfter != null) && (textAfter.length() > 0))
|| ((textBefore != null) && (textBefore.length()> 0));
if (!hasTextInField)
{
//try to look up saved field ids:
if (attribute.fieldId > -1)
{
SharedPreferences prefs = getApplicationContext().getSharedPreferences("savedFieldIds", MODE_PRIVATE);
String key = attribute.packageName+"/"+attribute.fieldId;
Log.d("KP2AK", "looking up saved field for "+key);
String fieldKey = prefs.getString(key, "");
if ("".equals(fieldKey) == false)
{
Log.d("KP2AK","Found field "+fieldKey);
if (commitTextForKey(attribute, fieldKey))
return true;
}
}
//try to look up saved field hint:
if ((attribute.hintText != null) && (attribute.hintText.length() > 0))
{
SharedPreferences prefs = getApplicationContext().getSharedPreferences("savedFieldHints", MODE_PRIVATE);
String key = attribute.packageName+"/"+keepass2android.kbbridge.KeyboardData.entryId+"/"+attribute.hintText;
Log.d("KP2AK", "looking up saved field hint for "+key);
String displayName = prefs.getString(key, "");
if ("".equals(displayName) == false)
{
Log.d("KP2AK","Found field "+displayName);
if (commitTextForKey(attribute, displayName))
return true;
}
}
//try to look up by hint
if ((attribute.hintText != null) && (attribute.hintText.length() > 0))
{
if (commitTextForKey(attribute, attribute.hintText.toString()))
return true;
}
}
return false;
}
private boolean commitTextForKey(final EditorInfo attribute, String key) {
List<StringForTyping> availableFields = keepass2android.kbbridge.KeyboardData.availableFields;
for (StringForTyping str: availableFields)
{
if (str.key.equals(key))
{
Log.d("KP2AK", "Typing!");
commitKp2aString(str.value, attribute);
return true;
}
}
return false;
}
private void commitKp2aString(String value, EditorInfo editorInfo) {
getCurrentInputConnection().commitText(
value, 0);
if ((editorInfo.imeOptions&(EditorInfo.IME_MASK_ACTION|EditorInfo.IME_FLAG_NO_ENTER_ACTION)) == EditorInfo.IME_ACTION_NEXT)
{
Log.d("KP2AK", "action is NEXT");
getCurrentInputConnection().performEditorAction(editorInfo.actionId);
}
} }
/** /**
@ -290,6 +376,7 @@ public class KP2AKeyboard extends InputMethodService implements
mComposing.setLength(0); mComposing.setLength(0);
updateCandidates(); updateCandidates();
InputConnection ic = getCurrentInputConnection(); InputConnection ic = getCurrentInputConnection();
if (ic != null) { if (ic != null) {
ic.finishComposingText(); ic.finishComposingText();
} }
@ -456,51 +543,62 @@ public class KP2AKeyboard extends InputMethodService implements
String title = "Keepass2Android"; String title = "Keepass2Android";
List<StringForTyping> availableFields = keepass2android.kbbridge.KeyboardData.availableFields; List<StringForTyping> availableFields = keepass2android.kbbridge.KeyboardData.availableFields;
final ArrayList<String> items = new ArrayList<String>(); Log.d("KP2AK", "hint: "+getCurrentInputEditorInfo().hintText);
final ArrayList<String> values = new ArrayList<String>(); Log.d("KP2AK", "field name: "+getCurrentInputEditorInfo().fieldName);
Log.d("KP2AK", "label: "+getCurrentInputEditorInfo().label);
getCurrentInputEditorInfo().dump(new Printer() {
@Override
public void println(String x) {
Log.d("KP2AK", x);
}
},"");
final ArrayList<StringForTyping> items = new ArrayList<StringForTyping>();
for (StringForTyping entry : availableFields) for (StringForTyping entry : availableFields)
{ {
String key = entry.displayName; items.add(entry.clone());
String value = entry.value;
items.add(key);
values.add(value);
} }
if (keepass2android.kbbridge.KeyboardData.entryName == null) if (keepass2android.kbbridge.KeyboardData.entryName == null)
{ {
items.add(getString(R.string.open_entry)); StringForTyping openEntry = new StringForTyping();
values.add("KP2ASPECIAL_SelectEntryTask"); openEntry.displayName = openEntry.key = getString(R.string.open_entry);
openEntry.value = "KP2ASPECIAL_SelectEntryTask";
items.add(openEntry);
} }
else else
{ {
title += " ("+keepass2android.kbbridge.KeyboardData.entryName+")"; StringForTyping changeEntry = new StringForTyping();
items.add(getString(R.string.change_entry)); changeEntry.displayName = changeEntry.key = getString(R.string.change_entry);
values.add(""); changeEntry.value = "KP2ASPECIAL_SelectEntryTask";
items.add(changeEntry);
} }
if ((mClientPackageName != null) && (mClientPackageName != "")) if ((mClientPackageName != null) && (mClientPackageName != ""))
{ {
items.add(getString(R.string.open_entry_for_app,mClientPackageName)); StringForTyping searchEntry = new StringForTyping();
values.add("KP2ASPECIAL_SearchUrlTask"); searchEntry.key = searchEntry.displayName
= getString(R.string.open_entry_for_app,mClientPackageName);
searchEntry.value = "KP2ASPECIAL_SearchUrlTask";
items.add(searchEntry);
} }
builder.setTitle(title); builder.setTitle(title);
// builder.setMessage("What do you want to type securely?");
builder.setItems(items.toArray(new CharSequence[items.size()]), builder.setItems(items.toArray(new CharSequence[items.size()]),
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) { public void onClick(DialogInterface dialog, int item) {
if (values.get(item).startsWith("KP2ASPECIAL")) { if (items.get(item).value.startsWith("KP2ASPECIAL")) {
//change entry //change entry
Intent startKp2aIntent = getPackageManager().getLaunchIntentForPackage(getApplicationContext().getPackageName()); Intent startKp2aIntent = getPackageManager().getLaunchIntentForPackage(getApplicationContext().getPackageName());
//Intent startKp2aIntent = getPackageManager().getLaunchIntentForPackage("keepass2android.keepass2android");
if (startKp2aIntent != null) if (startKp2aIntent != null)
{ {
startKp2aIntent.addCategory(Intent.CATEGORY_LAUNCHER); startKp2aIntent.addCategory(Intent.CATEGORY_LAUNCHER);
startKp2aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startKp2aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
String value = values.get(item); String value = items.get(item).value;
String taskName = value.substring("KP2ASPECIAL_".length()); String taskName = value.substring("KP2ASPECIAL_".length());
startKp2aIntent.putExtra("KP2A_APPTASK", taskName); startKp2aIntent.putExtra("KP2A_APPTASK", taskName);
if (taskName.equals("SearchUrlTask")) if (taskName.equals("SearchUrlTask"))
@ -508,14 +606,21 @@ public class KP2AKeyboard extends InputMethodService implements
startKp2aIntent.putExtra("UrlToSearch", "androidapp://"+mClientPackageName); startKp2aIntent.putExtra("UrlToSearch", "androidapp://"+mClientPackageName);
} }
startActivity(startKp2aIntent); startActivity(startKp2aIntent);
Settings.Secure.getString(
getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD
);
} }
} else { } else {
getCurrentInputConnection().commitText(
values.get(item), 0);
if (getCurrentInputEditorInfo().fieldId > 0)
{
SharedPreferences savedFieldIds = getApplicationContext().getSharedPreferences("savedFieldIds", MODE_PRIVATE);
Editor edit = savedFieldIds.edit();
edit.putString(getCurrentInputEditorInfo().packageName+"/"+getCurrentInputEditorInfo().fieldId, items.get(item).key);
edit.commit();
}
commitKp2aString(items.get(item).value, getCurrentInputEditorInfo());
} }
} }
}); });