refactoring regarding AppTasks:

- AppTasks are now returned by ActivityResult through all AppTask-Related activities (includes ForwardResult)
 - AppTasks are now passed correctly even when using search (this fixes a problem that AppTasks like SearchUrl were not passed to EntryActivity so the App didn't return to the browser automatically)
 - AppTasks are deleted by ActivityResult or by checking for LaunchedFromHistory

Added option to leave app with db unlocked (this is even the default now!)

Added missing EntryActivity files
This commit is contained in:
Philipp Crocoll 2014-05-16 17:15:43 +02:00
parent f613206dab
commit bcbc225652
29 changed files with 1762 additions and 1069 deletions

View File

@ -0,0 +1,60 @@
using System;
using KeePass.Util.Spr;
using KeePassLib;
using KeePassLib.Collections;
using KeePassLib.Security;
namespace keepass2android
{
/// <summary>
/// Represents the strings which are output from a PwEntry.
/// </summary>
/// In contrast to the original PwEntry, this means that placeholders are replaced. Also, plugins may modify
/// or add fields.
public class PwEntryOutput
{
private readonly PwEntry _entry;
private readonly PwDatabase _db;
private readonly ProtectedStringDictionary _outputStrings = new ProtectedStringDictionary();
/// <summary>
/// Constructs the PwEntryOutput by replacing the placeholders
/// </summary>
public PwEntryOutput(PwEntry entry, PwDatabase db)
{
_entry = entry;
_db = db;
foreach (var pair in entry.Strings)
{
_outputStrings.Set(pair.Key, new ProtectedString(entry.Strings.Get(pair.Key).IsProtected, GetStringAndReplacePlaceholders(pair.Key)));
}
}
string GetStringAndReplacePlaceholders(string key)
{
String value = Entry.Strings.ReadSafe(key);
value = SprEngine.Compile(value, new SprContext(Entry, _db, SprCompileFlags.All));
return value;
}
/// <summary>
/// Returns the ID of the entry
/// </summary>
public PwUuid Uuid
{
get { return Entry.Uuid; }
}
/// <summary>
/// The output strings for the represented entry
/// </summary>
public ProtectedStringDictionary OutputStrings { get { return _outputStrings; } }
public PwEntry Entry
{
get { return _entry; }
}
}
}

View File

@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Java.IO;
using KeePassLib;
using KeePassLib.Interfaces;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using keepass2android;
namespace Kp2aUnitTests
{
[TestClass]
internal class TestIntentsAndBundles
{
[TestMethod]
public void StringArray()
{
string[] dataIn = new string[] { "a","bcd"};
Intent i= new Intent();
i.PutExtra("key", dataIn);
Bundle extras = i.Extras;
var dataOut = extras.GetStringArray("key");
Assert.AreEqual(dataIn.Length, dataOut.Length);
Assert.AreEqual(dataIn[0], dataOut[0]);
Assert.AreEqual(dataIn[1], dataOut[1]);
}
}
}

View File

@ -38,7 +38,7 @@ namespace keepass2android
ctx.GetString(Resource.String.ChangeLog)
};
builder.SetPositiveButton(Android.Resource.String.Ok, (dlgSender, dlgEvt) => { });
builder.SetPositiveButton(Android.Resource.String.Ok, (dlgSender, dlgEvt) => {((AlertDialog)dlgSender).Dismiss(); });
builder.SetCancelable(false);
builder.SetMessage("temp");

View File

