mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-11-25 18:52:19 -05:00
Integrated new EntryActivity to KP2A
This commit is contained in:
parent
685c6c6453
commit
9008785c69
Binary file not shown.
@ -36,58 +36,75 @@ using Android.Graphics;
|
||||
using Java.IO;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Security;
|
||||
using KeePassLib.Utility;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using PluginHostTest;
|
||||
using keepass2android.Io;
|
||||
using Uri = Android.Net.Uri;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
|
||||
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
|
||||
Theme = "@style/NoTitleBar")]
|
||||
public class EntryActivity : Activity
|
||||
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden, Theme="@style/NoTitleBar")]
|
||||
public class EntryActivity : LockCloseActivity
|
||||
{
|
||||
public const String KeyEntry = "entry";
|
||||
public const String KeyRefreshPos = "refresh_pos";
|
||||
public const String KeyCloseAfterCreate = "close_after_create";
|
||||
|
||||
protected PwEntry Entry = new PwEntry(true, true);
|
||||
public static void Launch(Activity act, PwEntry pw, int pos, AppTask appTask)
|
||||
{
|
||||
Intent i = new Intent(act, typeof(EntryActivity));
|
||||
|
||||
|
||||
i.PutExtra(KeyEntry, pw.Uuid.ToHexString());
|
||||
i.PutExtra(KeyRefreshPos, pos);
|
||||
|
||||
appTask.ToIntent(i);
|
||||
|
||||
act.StartActivityForResult(i, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected PwEntry Entry;
|
||||
|
||||
private static Typeface _passwordFont;
|
||||
|
||||
internal bool _showPassword;
|
||||
private int _pos;
|
||||
|
||||
AppTask _appTask;
|
||||
private List<TextView> _protectedTextViews;
|
||||
private IMenu _menu;
|
||||
|
||||
private readonly Dictionary<string, List<IPopupMenuItem>> _popupMenuItems =
|
||||
new Dictionary<string, List<IPopupMenuItem>>();
|
||||
|
||||
private readonly Dictionary<string, IStringView> _stringViews = new Dictionary<string, IStringView>();
|
||||
private readonly List<PluginMenuOption> _pendingMenuOptions = new List<PluginMenuOption>();
|
||||
private IMenu _menu;
|
||||
|
||||
|
||||
protected void SetEntryView()
|
||||
{
|
||||
SetContentView(Resource.Layout.entry_view);
|
||||
}
|
||||
|
||||
protected void SetupEditButtons()
|
||||
{
|
||||
View edit = FindViewById(Resource.Id.entry_edit);
|
||||
if (true)
|
||||
protected void SetupEditButtons() {
|
||||
View edit = FindViewById(Resource.Id.entry_edit);
|
||||
if (App.Kp2a.GetDb().CanWrite)
|
||||
{
|
||||
edit.Visibility = ViewStates.Visible;
|
||||
edit.Click += (sender, e) =>
|
||||
{
|
||||
|
||||
};
|
||||
{
|
||||
EntryEditActivity.Launch(this, Entry, _appTask);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
edit.Visibility = ViewStates.Gone;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private class PluginActionReceiver : BroadcastReceiver
|
||||
@ -171,7 +188,7 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
//update the Entry output in the App database and notify the CopyToClipboard service
|
||||
App.Kp2A.GetDb().LastOpenedEntry.OutputStrings.Set(key, new ProtectedString(isProtected, value));
|
||||
App.Kp2a.GetDb().LastOpenedEntry.OutputStrings.Set(key, new ProtectedString(isProtected, value));
|
||||
Intent updateKeyboardIntent = new Intent(this, typeof(CopyToClipboardService));
|
||||
Intent.SetAction(Intents.UpdateKeyboard);
|
||||
updateKeyboardIntent.PutExtra(KeyEntry, Entry.Uuid.ToHexString());
|
||||
@ -221,7 +238,7 @@ namespace keepass2android
|
||||
i.SetPackage(pluginPackage);
|
||||
i.PutExtra(Strings.ExtraActionData, bundleExtra);
|
||||
i.PutExtra(Strings.ExtraSender, PackageName);
|
||||
PluginHost.AddEntryToIntent(i, App.Kp2A.GetDb().LastOpenedEntry);
|
||||
PluginHost.AddEntryToIntent(i, App.Kp2a.GetDb().LastOpenedEntry);
|
||||
|
||||
var menuOption = new PluginMenuOption()
|
||||
{
|
||||
@ -254,167 +271,7 @@ namespace keepass2android
|
||||
menuItem.SetIntent(menuOption.Intent);
|
||||
}
|
||||
|
||||
public override bool OnCreateOptionsMenu(IMenu menu)
|
||||
{
|
||||
_menu = menu;
|
||||
base.OnCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = MenuInflater;
|
||||
inflater.Inflate(Resource.Menu.entry, menu);
|
||||
|
||||
lock (_pendingMenuOptions)
|
||||
{
|
||||
foreach (var option in _pendingMenuOptions)
|
||||
AddMenuOption(option);
|
||||
_pendingMenuOptions.Clear();
|
||||
}
|
||||
|
||||
|
||||
UpdateTogglePasswordMenu();
|
||||
|
||||
IMenuItem gotoUrl = menu.FindItem(Resource.Id.menu_goto_url);
|
||||
//Disabled IMenuItem copyUser = menu.FindItem(Resource.Id.menu_copy_user);
|
||||
//Disabled IMenuItem copyPass = menu.FindItem(Resource.Id.menu_copy_pass);
|
||||
|
||||
// In API >= 11 onCreateOptionsMenu may be called before onCreate completes
|
||||
// so _entry may not be set
|
||||
if (Entry == null)
|
||||
{
|
||||
gotoUrl.SetVisible(false);
|
||||
//Disabled copyUser.SetVisible(false);
|
||||
//Disabled copyPass.SetVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
String url = Entry.Strings.ReadSafe(PwDefs.UrlField);
|
||||
if (String.IsNullOrEmpty(url))
|
||||
{
|
||||
// disable button if url is not available
|
||||
gotoUrl.SetVisible(false);
|
||||
}
|
||||
if (String.IsNullOrEmpty(Entry.Strings.ReadSafe(PwDefs.UserNameField)))
|
||||
{
|
||||
// disable button if username is not available
|
||||
//Disabled copyUser.SetVisible(false);
|
||||
}
|
||||
if (String.IsNullOrEmpty(Entry.Strings.ReadSafe(PwDefs.PasswordField)))
|
||||
{
|
||||
// disable button if password is not available
|
||||
//Disabled copyPass.SetVisible(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item)
|
||||
{
|
||||
//check if this is a plugin action
|
||||
if ((item.Intent != null) && (item.Intent.Action == Strings.ActionEntryActionSelected))
|
||||
{
|
||||
//yes. let the plugin handle the click:
|
||||
SendBroadcast(item.Intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (item.ItemId)
|
||||
{
|
||||
case Resource.Id.menu_donate:
|
||||
try
|
||||
{
|
||||
// Util.GotoDonateUrl(this);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.error_failed_to_launch_link, ToastLength.Long).Show();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
case Resource.Id.menu_toggle_pass:
|
||||
if (_showPassword)
|
||||
{
|
||||
item.SetTitle(Resource.String.show_password);
|
||||
_showPassword = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SetTitle(Resource.String.menu_hide_password);
|
||||
_showPassword = true;
|
||||
}
|
||||
SetPasswordStyle();
|
||||
|
||||
return true;
|
||||
|
||||
case Resource.Id.menu_goto_url:
|
||||
string url = _stringViews[PwDefs.UrlField].Text;
|
||||
if (url == null) return false;
|
||||
|
||||
// Default http:// if no protocol specified
|
||||
if (!url.Contains("://"))
|
||||
{
|
||||
url = "http://" + url;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
/* TODO: required?
|
||||
case Resource.Id.menu_copy_user:
|
||||
timeoutCopyToClipboard(_entry.Strings.ReadSafe (PwDefs.UserNameField));
|
||||
return true;
|
||||
|
||||
case Resource.Id.menu_copy_pass:
|
||||
timeoutCopyToClipboard(_entry.Strings.ReadSafe (PwDefs.UserNameField));
|
||||
return true;
|
||||
*/
|
||||
case Resource.Id.menu_rate:
|
||||
try
|
||||
{
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Resource.Id.menu_suggest_improvements:
|
||||
try
|
||||
{
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Resource.Id.menu_lock:
|
||||
return true;
|
||||
case Resource.Id.menu_translate:
|
||||
try
|
||||
{
|
||||
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Android.Resource.Id.Home:
|
||||
//Currently the action bar only displays the home button when we come from a previous activity.
|
||||
//So we can simply Finish. See this page for information on how to do this in more general (future?) cases:
|
||||
//http://developer.android.com/training/implementing-navigation/ancestral.html
|
||||
Finish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
@ -431,20 +288,55 @@ namespace keepass2android
|
||||
_showPassword =
|
||||
!prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));
|
||||
|
||||
Entry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "philipp "));
|
||||
Entry.Strings.Set(PwDefs.PasswordField, new ProtectedString(true, "password value"));
|
||||
Entry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "https://www.google.com"));
|
||||
Entry.Strings.Set("field header", new ProtectedString(true, "protected field value"));
|
||||
Entry.Strings.Set("public field header", new ProtectedString(false, "public field value"));
|
||||
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
new ActivityDesign(this).ApplyTheme();
|
||||
|
||||
SetEntryView();
|
||||
|
||||
Database db = App.Kp2a.GetDb();
|
||||
// Likely the app has been killed exit the activity
|
||||
if (!db.Loaded || (App.Kp2a.QuickLocked))
|
||||
{
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
SetResult(KeePass.ExitNormal);
|
||||
|
||||
Intent i = Intent;
|
||||
PwUuid uuid = new PwUuid(MemUtil.HexStringToByteArray(i.GetStringExtra(KeyEntry)));
|
||||
_pos = i.GetIntExtra(KeyRefreshPos, -1);
|
||||
|
||||
_appTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
|
||||
|
||||
Entry = db.Entries[uuid];
|
||||
|
||||
// Refresh Menu contents in case onCreateMenuOptions was called before Entry was set
|
||||
ActivityCompat.InvalidateOptionsMenu(this);
|
||||
|
||||
// Update last access time.
|
||||
Entry.Touch(false);
|
||||
|
||||
if (PwDefs.IsTanEntry(Entry) && prefs.GetBoolean(GetString(Resource.String.TanExpiresOnUse_key), Resources.GetBoolean(Resource.Boolean.TanExpiresOnUse_default)) && ((Entry.Expires == false) || Entry.ExpiryTime > DateTime.Now))
|
||||
{
|
||||
PwEntry backupEntry = Entry.CloneDeep();
|
||||
Entry.ExpiryTime = DateTime.Now;
|
||||
Entry.Expires = true;
|
||||
Entry.Touch(true);
|
||||
RequiresRefresh();
|
||||
UpdateEntry update = new UpdateEntry(this, App.Kp2a, backupEntry, Entry, null);
|
||||
ProgressTask pt = new ProgressTask(App.Kp2a, this, update);
|
||||
pt.Run();
|
||||
}
|
||||
FillData();
|
||||
|
||||
SetupEditButtons();
|
||||
|
||||
App.Kp2A.GetDb().LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2A.GetDb().KpDatabase);
|
||||
//depending on the app task, the final things to do might be delayed, so let the appTask call CompleteOnCreate when appropriate
|
||||
_appTask.OnCompleteCreateEntryActivity(this);
|
||||
|
||||
App.Kp2a.GetDb().LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2a.GetDb().KpDatabase);
|
||||
|
||||
RegisterReceiver(new PluginActionReceiver(this), new IntentFilter(Strings.ActionAddEntryAction));
|
||||
RegisterReceiver(new PluginFieldReceiver(this), new IntentFilter(Strings.ActionSetEntryField));
|
||||
@ -454,8 +346,6 @@ namespace keepass2android
|
||||
|
||||
private void NotifyPluginsOnOpen()
|
||||
{
|
||||
App.Kp2A.GetDb().SetEntry(Entry);
|
||||
|
||||
Intent i = new Intent(Strings.ActionOpenEntry);
|
||||
i.PutExtra(Strings.ExtraSender, PackageName);
|
||||
AddEntryToIntent(i);
|
||||
@ -485,6 +375,26 @@ namespace keepass2android
|
||||
public void CompleteOnCreate()
|
||||
{
|
||||
|
||||
Intent showNotIntent = new Intent(this, typeof(CopyToClipboardService));
|
||||
Intent.SetAction(Intents.ShowNotification);
|
||||
showNotIntent.PutExtra(KeyEntry, Entry.Uuid.ToHexString());
|
||||
bool closeAfterCreate = _appTask.CloseEntryActivityAfterCreate;
|
||||
showNotIntent.PutExtra(KeyCloseAfterCreate, closeAfterCreate);
|
||||
|
||||
StartService(showNotIntent);
|
||||
|
||||
Kp2aLog.Log("Requesting copy to clipboard for Uuid=" + Entry.Uuid.ToHexString());
|
||||
|
||||
/*foreach (PwUuid key in App.Kp2a.GetDb().entries.Keys)
|
||||
{
|
||||
Kp2aLog.Log(this,key.ToHexString() + " -> " + App.Kp2a.GetDb().entries[key].Uuid.ToHexString());
|
||||
}*/
|
||||
|
||||
if (closeAfterCreate)
|
||||
{
|
||||
SetResult(KeePass.ExitCloseAfterTaskComplete);
|
||||
Finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -552,6 +462,8 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private List<IPopupMenuItem> RegisterPopup(string popupKey, View clickView, View anchorView)
|
||||
{
|
||||
clickView.Click += (sender, args) =>
|
||||
@ -561,6 +473,97 @@ namespace keepass2android
|
||||
_popupMenuItems[popupKey] = new List<IPopupMenuItem>();
|
||||
return _popupMenuItems[popupKey];
|
||||
}
|
||||
internal Android.Net.Uri WriteBinaryToFile(string key, bool writeToCacheDirectory)
|
||||
{
|
||||
ProtectedBinary pb = Entry.Binaries.Get(key);
|
||||
System.Diagnostics.Debug.Assert(pb != null);
|
||||
if (pb == null)
|
||||
throw new ArgumentException();
|
||||
|
||||
|
||||
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
|
||||
string binaryDirectory = prefs.GetString(GetString(Resource.String.BinaryDirectory_key), GetString(Resource.String.BinaryDirectory_default));
|
||||
if (writeToCacheDirectory)
|
||||
binaryDirectory = CacheDir.Path + File.Separator + AttachmentContentProvider.AttachmentCacheSubDir;
|
||||
|
||||
string filepart = key;
|
||||
if (writeToCacheDirectory)
|
||||
filepart = filepart.Replace(" ", "");
|
||||
var targetFile = new File(binaryDirectory, filepart);
|
||||
|
||||
File parent = targetFile.ParentFile;
|
||||
|
||||
if (parent == null || (parent.Exists() && !parent.IsDirectory))
|
||||
{
|
||||
Toast.MakeText(this,
|
||||
Resource.String.error_invalid_path,
|
||||
ToastLength.Long).Show();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!parent.Exists())
|
||||
{
|
||||
// Create parent directory
|
||||
if (!parent.Mkdirs())
|
||||
{
|
||||
Toast.MakeText(this,
|
||||
Resource.String.error_could_not_create_parent,
|
||||
ToastLength.Long).Show();
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
string filename = targetFile.AbsolutePath;
|
||||
Android.Net.Uri fileUri = Android.Net.Uri.FromFile(targetFile);
|
||||
|
||||
byte[] pbData = pb.ReadData();
|
||||
try
|
||||
{
|
||||
System.IO.File.WriteAllBytes(filename, pbData);
|
||||
}
|
||||
catch (Exception exWrite)
|
||||
{
|
||||
Toast.MakeText(this, GetString(Resource.String.SaveAttachment_Failed, new Java.Lang.Object[] { filename })
|
||||
+ exWrite.Message, ToastLength.Long).Show();
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemUtil.ZeroByteArray(pbData);
|
||||
}
|
||||
Toast.MakeText(this, GetString(Resource.String.SaveAttachment_doneMessage, new Java.Lang.Object[] { filename }), ToastLength.Short).Show();
|
||||
if (writeToCacheDirectory)
|
||||
{
|
||||
return Android.Net.Uri.Parse("content://" + AttachmentContentProvider.Authority + "/"
|
||||
+ filename);
|
||||
}
|
||||
return fileUri;
|
||||
}
|
||||
|
||||
internal void OpenBinaryFile(Android.Net.Uri uri)
|
||||
{
|
||||
|
||||
|
||||
String theMimeType = GetMimeType(uri.Path);
|
||||
if (theMimeType != null)
|
||||
{
|
||||
|
||||
Intent theIntent = new Intent(Intent.ActionView);
|
||||
theIntent.AddFlags(ActivityFlags.NewTask | ActivityFlags.ExcludeFromRecents);
|
||||
theIntent.SetDataAndType(uri, theMimeType);
|
||||
try
|
||||
{
|
||||
StartActivity(theIntent);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
//ignore
|
||||
Toast.MakeText(this, "Couldn't open file", ToastLength.Short).Show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void RegisterProtectedTextView(TextView protectedTextView)
|
||||
@ -572,7 +575,7 @@ namespace keepass2android
|
||||
private void PopulateBinaries()
|
||||
{
|
||||
ViewGroup binariesGroup = (ViewGroup) FindViewById(Resource.Id.binaries);
|
||||
foreach (KeyValuePair<string, string> pair in new Dictionary<string, string>() {{"abc", ""}, {"test.png", "uia"}})
|
||||
foreach (KeyValuePair<string, ProtectedBinary> pair in Entry.Binaries)
|
||||
{
|
||||
String key = pair.Key;
|
||||
|
||||
@ -645,6 +648,7 @@ namespace keepass2android
|
||||
public override void OnBackPressed()
|
||||
{
|
||||
base.OnBackPressed();
|
||||
OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
}
|
||||
|
||||
protected void FillData()
|
||||
@ -658,11 +662,9 @@ namespace keepass2android
|
||||
|
||||
|
||||
|
||||
ActionBar.Title = "Entry title";
|
||||
ActionBar.Title = Entry.Strings.ReadSafe(PwDefs.TitleField);
|
||||
ActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
|
||||
|
||||
|
||||
PopulateStandardText(Resource.Id.entry_user_name, Resource.Id.entryfield_container_username, PwDefs.UserNameField);
|
||||
PopulateStandardText(Resource.Id.entry_url, Resource.Id.entryfield_container_url, PwDefs.UrlField);
|
||||
PopulateStandardText(Resource.Id.entry_password, Resource.Id.entryfield_container_password, PwDefs.PasswordField);
|
||||
@ -701,18 +703,7 @@ namespace keepass2android
|
||||
SetPasswordStyle();
|
||||
}
|
||||
|
||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||
{
|
||||
base.OnActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == /*TODO*/ 0)
|
||||
{
|
||||
if (resultCode == /*TODO*/ 0)
|
||||
{
|
||||
RequiresRefresh();
|
||||
}
|
||||
Recreate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
@ -778,7 +769,9 @@ namespace keepass2android
|
||||
|
||||
private void SetPasswordTypeface(TextView textView)
|
||||
{
|
||||
|
||||
if (_passwordFont == null)
|
||||
_passwordFont = Typeface.CreateFromAsset(Assets, "DejaVuSansMono.ttf");
|
||||
textView.Typeface = _passwordFont;
|
||||
}
|
||||
|
||||
private void PopulateText(int viewId, int containerViewId, int resId)
|
||||
@ -817,9 +810,83 @@ namespace keepass2android
|
||||
Intent ret = new Intent();
|
||||
ret.PutExtra(KeyRefreshPos, _pos);
|
||||
|
||||
SetResult(KeePass.ExitRefresh, ret);
|
||||
}
|
||||
|
||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) {
|
||||
base.OnActivityResult(requestCode, resultCode, data);
|
||||
if ( resultCode == KeePass.ExitRefresh || resultCode == KeePass.ExitRefreshTitle ) {
|
||||
if ( resultCode == KeePass.ExitRefreshTitle ) {
|
||||
RequiresRefresh ();
|
||||
}
|
||||
Recreate();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnCreateOptionsMenu(IMenu menu)
|
||||
{
|
||||
_menu = menu;
|
||||
base.OnCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = MenuInflater;
|
||||
inflater.Inflate(Resource.Menu.entry, menu);
|
||||
|
||||
lock (_pendingMenuOptions)
|
||||
{
|
||||
foreach (var option in _pendingMenuOptions)
|
||||
AddMenuOption(option);
|
||||
_pendingMenuOptions.Clear();
|
||||
}
|
||||
|
||||
|
||||
UpdateTogglePasswordMenu();
|
||||
|
||||
IMenuItem gotoUrl = menu.FindItem(Resource.Id.menu_goto_url);
|
||||
//Disabled IMenuItem copyUser = menu.FindItem(Resource.Id.menu_copy_user);
|
||||
//Disabled IMenuItem copyPass = menu.FindItem(Resource.Id.menu_copy_pass);
|
||||
|
||||
// In API >= 11 onCreateOptionsMenu may be called before onCreate completes
|
||||
// so _entry may not be set
|
||||
if (Entry == null)
|
||||
{
|
||||
gotoUrl.SetVisible(false);
|
||||
//Disabled copyUser.SetVisible(false);
|
||||
//Disabled copyPass.SetVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
String url = Entry.Strings.ReadSafe(PwDefs.UrlField);
|
||||
if (String.IsNullOrEmpty(url))
|
||||
{
|
||||
// disable button if url is not available
|
||||
gotoUrl.SetVisible(false);
|
||||
}
|
||||
if (String.IsNullOrEmpty(Entry.Strings.ReadSafe(PwDefs.UserNameField)))
|
||||
{
|
||||
// disable button if username is not available
|
||||
//Disabled copyUser.SetVisible(false);
|
||||
}
|
||||
if (String.IsNullOrEmpty(Entry.Strings.ReadSafe(PwDefs.PasswordField)))
|
||||
{
|
||||
// disable button if password is not available
|
||||
//Disabled copyPass.SetVisible(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateTogglePasswordMenu()
|
||||
{
|
||||
IMenuItem togglePassword = _menu.FindItem(Resource.Id.menu_toggle_pass);
|
||||
if (_showPassword)
|
||||
{
|
||||
togglePassword.SetTitle(Resource.String.menu_hide_password);
|
||||
}
|
||||
else
|
||||
{
|
||||
togglePassword.SetTitle(Resource.String.show_password);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPasswordStyle()
|
||||
{
|
||||
@ -839,10 +906,123 @@ namespace keepass2android
|
||||
|
||||
protected override void OnResume()
|
||||
{
|
||||
|
||||
ClearCache();
|
||||
base.OnResume();
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
try
|
||||
{
|
||||
File dir = new File(CacheDir.Path + File.Separator + AttachmentContentProvider.AttachmentCacheSubDir);
|
||||
if (dir.IsDirectory)
|
||||
{
|
||||
IoUtil.DeleteDir(dir);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item)
|
||||
{
|
||||
//check if this is a plugin action
|
||||
if ((item.Intent != null) && (item.Intent.Action == Strings.ActionEntryActionSelected))
|
||||
{
|
||||
//yes. let the plugin handle the click:
|
||||
SendBroadcast(item.Intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (item.ItemId)
|
||||
{
|
||||
case Resource.Id.menu_donate:
|
||||
try
|
||||
{
|
||||
Util.GotoDonateUrl(this);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.error_failed_to_launch_link, ToastLength.Long).Show();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
case Resource.Id.menu_toggle_pass:
|
||||
if (_showPassword)
|
||||
{
|
||||
item.SetTitle(Resource.String.show_password);
|
||||
_showPassword = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SetTitle(Resource.String.menu_hide_password);
|
||||
_showPassword = true;
|
||||
}
|
||||
SetPasswordStyle();
|
||||
|
||||
return true;
|
||||
|
||||
case Resource.Id.menu_goto_url:
|
||||
return GotoUrl();
|
||||
/* TODO: required?
|
||||
case Resource.Id.menu_copy_user:
|
||||
timeoutCopyToClipboard(_entry.Strings.ReadSafe (PwDefs.UserNameField));
|
||||
return true;
|
||||
|
||||
case Resource.Id.menu_copy_pass:
|
||||
timeoutCopyToClipboard(_entry.Strings.ReadSafe (PwDefs.UserNameField));
|
||||
return true;
|
||||
*/
|
||||
case Resource.Id.menu_rate:
|
||||
try
|
||||
{
|
||||
Util.GotoMarket(this);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Resource.Id.menu_suggest_improvements:
|
||||
try
|
||||
{
|
||||
Util.GotoUrl(this, Resource.String.SuggestionsURL);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Resource.Id.menu_lock:
|
||||
return true;
|
||||
case Resource.Id.menu_translate:
|
||||
try
|
||||
{
|
||||
Util.GotoUrl(this, Resource.String.TranslationURL);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Android.Resource.Id.Home:
|
||||
//Currently the action bar only displays the home button when we come from a previous activity.
|
||||
//So we can simply Finish. See this page for information on how to do this in more general (future?) cases:
|
||||
//http://developer.android.com/training/implementing-navigation/ancestral.html
|
||||
Finish();
|
||||
OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// brings up a dialog asking the user whether he wants to add the given URL to the entry for automatic finding
|
||||
/// </summary>
|
||||
@ -856,6 +1036,8 @@ namespace keepass2android
|
||||
builder.SetPositiveButton(GetString(Resource.String.yes), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
|
||||
AddUrlToEntryThenCompleteCreate(url);
|
||||
|
||||
});
|
||||
|
||||
builder.SetNegativeButton(GetString(Resource.String.no), (dlgSender, dlgEvt) =>
|
||||
@ -867,7 +1049,47 @@ namespace keepass2android
|
||||
dialog.Show();
|
||||
|
||||
}
|
||||
private void AddUrlToEntryThenCompleteCreate(string url)
|
||||
{
|
||||
PwEntry initialEntry = Entry.CloneDeep();
|
||||
|
||||
PwEntry newEntry = Entry;
|
||||
newEntry.History = newEntry.History.CloneDeep();
|
||||
newEntry.CreateBackup(null);
|
||||
|
||||
newEntry.Touch(true, false); // Touch *after* backup
|
||||
|
||||
//if there is no URL in the entry, set that field. If it's already in use, use an additional (not existing) field
|
||||
if (String.IsNullOrEmpty(newEntry.Strings.ReadSafe(PwDefs.UrlField)))
|
||||
{
|
||||
newEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, url));
|
||||
}
|
||||
else
|
||||
{
|
||||
int c = 1;
|
||||
while (newEntry.Strings.Get("KP2A_URL_" + c) != null)
|
||||
{
|
||||
c++;
|
||||
}
|
||||
|
||||
newEntry.Strings.Set("KP2A_URL_" + c, new ProtectedString(false, url));
|
||||
}
|
||||
|
||||
//save the entry:
|
||||
|
||||
ActionOnFinish closeOrShowError = new ActionOnFinish((success, message) =>
|
||||
{
|
||||
OnFinish.DisplayMessage(this, message);
|
||||
CompleteOnCreate();
|
||||
});
|
||||
|
||||
|
||||
RunnableOnFinish runnable = new UpdateEntry(this, App.Kp2a, initialEntry, newEntry, closeOrShowError);
|
||||
|
||||
ProgressTask pt = new ProgressTask(App.Kp2a, this, runnable);
|
||||
pt.Run();
|
||||
|
||||
}
|
||||
public void ToggleVisibility()
|
||||
{
|
||||
_showPassword = !_showPassword;
|
||||
@ -875,31 +1097,32 @@ namespace keepass2android
|
||||
UpdateTogglePasswordMenu();
|
||||
}
|
||||
|
||||
public Android.Net.Uri WriteBinaryToFile(string key, bool writeToCacheDirectory)
|
||||
{
|
||||
return Android.Net.Uri.Empty;
|
||||
//TODO
|
||||
}
|
||||
|
||||
private void UpdateTogglePasswordMenu()
|
||||
public bool GotoUrl()
|
||||
{
|
||||
//todo use real method
|
||||
}
|
||||
string url = _stringViews[PwDefs.UrlField].Text;
|
||||
if (url == null) return false;
|
||||
|
||||
public void GotoUrl()
|
||||
{
|
||||
//TODO
|
||||
|
||||
}
|
||||
// Default http:// if no protocol specified
|
||||
if (!url.Contains("://"))
|
||||
{
|
||||
url = "http://" + url;
|
||||
}
|
||||
|
||||
public void OpenBinaryFile(Uri newUri)
|
||||
{
|
||||
Toast.MakeText(this, "opening file TODO", ToastLength.Short).Show();
|
||||
try
|
||||
{
|
||||
Util.GotoUrl(this, url);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddEntryToIntent(Intent intent)
|
||||
{
|
||||
PluginHost.AddEntryToIntent(intent, App.Kp2A.GetDb().LastOpenedEntry);
|
||||
PluginHost.AddEntryToIntent(intent, App.Kp2a.GetDb().LastOpenedEntry);
|
||||
}
|
||||
}
|
||||
}
|
7754
src/keepass2android/Resources/Resource.designer.cs
generated
7754
src/keepass2android/Resources/Resource.designer.cs
generated
File diff suppressed because it is too large
Load Diff
@ -1,82 +1,131 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/plugin_scroll"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true"
|
||||
android:scrollbarStyle="insideOverlay">
|
||||
<LinearLayout android:id="@+id/scopes_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp">
|
||||
<ImageView
|
||||
android:id="@+id/imgIcon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:divider="?android:attr/dividerVertical"
|
||||
android:showDividers="middle"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:dividerPadding="12dp"
|
||||
android:baselineAligned="false">
|
||||
<FrameLayout
|
||||
android:id="@+id/accept_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
<TextView
|
||||
android:id="@+id/txtLabel"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/imgIcon"
|
||||
android:textSize="22dp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/txtVersion"
|
||||
android:text="0.9.3"
|
||||
android:textSize="14dp"
|
||||
android:textColor="#888888"
|
||||
android:layout_toRightOf="@id/imgIcon"
|
||||
android:layout_below="@id/txtLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:id="@+id/txtAuthor"
|
||||
android:text=""
|
||||
android:textSize="14dp"
|
||||
android:textColor="#888888"
|
||||
android:layout_below="@id/txtVersion"
|
||||
android:layout_toRightOf="@id/imgIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtShortDesc"
|
||||
android:text=""
|
||||
android:textSize="14dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cb_enabled"
|
||||
style="?android:actionBarTabTextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/plugin_enabled_checkbox"
|
||||
android:checked="true"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scopes_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingRight="20dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableLeft="?attr/NavigationAcceptDrawable"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/accept" />
|
||||
</FrameLayout>
|
||||
<FrameLayout
|
||||
android:id="@+id/deny_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
<TextView
|
||||
style="?android:actionBarTabTextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingRight="20dp"
|
||||
android:drawableLeft="?attr/CancelDrawable"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/deny" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/plugin_scroll"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Scopes"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="18dp"
|
||||
style="@style/ExtraFieldHeader" />
|
||||
android:padding="12dp"
|
||||
android:scrollbarStyle="insideOverlay">
|
||||
<LinearLayout android:id="@+id/scopes_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp">
|
||||
<ImageView
|
||||
android:id="@+id/imgIcon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp" />
|
||||
<TextView
|
||||
android:id="@+id/txtLabel"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/imgIcon"
|
||||
android:textSize="22dp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/txtVersion"
|
||||
android:text="0.9.3"
|
||||
android:textSize="14dp"
|
||||
android:textColor="#888888"
|
||||
android:layout_toRightOf="@id/imgIcon"
|
||||
android:layout_below="@id/txtLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:id="@+id/txtAuthor"
|
||||
android:text=""
|
||||
android:textSize="14dp"
|
||||
android:textColor="#888888"
|
||||
android:layout_below="@id/txtVersion"
|
||||
android:layout_toRightOf="@id/imgIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtShortDesc"
|
||||
android:text=""
|
||||
android:textSize="14dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cb_enabled"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/plugin_enabled_checkbox"
|
||||
android:checked="true"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scopes_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Scopes"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="18dp"
|
||||
style="@style/ExtraFieldHeader" />
|
||||
<LinearLayout android:id="@+id/scopes_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
</RelativeLayout>
|
@ -4,14 +4,15 @@
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
<Button
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:text="plugin text"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/plugin_web"
|
||||
style="@style/PaddedElement"
|
||||
android:id="@+id/textView"
|
||||
android:id="@+id/btnPluginsOnline"
|
||||
android:layout_gravity="left|center_vertical" />
|
||||
<ListView
|
||||
android:id="@android:id/list"
|
||||
|
@ -7,6 +7,7 @@
|
||||
<string name="CreditsText">The User Interface is based on a port of KeepassDroid developed by Brian Pellin. Code for database operations is based on KeePass by Dominik Reichl. The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.</string>
|
||||
<string name="CreditsTextSFTP">SFTP support is implemented using the JSch library under BSD licence, created by JCraft, Inc.</string>
|
||||
<string name="accept">Accept</string>
|
||||
<string name="deny">Deny</string>
|
||||
<string name="add_entry">Add entry</string>
|
||||
<string name="add_url_entry">Create entry for URL</string>
|
||||
<string name="add_group">Add group</string>
|
||||
@ -401,16 +402,14 @@
|
||||
<string name="SavingOtpAuxFile">Saving auxiliary OTP file…</string>
|
||||
|
||||
<string name="loading">Loading…</string>
|
||||
|
||||
<string name="enable_plugin_title">Enable plug-in?</string>
|
||||
<string name="enable_plugin_question">Keepass2Android detected a plug-in with name: %1$s from package name: %2$s. Do you want to enable this plug-in? It will have access to the data stored in your Keepass databases.</string>
|
||||
|
||||
|
||||
<string name="plugins">Plug-ins</string>
|
||||
<string name="plugin_packagename">Package name:</string>
|
||||
<string name="plugin_description">Description (not verified):</string>
|
||||
<string name="plugin_author">Author (not verified):</string>
|
||||
<string name="plugin_enabled">enabled</string>
|
||||
<string name="plugin_disabled">disabled</string>
|
||||
<string name="plugin_web">Find plug-ins online</string>
|
||||
|
||||
<string name="plugin_enabled_checkbox">Enabled</string>
|
||||
|
||||
|
@ -63,9 +63,6 @@
|
||||
<item name="@android:textSize">12sp</item>
|
||||
<item name="@android:background">#222222</item>
|
||||
</style>
|
||||
<style name="EntryFieldHeader" parent="WhiteOnDarkSmall">
|
||||
<item name="@android:textSize">12sp</item>
|
||||
</style>
|
||||
<style name="ElementTextTitle" parent="WhiteOnBlack">
|
||||
<item name="@android:textColor">@color/group</item>
|
||||
<item name="@android:background">@android:color/transparent</item>
|
||||
|
@ -284,4 +284,8 @@
|
||||
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
<PreferenceScreen android:key="plugin_key" android:title="@string/plugins">
|
||||
<intent android:action="keepass2android.PluginListActivity"/>
|
||||
</PreferenceScreen>
|
||||
</PreferenceScreen>
|
||||
|
@ -99,6 +99,17 @@
|
||||
<Compile Include="app\OtpAuxCacheSupervisor.cs" />
|
||||
<Compile Include="CreateDatabaseActivity.cs" />
|
||||
<Compile Include="CreateNewFilename.cs" />
|
||||
<Compile Include="EntryActivityClasses\CopyToClipboardPopupMenuIcon.cs" />
|
||||
<Compile Include="EntryActivityClasses\ExtraStringView.cs" />
|
||||
<Compile Include="EntryActivityClasses\GotoUrlMenuItem.cs" />
|
||||
<Compile Include="EntryActivityClasses\IPopupMenuItem.cs" />
|
||||
<Compile Include="EntryActivityClasses\IStringView.cs" />
|
||||
<Compile Include="EntryActivityClasses\OpenBinaryPopupItem.cs" />
|
||||
<Compile Include="EntryActivityClasses\PluginMenuOption.cs" />
|
||||
<Compile Include="EntryActivityClasses\PluginPopupMenuItem.cs" />
|
||||
<Compile Include="EntryActivityClasses\StandardStringView.cs" />
|
||||
<Compile Include="EntryActivityClasses\ToggleVisibilityPopupMenuItem.cs" />
|
||||
<Compile Include="EntryActivityClasses\WriteBinaryToFilePopupItem.cs" />
|
||||
<Compile Include="ExportDatabaseActivity.cs" />
|
||||
<Compile Include="fileselect\FileChooserFileProvider.cs" />
|
||||
<Compile Include="fileselect\FileStorageSetupActivity.cs" />
|
||||
@ -649,10 +660,6 @@
|
||||
<Project>{3c0f7fe5-639f-4422-a087-8b26cf862d1b}</Project>
|
||||
<Name>AndroidFileChooserBinding</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\AppCompatV7Binding\AppCompatV7Binding.csproj">
|
||||
<Project>{23233a28-d74f-4bf8-b4d8-834060840bd7}</Project>
|
||||
<Name>AppCompatV7Binding</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
||||
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
|
||||
<Name>JavaFileStorageBindings</Name>
|
||||
@ -956,4 +963,7 @@
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\vdots.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\ic_menu_copy_holo_light.png" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -87,21 +87,12 @@ namespace keepass2android
|
||||
|
||||
public void StorePlugin(string pluginPackage, string accessToken, IList<string> requestedScopes)
|
||||
{
|
||||
ISharedPreferences hostPrefs = GetHostPrefs();
|
||||
ISharedPreferences pluginPrefs = GetPreferencesForPlugin(pluginPackage);
|
||||
var stringSet = hostPrefs.GetStringSet(_pluginlist, new Collection<string>());
|
||||
if (!stringSet.Contains(pluginPackage))
|
||||
{
|
||||
stringSet.Add(pluginPackage);
|
||||
hostPrefs.Edit()
|
||||
.PutStringSet(_pluginlist, stringSet)
|
||||
.Commit();
|
||||
}
|
||||
|
||||
pluginPrefs.Edit()
|
||||
.PutString(_scopes, AccessManager.StringArrayToString(requestedScopes))
|
||||
.PutString(_accessToken, accessToken)
|
||||
.Commit();
|
||||
.PutString(_scopes, AccessManager.StringArrayToString(requestedScopes))
|
||||
.PutString(_accessToken, accessToken)
|
||||
.Commit();
|
||||
}
|
||||
|
||||
private ISharedPreferences GetHostPrefs()
|
||||
@ -123,7 +114,7 @@ namespace keepass2android
|
||||
i.PutExtra(Strings.ExtraAccessToken, accessToken);
|
||||
_ctx.SendBroadcast(i);
|
||||
|
||||
StorePlugin(pluginPackage, accessToken, GetPluginScopes( pluginPackage));
|
||||
StorePlugin(pluginPackage, accessToken, GetPluginScopes(pluginPackage));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -148,7 +139,7 @@ namespace keepass2android
|
||||
{
|
||||
Log.Warn(_tag, "No accessToken specified!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var prefs = GetPreferencesForPlugin(pluginPackage);
|
||||
if (prefs.GetString(_accessToken, null) != accessToken)
|
||||
@ -178,16 +169,16 @@ namespace keepass2android
|
||||
GetHostPrefs().Edit().Clear().Commit();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public IEnumerable<string> GetPluginsWithAcceptedScope(string scope)
|
||||
{
|
||||
return GetAllPluginPackages().Where(plugin =>
|
||||
{
|
||||
var prefs = GetPreferencesForPlugin(plugin);
|
||||
return (prefs.GetString(_accessToken, null) != null)
|
||||
&& AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(scope);
|
||||
{
|
||||
var prefs = GetPreferencesForPlugin(plugin);
|
||||
return (prefs.GetString(_accessToken, null) != null)
|
||||
&& AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(scope);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearPlugin(string plugin)
|
||||
|
@ -20,36 +20,28 @@ using keepass2android.views;
|
||||
|
||||
namespace PluginHostTest
|
||||
{
|
||||
[Activity(Label = "@string/app_name", Theme = "@style/Base")]
|
||||
[Activity(Label = AppNames.AppName)]
|
||||
[IntentFilter(new[] { Strings.ActionEditPluginSettings },
|
||||
Label = AppNames.AppName,
|
||||
Categories = new[] { Intent.CategoryDefault })]
|
||||
public class PluginDetailsActivity : Activity
|
||||
{
|
||||
private CheckBox _checkbox;
|
||||
private string _pluginPackageName;
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
new ActivityDesign(this).ApplyTheme();
|
||||
|
||||
string pluginPackage = Intent.GetStringExtra("PluginPackage");
|
||||
_pluginPackageName = Intent.GetStringExtra(Strings.ExtraPluginPackage);
|
||||
|
||||
Intent mainIntent = new Intent(Intent.ActionMain, null);
|
||||
mainIntent.AddCategory(Intent.CategoryLauncher);
|
||||
var pluginRes = PackageManager.GetResourcesForApplication(_pluginPackageName);
|
||||
var title = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_title");
|
||||
var author = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_author");
|
||||
var shortDesc = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_shortdesc");
|
||||
var version = PackageManager.GetPackageInfo(_pluginPackageName, 0).VersionName;
|
||||
|
||||
IList<ResolveInfo> appList = PackageManager.QueryIntentActivities(mainIntent, 0);
|
||||
//Collections.Sort(appList, new ResolveInfo.DisplayNameComparator(PackageManager));
|
||||
|
||||
foreach (ResolveInfo temp in appList)
|
||||
{
|
||||
|
||||
Log.Verbose("my logs", "package and activity name = "
|
||||
+ temp.ActivityInfo.PackageName + " "
|
||||
+ temp.ActivityInfo.Name + " " + temp.ActivityInfo.IconResource);
|
||||
|
||||
}
|
||||
var pluginRes = PackageManager.GetResourcesForApplication(pluginPackage);
|
||||
var title = GetStringFromPlugin(pluginRes, pluginPackage, "kp2aplugin_title");
|
||||
var author = GetStringFromPlugin(pluginRes, pluginPackage, "kp2aplugin_author");
|
||||
var shortDesc = GetStringFromPlugin(pluginRes, pluginPackage, "kp2aplugin_shortdesc");
|
||||
|
||||
var version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName;
|
||||
|
||||
SetContentView(Resource.Layout.plugin_details);
|
||||
if (title != null)
|
||||
FindViewById<TextView>(Resource.Id.txtLabel).Text = title;
|
||||
@ -57,32 +49,86 @@ namespace PluginHostTest
|
||||
SetTextOrHide(Resource.Id.txtAuthor, author);
|
||||
SetTextOrHide(Resource.Id.txtShortDesc, shortDesc);
|
||||
|
||||
var checkbox = FindViewById<CheckBox>(Resource.Id.cb_enabled);
|
||||
PluginDatabase pluginDb = new PluginDatabase(this);
|
||||
checkbox.Checked = pluginDb.IsEnabled(pluginPackage);
|
||||
checkbox.CheckedChange += delegate(object sender, CompoundButton.CheckedChangeEventArgs args)
|
||||
{
|
||||
pluginDb.SetEnabled(pluginPackage, checkbox.Checked);
|
||||
};
|
||||
|
||||
Drawable d = PackageManager.GetApplicationIcon(pluginPackage);
|
||||
_checkbox = FindViewById<CheckBox>(Resource.Id.cb_enabled);
|
||||
_checkbox.CheckedChange += delegate
|
||||
{
|
||||
new PluginDatabase(this).SetEnabled(_pluginPackageName, _checkbox.Checked);
|
||||
};
|
||||
|
||||
Drawable d = PackageManager.GetApplicationIcon(_pluginPackageName);
|
||||
FindViewById<ImageView>(Resource.Id.imgIcon).SetImageDrawable(d);
|
||||
|
||||
|
||||
FindViewById<TextView>(Resource.Id.txtVersion).Text = version;
|
||||
|
||||
var scopesContainer = FindViewById<LinearLayout>(Resource.Id.scopes_list);
|
||||
//cannot be wrong to update the view when we received an update
|
||||
PluginHost.OnReceivedRequest += OnPluginHostOnOnReceivedRequest;
|
||||
|
||||
foreach (string scope in pluginDb.GetPluginScopes(pluginPackage))
|
||||
if (Intent.Action == Strings.ActionEditPluginSettings)
|
||||
{
|
||||
//this action can be triggered by external apps so we don't know if anybody has ever triggered
|
||||
//the plugin to request access. Do this now, don't set the view right now
|
||||
PluginHost.TriggerRequest(this, _pluginPackageName, new PluginDatabase(this));
|
||||
//show the buttons instead of the checkbox
|
||||
_checkbox.Visibility = ViewStates.Invisible;
|
||||
FindViewById(Resource.Id.accept_button).Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.deny_button).Visibility = ViewStates.Visible;
|
||||
|
||||
FindViewById(Resource.Id.accept_button).Click += delegate(object sender, EventArgs args)
|
||||
{
|
||||
new PluginDatabase(this).SetEnabled(_pluginPackageName, true);
|
||||
SetResult(Result.Ok);
|
||||
Finish();
|
||||
};
|
||||
|
||||
FindViewById(Resource.Id.deny_button).Click += delegate(object sender, EventArgs args)
|
||||
{
|
||||
new PluginDatabase(this).SetEnabled(_pluginPackageName, false);
|
||||
SetResult(Result.Canceled);
|
||||
Finish();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateView();
|
||||
_checkbox.Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.accept_button).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.deny_button).Visibility = ViewStates.Gone;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPluginHostOnOnReceivedRequest(object sender, PluginHost.PluginHostEventArgs args)
|
||||
{
|
||||
if (args.Package == _pluginPackageName)
|
||||
{
|
||||
UpdateView();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
PluginHost.OnReceivedRequest -= OnPluginHostOnOnReceivedRequest;
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private void UpdateView()
|
||||
{
|
||||
var scopesContainer = FindViewById<LinearLayout>(Resource.Id.scopes_list);
|
||||
//scopesContainer.RemoveAllViews();
|
||||
|
||||
var pluginDb = new PluginDatabase(this);
|
||||
_checkbox.Checked = pluginDb.IsEnabled(_pluginPackageName);
|
||||
foreach (string scope in pluginDb.GetPluginScopes(_pluginPackageName))
|
||||
{
|
||||
string scopeId = scope.Substring("keepass2android.".Length);
|
||||
|
||||
TextWithHelp help = new TextWithHelp(this, GetString(Resources.GetIdentifier(scopeId + "_title", "string", PackageName)), GetString(Resources.GetIdentifier(scopeId + "_explanation", "string", PackageName)));
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
|
||||
TextWithHelp help = new TextWithHelp(this,
|
||||
GetString(Resources.GetIdentifier(scopeId + "_title", "string", PackageName)),
|
||||
GetString(Resources.GetIdentifier(scopeId + "_explanation", "string", PackageName)));
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent,
|
||||
ViewGroup.LayoutParams.WrapContent);
|
||||
help.LayoutParameters = layoutParams;
|
||||
scopesContainer.AddView(help);
|
||||
scopesContainer.AddView(help);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void SetTextOrHide(int resourceId, string text)
|
||||
@ -98,7 +144,7 @@ namespace PluginHostTest
|
||||
|
||||
public static string GetStringFromPlugin(Resources pluginRes, string pluginPackage, string stringId)
|
||||
{
|
||||
int titleId = pluginRes.GetIdentifier(pluginPackage + ":string/"+stringId, null, null);
|
||||
int titleId = pluginRes.GetIdentifier(pluginPackage + ":string/" + stringId, null, null);
|
||||
string title = null;
|
||||
if (titleId != 0)
|
||||
title = pluginRes.GetString(titleId);
|
||||
|
@ -11,16 +11,22 @@ using Org.Json;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[BroadcastReceiver()]
|
||||
[IntentFilter(new[] { Strings.ActionRequestAccess})]
|
||||
public class PluginHost: BroadcastReceiver
|
||||
/// <summary>
|
||||
/// Class which manages plugins inside the app
|
||||
/// </summary>
|
||||
[BroadcastReceiver]
|
||||
[IntentFilter(new[] { Strings.ActionRequestAccess })]
|
||||
public class PluginHost : BroadcastReceiver
|
||||
{
|
||||
|
||||
|
||||
private const string _tag = "KP2A_PluginHost";
|
||||
|
||||
|
||||
|
||||
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions, Strings.ScopeCurrentEntry };
|
||||
|
||||
/// <summary>
|
||||
/// Sends a broadcast to all potential plugins prompting them to request access to our app.
|
||||
/// </summary>
|
||||
public static void TriggerRequests(Context ctx)
|
||||
{
|
||||
Intent accessIntent = new Intent(Strings.ActionTriggerRequestAccess);
|
||||
@ -32,28 +38,27 @@ namespace keepass2android
|
||||
{
|
||||
ApplicationInfo appInfo = ri.ActivityInfo.ApplicationInfo;
|
||||
String pkgName = appInfo.PackageName;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
Intent triggerIntent = new Intent(Strings.ActionTriggerRequestAccess);
|
||||
triggerIntent.SetPackage(pkgName);
|
||||
triggerIntent.PutExtra(Strings.ExtraSender, ctx.PackageName);
|
||||
|
||||
triggerIntent.PutExtra(Strings.ExtraRequestToken, pluginDatabase.GetRequestToken(pkgName));
|
||||
ctx.SendBroadcast(triggerIntent);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
TriggerRequest(ctx, pkgName, pluginDatabase);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void TriggerRequest(Context ctx, string pkgName, PluginDatabase pluginDatabase)
|
||||
{
|
||||
try
|
||||
{
|
||||
Intent triggerIntent = new Intent(Strings.ActionTriggerRequestAccess);
|
||||
triggerIntent.SetPackage(pkgName);
|
||||
triggerIntent.PutExtra(Strings.ExtraSender, ctx.PackageName);
|
||||
|
||||
triggerIntent.PutExtra(Strings.ExtraRequestToken, pluginDatabase.GetRequestToken(pkgName));
|
||||
ctx.SendBroadcast(triggerIntent);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void OnReceive(Context context, Intent intent)
|
||||
@ -64,7 +69,7 @@ namespace keepass2android
|
||||
var senderPackage = intent.GetStringExtra(Strings.ExtraSender);
|
||||
var requestToken = intent.GetStringExtra(Strings.ExtraRequestToken);
|
||||
|
||||
var requestedScopes = intent.GetStringArrayListExtra(Strings.ExtraScopes);
|
||||
var requestedScopes = intent.GetStringArrayListExtra(Strings.ExtraScopes);
|
||||
|
||||
if (!AreScopesValid(requestedScopes))
|
||||
{
|
||||
@ -77,9 +82,9 @@ namespace keepass2android
|
||||
return;
|
||||
}
|
||||
string currentAccessToken = pluginDb.GetAccessToken(senderPackage);
|
||||
if ((currentAccessToken != null)
|
||||
if ((currentAccessToken != null)
|
||||
&& (AccessManager.IsSubset(requestedScopes,
|
||||
pluginDb.GetPluginScopes(senderPackage))))
|
||||
pluginDb.GetPluginScopes(senderPackage))))
|
||||
{
|
||||
//permission already there.
|
||||
var i = new Intent(Strings.ActionReceiveAccess);
|
||||
@ -111,9 +116,10 @@ namespace keepass2android
|
||||
context.SendBroadcast(i);
|
||||
Log.Warn(_tag, "Access token of plugin " + senderPackage + " not (or no more) valid.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (OnReceivedRequest != null)
|
||||
OnReceivedRequest(this, new PluginHostEventArgs() { Package = senderPackage });
|
||||
|
||||
}
|
||||
}
|
||||
@ -131,6 +137,9 @@ namespace keepass2android
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// adds the entry output data to the intent to be sent to a plugin
|
||||
/// </summary>
|
||||
public static void AddEntryToIntent(Intent intent, PwEntryOutput entry)
|
||||
{
|
||||
/*//add the entry XML
|
||||
@ -150,5 +159,11 @@ namespace keepass2android
|
||||
intent.PutExtra(Strings.ExtraEntryId, entry.Uuid.ToHexString());
|
||||
|
||||
}
|
||||
|
||||
public class PluginHostEventArgs
|
||||
{
|
||||
public string Package { get; set; }
|
||||
}
|
||||
public static event EventHandler<PluginHostEventArgs> OnReceivedRequest;
|
||||
}
|
||||
}
|
@ -5,11 +5,13 @@ using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity(Label = "@string/app_name", Theme = "@style/Base")]
|
||||
[Activity(Label = "@string/plugins", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden)]
|
||||
[IntentFilter(new[] { "keepass2android.PluginListActivity" }, Categories = new[] { Intent.CategoryDefault })]
|
||||
public class PluginListActivity : ListActivity
|
||||
{
|
||||
private PluginArrayAdapter _pluginArrayAdapter;
|
||||
@ -18,21 +20,27 @@ namespace keepass2android
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
//TODO _design.ApplyTheme();
|
||||
|
||||
new ActivityDesign(this).ApplyTheme();
|
||||
|
||||
SetContentView(Resource.Layout.plugin_list);
|
||||
|
||||
|
||||
PluginHost.TriggerRequests(this);
|
||||
|
||||
ListView listView = FindViewById<ListView>(Android.Resource.Id.List);
|
||||
listView.ItemClick +=
|
||||
(sender, args) =>
|
||||
{
|
||||
Intent i = new Intent(this, typeof(PluginDetailsActivity));
|
||||
i.PutExtra("PluginPackage", _items[args.Position].Package);
|
||||
StartActivity(i);
|
||||
};
|
||||
|
||||
// Create your application here
|
||||
{
|
||||
Intent i = new Intent(this, typeof(PluginDetailsActivity));
|
||||
i.PutExtra(Strings.ExtraPluginPackage, _items[args.Position].Package);
|
||||
StartActivity(i);
|
||||
};
|
||||
|
||||
FindViewById<Button>(Resource.Id.btnPluginsOnline).Click += delegate
|
||||
{
|
||||
Util.GotoUrl(this, "https://keepass2android.codeplex.com/wikipage?title=Available%20Plug-ins");
|
||||
};
|
||||
|
||||
}
|
||||
protected override void OnResume()
|
||||
{
|
||||
@ -40,9 +48,9 @@ namespace keepass2android
|
||||
PluginDatabase pluginDb = new PluginDatabase(this);
|
||||
|
||||
_items = (from pluginPackage in pluginDb.GetAllPluginPackages()
|
||||
let version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName
|
||||
let enabledStatus = pluginDb.IsEnabled(pluginPackage) ? GetString(Resource.String.plugin_enabled) : GetString(Resource.String.plugin_disabled)
|
||||
select new PluginItem(pluginPackage, enabledStatus, this)).ToList();
|
||||
let version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName
|
||||
let enabledStatus = pluginDb.IsEnabled(pluginPackage) ? GetString(Resource.String.plugin_enabled) : GetString(Resource.String.plugin_disabled)
|
||||
select new PluginItem(pluginPackage, enabledStatus, this)).ToList();
|
||||
/*
|
||||
{
|
||||
new PluginItem("PluginA", Resource.Drawable.Icon, "keepass2android.plugina", "connected"),
|
||||
|
@ -49,7 +49,7 @@ namespace keepass2android
|
||||
public const int NotifyKeyboard = 3;
|
||||
public const int ClearClipboard = 4;
|
||||
|
||||
public void CopyValueToClipboardWithTimeout(Context ctx, string text)
|
||||
static public void CopyValueToClipboardWithTimeout(Context ctx, string text)
|
||||
{
|
||||
Intent i = new Intent(ctx, typeof(CopyToClipboardService));
|
||||
i.SetAction(Intents.CopyStringToClipboard);
|
||||
|
Loading…
Reference in New Issue
Block a user