@ -52,17 +52,21 @@ namespace keepass2android
public const String KeyRefreshPos = "refresh_pos";
public const String KeyCloseAfterCreate = "close_after_create";
public static void Launch(Activity act, PwEntry pw, int pos, AppTask appTask)
public static void Launch(Activity act, PwEntry pw, int pos, AppTask appTask, ActivityFlags? flags = null)
{
Intent i = new Intent(act, typeof(EntryActivity));
i.PutExtra(KeyEntry, pw.Uuid.ToHexString());
i.PutExtra(KeyRefreshPos, pos);
appTask.ToIntent(i);
if (flags != null)
i.SetFlags((ActivityFlags) flags);
act.StartActivityForResult(i, 0);
appTask.ToIntent(i);
if (flags != null && (((ActivityFlags) flags) | ActivityFlags.ForwardResult) == ActivityFlags.ForwardResult)
act.StartActivity(i);
else
act.StartActivityForResult(i, 0);
}
public EntryActivity (IntPtr javaReference, JniHandleOwnership transfer)
@ -93,6 +97,9 @@ namespace keepass2android
private readonly Dictionary<string, IStringView> _stringViews = new Dictionary<string, IStringView>();
private readonly List<PluginMenuOption> _pendingMenuOptions = new List<PluginMenuOption>();
//make sure _timer doesn't go out of scope:
private Timer _timer;
protected void SetEntryView()
{
@ -298,6 +305,7 @@ namespace keepass2android
!prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));
base.OnCreate(savedInstanceState);
RequestWindowFeature(WindowFeatures.IndeterminateProgress);
new ActivityDesign(this).ApplyTheme();
@ -342,15 +350,15 @@ namespace keepass2android
SetupEditButtons();
//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));
new Thread(NotifyPluginsOnOpen).Start();
//the rest of the things to do depends on the current app task:
_appTask.CompleteOnCreateEntryActivity(this);
}
private void NotifyPluginsOnOpen()
@ -381,29 +389,17 @@ namespace keepass2android
}
}
public void CompleteOnCreate()
{
Intent showNotIntent = new Intent(this, typeof(CopyToClipboardService));
internal void StartNotificationsService(bool closeAfterCreate)
{
Intent showNotIntent = new Intent(this, typeof (CopyToClipboardService));
showNotIntent.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();
}
}
@ -805,12 +801,24 @@ namespace keepass2android
{
Intent ret = new Intent();
ret.PutExtra(KeyRefreshPos, _pos);
_appTask.ToIntent(ret);
SetResult(KeePass.ExitRefresh, ret);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) {
base.OnActivityResult(requestCode, resultCode, data);
if (AppTask.TryGetFromActivityResult(data, ref _appTask))
{
//make sure app task is passed to calling activity.
//the result code might be modified later.
Intent retData = new Intent();
_appTask.ToIntent(retData);
SetResult(KeePass.ExitNormal, retData);
}
if ( resultCode == KeePass.ExitRefresh || resultCode == KeePass.ExitRefreshTitle ) {
if ( resultCode == KeePass.ExitRefreshTitle ) {
RequiresRefresh ();
@ -1019,33 +1027,8 @@ namespace keepass2android
}
/// <summary>
/// brings up a dialog asking the user whether he wants to add the given URL to the entry for automatic finding
/// </summary>
public void AskAddUrlThenCompleteCreate(string url)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetTitle(GetString(Resource.String.AddUrlToEntryDialog_title));
builder.SetMessage(GetString(Resource.String.AddUrlToEntryDialog_text, new Java.Lang.Object[] {url}));
builder.SetPositiveButton(GetString(Resource.String.yes), (dlgSender, dlgEvt) =>
{
AddUrlToEntryThenCompleteCreate(url);
});
builder.SetNegativeButton(GetString(Resource.String.no), (dlgSender, dlgEvt) =>
{
CompleteOnCreate();
});
Dialog dialog = builder.Create();
dialog.Show();
}
private void AddUrlToEntryThenCompleteCreate(string url)
internal void AddUrlToEntry(string url, Action finishAction)
{
PwEntry initialEntry = Entry.CloneDeep();
@ -1076,7 +1059,7 @@ namespace keepass2android
ActionOnFinish closeOrShowError = new ActionOnFinish((success, message) =>
{
OnFinish.DisplayMessage(this, message);
CompleteOnCreate();
finishAction();
});
@ -1085,7 +1068,7 @@ namespace keepass2android
ProgressTask pt = new ProgressTask(App.Kp2a, this, runnable);
pt.Run();
}
}
public void ToggleVisibility()
{
_showPassword = !_showPassword;
@ -1120,5 +1103,27 @@ namespace keepass2android
{
PluginHost.AddEntryToIntent(intent, App.Kp2a.GetDb().LastOpenedEntry);
}
public void CloseAfterTaskComplete()
{
//before closing, wait a little to get plugin updates
int numPlugins = new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry).Count();
var timeToWait = TimeSpan.FromMilliseconds(500*numPlugins);
SetProgressBarIndeterminateVisibility(true);
_timer = new Timer(obj =>
{
RunOnUiThread(() =>
{
//task is completed -> return NullTask
Intent resIntent = new Intent();
new NullTask().ToIntent(resIntent);
SetResult(KeePass.ExitCloseAfterTaskComplete, resIntent);
//close activity:
Finish();
}
);
},
null, timeToWait, TimeSpan.FromMilliseconds(-1));
}
}
}

View File

@ -0,0 +1,41 @@
using Android.Content;
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to copy a string to clipboard
/// </summary>
class CopyToClipboardPopupMenuIcon : IPopupMenuItem
{
private readonly Context _context;
private readonly IStringView _stringView;
public CopyToClipboardPopupMenuIcon(Context context, IStringView stringView)
{
_context = context;
_stringView = stringView;
}
public Drawable Icon
{
get
{
return _context.Resources.GetDrawable(Resource.Drawable.ic_menu_copy_holo_light);
}
}
public string Text
{
//TODO localize
get { return "Copy to clipboard"; }
}
public void HandleClick()
{
CopyToClipboardService.CopyValueToClipboardWithTimeout(_context, _stringView.Text);
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using Android.Views;
using Android.Widget;
namespace keepass2android
{
internal class ExtraStringView : IStringView
{
private readonly View _container;
private readonly TextView _valueView;
private readonly TextView _keyView;
public ExtraStringView(LinearLayout container, TextView valueView, TextView keyView)
{
_container = container;
_valueView = valueView;
_keyView = keyView;
}
public View View
{
get { return _container; }
}
public string Text
{
get { return _valueView.Text; }
set
{
if (String.IsNullOrEmpty(value))
{
_valueView.Visibility = ViewStates.Gone;
_keyView.Visibility = ViewStates.Gone;
}
else
{
_valueView.Visibility = ViewStates.Visible;
_keyView.Visibility = ViewStates.Visible;
_valueView.Text = value;
}
}
}
}
}

View File

@ -0,0 +1,33 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to go to the URL in the field
/// </summary>
class GotoUrlMenuItem : IPopupMenuItem
{
private readonly EntryActivity _ctx;
public GotoUrlMenuItem(EntryActivity ctx)
{
_ctx = ctx;
}
public Drawable Icon
{
get { return _ctx.Resources.GetDrawable(Android.Resource.Drawable.IcMenuUpload); }
}
public string Text
{
get { return _ctx.Resources.GetString(Resource.String.menu_url); }
}
public void HandleClick()
{
_ctx.GotoUrl();
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using Android.Graphics.Drawables;
using KeePassLib;
namespace keepass2android
{
/// <summary>
/// Interface for popup menu items in EntryActivity
/// </summary>
internal interface IPopupMenuItem
{
Drawable Icon { get; }
String Text { get; }
void HandleClick();
}
}

View File

@ -0,0 +1,7 @@
namespace keepass2android
{
internal interface IStringView
{
string Text { set; get; }
}
}

View File

@ -0,0 +1,40 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Represents the popup menu item in EntryActivity to open the associated attachment
/// </summary>
internal class OpenBinaryPopupItem : IPopupMenuItem
{
private readonly string _key;
private readonly EntryActivity _entryActivity;
public OpenBinaryPopupItem(string key, EntryActivity entryActivity)
{
_key = key;
_entryActivity = entryActivity;
}
public Drawable Icon
{
get { return _entryActivity.Resources.GetDrawable(Android.Resource.Drawable.IcMenuShare); }
}
public string Text
{
get { return _entryActivity.Resources.GetString(Resource.String.SaveAttachmentDialog_open); }
}
public void HandleClick()
{
Android.Net.Uri newUri = _entryActivity.WriteBinaryToFile(_key, true);
if (newUri != null)
{
_entryActivity.OpenBinaryFile(newUri);
}
}
}
}

View File

@ -0,0 +1,14 @@
using Android.Content;
using Android.Graphics.Drawables;
namespace keepass2android
{
class PluginMenuOption
{
public string DisplayText { get; set; }
public Drawable Icon { get; set; }
public Intent Intent { get; set; }
}
}

View File

@ -0,0 +1,59 @@
using Android.Content;
using Android.Graphics.Drawables;
using Android.OS;
using Keepass2android.Pluginsdk;
namespace keepass2android
{
/// <summary>
/// Represents a popup menu item in EntryActivity which was added by a plugin. The click will therefore broadcast to the plugin.
/// </summary>
class PluginPopupMenuItem : IPopupMenuItem
{
private readonly EntryActivity _activity;
private readonly string _pluginPackage;
private readonly string _fieldId;
private readonly string _popupItemId;
private readonly string _displayText;
private readonly int _iconId;
private readonly Bundle _bundleExtra;
public PluginPopupMenuItem(EntryActivity activity, string pluginPackage, string fieldId, string popupItemId, string displayText, int iconId, Bundle bundleExtra)
{
_activity = activity;
_pluginPackage = pluginPackage;
_fieldId = fieldId;
_popupItemId = popupItemId;
_displayText = displayText;
_iconId = iconId;
_bundleExtra = bundleExtra;
}
public Drawable Icon
{
get { return _activity.PackageManager.GetResourcesForApplication(_pluginPackage).GetDrawable(_iconId); }
}
public string Text
{
get { return _displayText; }
}
public string PopupItemId
{
get { return _popupItemId; }
}
public void HandleClick()
{
Intent i = new Intent(Strings.ActionEntryActionSelected);
i.SetPackage(_pluginPackage);
i.PutExtra(Strings.ExtraActionData, _bundleExtra);
i.PutExtra(Strings.ExtraFieldId, _fieldId);
i.PutExtra(Strings.ExtraSender, _activity.PackageName);
_activity.AddEntryToIntent(i);
_activity.SendBroadcast(i);
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using Android.App;
using Android.Views;
using Android.Widget;
namespace keepass2android
{
internal class StandardStringView : IStringView
{
private readonly int _viewId;
private readonly int _containerViewId;
private readonly Activity _activity;
public StandardStringView(int viewId, int containerViewId, Activity activity)
{
_viewId = viewId;
_containerViewId = containerViewId;
_activity = activity;
}
public string Text
{
set
{
View container = _activity.FindViewById(_containerViewId);
TextView tv = (TextView) _activity.FindViewById(_viewId);
if (String.IsNullOrEmpty(value))
{
container.Visibility = tv.Visibility = ViewStates.Gone;
}
else
{
container.Visibility = tv.Visibility = ViewStates.Visible;
tv.Text = value;
}
}
get
{
TextView tv = (TextView) _activity.FindViewById(_viewId);
return tv.Text;
}
}
}
}

View File

@ -0,0 +1,46 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to toggle visibility of all protected strings (e.g. Password)
/// </summary>
class ToggleVisibilityPopupMenuItem : IPopupMenuItem
{
private readonly EntryActivity _activity;
public ToggleVisibilityPopupMenuItem(EntryActivity activity)
{
_activity = activity;
}
public Drawable Icon
{
get
{
//return new TextDrawable("\uF06E", _activity);
return _activity.Resources.GetDrawable(Resource.Drawable.ic_action_eye_open);
}
}
public string Text
{
get
{
return _activity.Resources.GetString(
_activity._showPassword ?
Resource.String.menu_hide_password
: Resource.String.show_password);
}
}
public void HandleClick()
{
_activity.ToggleVisibility();
}
}
}

View File

@ -0,0 +1,35 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Represents the popup menu item in EntryActivity to store the binary attachment on SD card
/// </summary>
internal class WriteBinaryToFilePopupItem : IPopupMenuItem
{
private readonly string _key;
private readonly EntryActivity _activity;
public WriteBinaryToFilePopupItem(string key, EntryActivity activity)
{
_key = key;
_activity = activity;
}
public Drawable Icon
{
get { return _activity.Resources.GetDrawable(Android.Resource.Drawable.IcMenuSave); }
}
public string Text
{
get { return _activity.Resources.GetString(Resource.String.SaveAttachmentDialog_save); }
}
public void HandleClick()
{
_activity.WriteBinaryToFile(_key, false);
}
}
}

View File

@ -601,7 +601,7 @@ namespace keepass2android
Intent intent = Intent;
intent.PutExtra(IntentContinueWithEditing, true);
OverridePendingTransition(0, 0);
intent.AddFlags(ActivityFlags.NoAnimation);
intent.AddFlags(ActivityFlags.NoAnimation | ActivityFlags.ForwardResult);
_closeForReload = true;
SetResult(KeePass.ExitRefreshTitle); //probably the entry will be modified -> let the EditActivity refresh to be safe
Finish();

View File

@ -70,7 +70,11 @@ namespace keepass2android
{
base.OnActivityResult(requestCode, resultCode, data);
AppTask.TryGetFromActivityResult(data, ref AppTask);
if (AppTask.TryGetFromActivityResult(data, ref AppTask))
{
//make sure the app task is passed to the calling activity
AppTask.SetActivityResult(this, KeePass.ExitNormal);
}
if (resultCode == Result.Ok)
{
@ -236,17 +240,24 @@ namespace keepass2android
class SuggestionListener: Java.Lang.Object, SearchView.IOnSuggestionListener
{
private readonly CursorAdapter _suggestionsAdapter;
private readonly GroupBaseActivity _activity;
private readonly IMenuItem _searchItem;
public SuggestionListener(CursorAdapter suggestionsAdapter)
public SuggestionListener(CursorAdapter suggestionsAdapter, GroupBaseActivity activity, IMenuItem searchItem)
{
_suggestionsAdapter = suggestionsAdapter;
_activity = activity;
_searchItem = searchItem;
}
public bool OnSuggestionClick(int position)
{
var cursor = _suggestionsAdapter.Cursor;
cursor.MoveToPosition(position);
var x = cursor.GetString(cursor.GetColumnIndexOrThrow(SearchManager.SuggestColumnIntentDataId));
string entryIdAsHexString = cursor.GetString(cursor.GetColumnIndexOrThrow(SearchManager.SuggestColumnIntentDataId));
EntryActivity.Launch(_activity, App.Kp2a.GetDb().Entries[new PwUuid(MemUtil.HexStringToByteArray(entryIdAsHexString))],-1,_activity.AppTask);
((SearchView) _searchItem.ActionView).Iconified = true;
return true;
}
@ -255,6 +266,37 @@ namespace keepass2android
return false;
}
}
class OnQueryTextListener: Java.Lang.Object, SearchView.IOnQueryTextListener
{
private readonly GroupBaseActivity _activity;
public OnQueryTextListener(GroupBaseActivity activity)
{
_activity = activity;
}
public bool OnQueryTextChange(string newText)
{
return false;
}
public bool OnQueryTextSubmit(string query)
{
if (String.IsNullOrEmpty(query))
return false; //let the default happen
Intent searchIntent = new Intent(_activity, typeof(search.SearchResults));
searchIntent.SetAction(Intent.ActionSearch); //currently not necessary to set because SearchResults doesn't care, but let's be as close to the default as possible
searchIntent.PutExtra(SearchManager.Query, query);
//forward appTask:
_activity.AppTask.ToIntent(searchIntent);
_activity.StartActivityForResult(searchIntent, 0);
return true;
}
}
public override bool OnCreateOptionsMenu(IMenu menu) {
base.OnCreateOptionsMenu(menu);
@ -263,11 +305,13 @@ namespace keepass2android
inflater.Inflate(Resource.Menu.group, menu);
if (Util.HasActionBar(this))
{
var searchManager = (SearchManager) GetSystemService(Context.SearchService);
var searchView = (SearchView) menu.FindItem(Resource.Id.menu_search).ActionView;
var searchManager = (SearchManager) GetSystemService(SearchService);
IMenuItem searchItem = menu.FindItem(Resource.Id.menu_search);
var searchView = (SearchView) searchItem.ActionView;
searchView.SetSearchableInfo(searchManager.GetSearchableInfo(ComponentName));
searchView.SetOnSuggestionListener(new SuggestionListener(searchView.SuggestionsAdapter));
searchView.SetOnSuggestionListener(new SuggestionListener(searchView.SuggestionsAdapter, this, searchItem));
searchView.SetOnQueryTextListener(new OnQueryTextListener(this));
}
var item = menu.FindItem(Resource.Id.menu_sync);
if (item != null)

View File

@ -46,6 +46,7 @@ using String = System.String;
* (AdvancedSearch Menu) -> Search -> SearchResults -> EntryView -> EntryEdit
* (SearchWidget) -> SearchResults -> EntryView -> EntryEdit
* Password -> ShareUrlResults -> EntryView
* FileSelect -> Group (after Create DB)
*
*
* In each of these activities, an AppTask may be present and must be passed to started activities and ActivityResults
@ -84,11 +85,23 @@ namespace keepass2android
AppTask _appTask;
private ActivityDesign _design;
protected override void OnCreate (Bundle bundle)
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate (bundle);
base.OnCreate(savedInstanceState);
_design.ApplyTheme();
_appTask = AppTask.GetTaskInOnCreate(bundle, Intent);
//see comment to this in PasswordActivity.
//Note that this activity is affected even though it's finished when the app is closed because it
//seems that the "app launch intent" is re-delivered, so this might end up here.
if ((_appTask == null) && (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)))
{
_appTask = new NullTask();
}
else
{
_appTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
}
Kp2aLog.Log("KeePass.OnCreate");
}
@ -274,9 +287,8 @@ namespace keepass2android
Intent intent = new Intent(this, typeof(FileSelectActivity));
_appTask.ToIntent(intent);
StartActivityForResult(intent, 0);
intent.AddFlags(ActivityFlags.ForwardResult);
StartActivity(intent);
Finish();
}
@ -288,11 +300,6 @@ namespace keepass2android
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) {
base.OnActivityResult(requestCode, resultCode, data);
Finish();
}
}
}

View File

@ -179,11 +179,11 @@ namespace keepass2android
Intent i = new Intent(act, typeof(PasswordActivity));
PutIoConnectionToIntent(ioc, i);
i.SetFlags(ActivityFlags.ClearTask);
i.SetFlags(ActivityFlags.ClearTask | ActivityFlags.ForwardResult);
appTask.ToIntent(i);
act.StartActivityForResult(i, 0);
act.StartActivity(i);
}
@ -198,11 +198,21 @@ namespace keepass2android
Kp2aLog.Log("PasswordActivity.OnActivityResult "+resultCode+"/"+requestCode);
AppTask.TryGetFromActivityResult(data, ref AppTask);
//NOTE: original code from k eepassdroid used switch ((Android.App.Result)requestCode) { (but doesn't work here, although k eepassdroid works)
switch(resultCode) {
case KeePass.ExitNormal: // Returned to this screen using the Back key, treat as locking the database
App.Kp2a.LockDatabase();
case KeePass.ExitNormal: // Returned to this screen using the Back key
if (PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.LockWhenNavigateBack_key), false))
{
App.Kp2a.LockDatabase();
}
//by leaving the app with the back button, the user probably wants to cancel the task
//The activity might be resumed (through Android's recent tasks list), then use a NullTask:
AppTask = new NullTask();
Finish();
break;
case KeePass.ExitLock:
// The database has already been locked, and the quick unlock screen will be shown if appropriate
@ -266,6 +276,7 @@ namespace keepass2android
}
private void LoadOtpFile()
{
new LoadingDialog<object, object, object>(this, true,
@ -340,7 +351,19 @@ namespace keepass2android
Intent i = Intent;
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
//only load the AppTask if this is the "first" OnCreate (not because of kill/resume, i.e. savedInstanceState==null)
// and if the activity is not launched from history (i.e. recent tasks) because this would mean that
// the Activity was closed already (user cancelling the task or task complete) but is restarted due recent tasks.
// Don't re-start the task (especially bad if tak was complete already)
if ((savedInstanceState == null) && (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)))
{
AppTask = new NullTask();
}
else
{
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
}
String action = i.Action;
@ -470,7 +493,6 @@ namespace keepass2android
Toast.MakeText(this, GetString(Resource.String.otp_discarded_because_no_db), ToastLength.Long).Show();
GoToFileSelectActivity();
Finish();
return false;
}
@ -1096,7 +1118,8 @@ namespace keepass2android
Intent intent = new Intent(this, typeof (FileSelectActivity));
intent.PutExtra(FileSelectActivity.NoForwardToPasswordActivity, true);
AppTask.ToIntent(intent);
StartActivityForResult(intent, 0);
intent.AddFlags(ActivityFlags.ForwardResult);
StartActivity(intent);
Finish();
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

View File

@ -90,6 +90,8 @@
<string name="UseFileTransactions_key">UseFileTransactions</string>
<string name="LockWhenScreenOff_key">LockWhenScreenOff</string>
<string name="LockWhenNavigateBack_key">LockWhenNavigateBack</string>
<string name="UseOfflineCache_key">UseOfflineCache</string>
<string name="AcceptAllServerCertificates_key">AcceptAllServerCertificates</string>
<string name="CheckForFileChangesOnSave_key">CheckForFileChangesOnSave</string>

View File

@ -251,6 +251,11 @@
<string name="UseFileTransactions_summary">Use file transactions for writing databases</string>
<string name="LockWhenScreenOff_title">Lock when screen off</string>
<string name="LockWhenScreenOff_summary">Lock the database when screen is switched off.</string>
<string name="LockWhenNavigateBack_title">Lock when leaving app</string>
<string name="LockWhenNavigateBack_summary">Lock the database when leaving the app by pressing the back button.</string>
<string name="UseOfflineCache_title">Database caching</string>
<string name="UseOfflineCache_summary">Keep a copy of remote database files in the application cache directory. This allows to use remote databases even when offline.</string>

View File

@ -88,6 +88,14 @@
android:title="@string/LockWhenScreenOff_title"
android:key="@string/LockWhenScreenOff_key" />
<CheckBoxPreference
android:enabled="true"
android:persistent="true"
android:summary="@string/LockWhenNavigateBack_summary"
android:defaultValue="false"
android:title="@string/LockWhenNavigateBack_title"
android:key="@string/LockWhenNavigateBack_key" />
<CheckBoxPreference
android:key="@string/RememberRecentFiles_key"
android:title="@string/RememberRecentFiles_title"

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
-->
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchMode="showSearchLabelAsBadge"
android:searchSuggestAuthority="keepass2android.keepass2android_debug.SearchProvider"
android:searchSuggestSelection=" ?"
android:searchSuggestThreshold="2"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestIntentData="content://keepass2android.EntryActivity"
/>

View File

@ -105,11 +105,6 @@ namespace keepass2android
{
}
public virtual bool CloseEntryActivityAfterCreate
{
get { return false;}
}
public virtual void PrepareNewEntry(PwEntry newEntry)
{
@ -233,7 +228,10 @@ namespace keepass2android
public static bool TryGetFromActivityResult(Intent data, ref AppTask task)
{
if (data == null)
{
Kp2aLog.Log("TryGetFromActivityResult: no data");
return false;
}
AppTask tempTask = CreateFromBundle(data.Extras, null);
if (tempTask == null)
{
@ -253,9 +251,9 @@ namespace keepass2android
}
public virtual void OnCompleteCreateEntryActivity(EntryActivity entryActivity)
public virtual void CompleteOnCreateEntryActivity(EntryActivity activity)
{
entryActivity.CompleteOnCreate();
activity.StartNotificationsService(false);
}
}
@ -310,10 +308,14 @@ namespace keepass2android
//act.AppTask = new NullTask();
}
public override bool CloseEntryActivityAfterCreate
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
get { return true;}
//show the notifications
activity.StartNotificationsService(true);
//close
activity.CloseAfterTaskComplete();
}
}
@ -322,11 +324,12 @@ namespace keepass2android
/// </summary>
public class SelectEntryTask: AppTask
{
public override bool CloseEntryActivityAfterCreate
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
//keypoint here: close the app after selecting the entry
get { return true;}
//show the notifications
activity.StartNotificationsService(true);
//close
activity.CloseAfterTaskComplete();
}
}
@ -369,20 +372,49 @@ namespace keepass2android
}
}
public override bool CloseEntryActivityAfterCreate
{
get { return true; }
}
public override void OnCompleteCreateEntryActivity(EntryActivity entryActivity)
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
//if the database is readonly, don't offer to modify the URL
if (App.Kp2a.GetDb().CanWrite == false)
{
base.OnCompleteCreateEntryActivity(entryActivity);
ShowNotificationsAndClose(activity);
return;
}
entryActivity.AskAddUrlThenCompleteCreate(UrlToSearchFor);
AskAddUrlThenCompleteCreate(activity, UrlToSearchFor);
}
private static void ShowNotificationsAndClose(EntryActivity activity)
{
activity.StartNotificationsService(true);
activity.CloseAfterTaskComplete();
}
/// <summary>
/// brings up a dialog asking the user whether he wants to add the given URL to the entry for automatic finding
/// </summary>
public void AskAddUrlThenCompleteCreate(EntryActivity activity, string url)
{
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.SetTitle(activity.GetString(Resource.String.AddUrlToEntryDialog_title));
builder.SetMessage(activity.GetString(Resource.String.AddUrlToEntryDialog_text, new Java.Lang.Object[] { url }));
builder.SetPositiveButton(activity.GetString(Resource.String.yes), (dlgSender, dlgEvt) =>
{
activity.AddUrlToEntry(url, () => ShowNotificationsAndClose(activity));
});
builder.SetNegativeButton(activity.GetString(Resource.String.no), (dlgSender, dlgEvt) =>
{
ShowNotificationsAndClose(activity);
});
Dialog dialog = builder.Create();
dialog.Show();
}
}
@ -499,16 +531,14 @@ namespace keepass2android
public override void AfterAddNewEntry(EntryEditActivity entryEditActivity, PwEntry newEntry)
{
EntryActivity.Launch(entryEditActivity, newEntry, -1, new SelectEntryTask());
entryEditActivity.SetResult
(KeePass.ExitCloseAfterTaskComplete);
EntryActivity.Launch(entryEditActivity, newEntry, -1, new SelectEntryTask(), ActivityFlags.ForwardResult);
//no need to call Finish here, that's done in EntryEditActivity ("closeOrShowError")
}
public override bool CloseEntryActivityAfterCreate
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
//if the user selects an entry before creating the new one, we're not closing the app
get { return false;}
base.CompleteOnCreateEntryActivity(activity);
}
}
}

View File

@ -83,7 +83,15 @@ namespace keepass2android
}
else
{
AppTask = AppTask.CreateFromIntent(Intent);
//see PasswordActivity for an explanation
if ((savedInstanceState == null) && (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)))
{
AppTask = new NullTask();
}
else
{
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
}
}
@ -301,8 +309,14 @@ namespace keepass2android
{
base.OnActivityResult(requestCode, resultCode, data);
//update app task.
//this is important even if we're about to close, because then we should get a NullTask here
//in order not to do the same task next time again!
AppTask.TryGetFromActivityResult(data, ref AppTask);
if (resultCode == KeePass.ExitCloseAfterTaskComplete)
{
//no need to set the result ExitCloseAfterTaskComplete here, there's no parent Activity on the stack
Finish();
return;
}

View File

@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/
using System;
using System.Security.Cryptography;
using System.Text;
using Android.Content;
@ -35,6 +36,47 @@ namespace keepass2android
private const String BracketChars = "[]{}()<>";
private readonly Context _cxt;
public sealed class SecureRandom : Random
{
private readonly RandomNumberGenerator _rng = new RNGCryptoServiceProvider();
public override int Next()
{
var data = new byte[sizeof(int)];
_rng.GetBytes(data);
return BitConverter.ToInt32(data, 0) & (int.MaxValue - 1);
}
public override int Next(int maxValue)
{
return Next(0, maxValue);
}
public override int Next(int minValue, int maxValue)
{
if (minValue > maxValue)
{
throw new ArgumentOutOfRangeException();
}
return (int)Math.Floor((minValue + (maxValue - minValue) * NextDouble()));
}
public override double NextDouble()
{
var data = new byte[sizeof(uint)];
_rng.GetBytes(data);
var randUint = BitConverter.ToUInt32(data, 0);
return randUint / (uint.MaxValue + 1.0);
}
public override void NextBytes(byte[] data)
{
_rng.GetBytes(data);
}
}
public PasswordGenerator(Context cxt) {
_cxt = cxt;
@ -52,11 +94,12 @@ namespace keepass2android
int size = characterSet.Length;
StringBuilder buffer = new StringBuilder();
Random random = new Random();
if (size > 0) {
for (int i = 0; i < length; i++) {
Random random = new SecureRandom();
if (size > 0)
{
for (int i = 0; i < length; i++)
{
char c = characterSet[random.Next(size)];
buffer.Append(c);

View File

@ -64,7 +64,7 @@ namespace keepass2android.search
{
var entryIntent = new Intent(this, typeof(EntryActivity));
entryIntent.PutExtra(EntryActivity.KeyEntry, intent.Data.LastPathSegment);
entryIntent.AddFlags(ActivityFlags.ForwardResult);
Finish(); // Close this activity so that the entry activity is navigated to from the main activity, not this one.
StartActivity(entryIntent);
}