Fixed problems with returning Activity results

Added scopes for querying credentials
Extended possibilities of AppTasks
Added QueryCredentialsActivity.cs
Added missing AboutActivity.cs
FileSelectActivity: removed check for savedInstanceState=null. According to stackoverflow, savedInstanceState may be passed when launched from history
Passing of protected field lists added to PluginHost to allow full transfer of entries
This commit is contained in:
Philipp Crocoll 2014-05-21 06:43:56 +02:00
parent c5064d26bf
commit f0a2f9a038
23 changed files with 517 additions and 190 deletions

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/Base")]
[IntentFilter(new[] { "keepass2android.AboutActivity" }, Categories = new[] { Intent.CategoryDefault })]
public class AboutActivity: Activity, IDialogInterfaceOnDismissListener
{
private AboutDialog _dialog;
protected override void OnResume()
{
if ((_dialog == null) || (_dialog.IsShowing == false))
{
if (new ActivityDesign(this).UseDarkTheme)
_dialog = new AboutDialog(this, Android.Resource.Style.ThemeHoloNoActionBarFullscreen);
else
_dialog = new AboutDialog(this, Android.Resource.Style.ThemeHoloLightNoActionBarFullscreen);
_dialog.SetOnDismissListener(this);
_dialog.Show();
}
base.OnResume();
}
public void OnDismiss(IDialogInterface dialog)
{
Finish();
}
}
}

View File

@ -38,7 +38,6 @@ using KeePassLib;
using KeePassLib.Security;
using KeePassLib.Utility;
using Keepass2android.Pluginsdk;
using PluginHostTest;
using keepass2android.Io;
using Uri = Android.Net.Uri;

View File

@ -1,6 +1,5 @@
using Android.Content;
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{

View File

@ -1,5 +1,5 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{

View File

@ -1,5 +1,4 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{

View File

@ -1,5 +1,4 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{

View File

@ -1,5 +1,4 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{

View File

@ -167,18 +167,6 @@ namespace keepass2android
return cv.OnContextItemSelected(item);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
switch (resultCode)
{
case Result.Canceled:
break;
default:
base.OnActivityResult(requestCode, resultCode, data);
break;
}
}
}
}

View File

@ -40,6 +40,9 @@ using String = System.String;
* While the database is closed, there is only one activity on the stack: Keepass -> FileSelect <-> Password.
* After opening an database (in Password), Password is always the root of the stack (exception: after creating a database,
* FileSelect is the root without Password being open).
* Another exception: QueryCredentialsActivity is root of the stack if an external app is querying credentials.
* QueryCredentialsActivity checks the plugin access permissions, then launches FileSelectActivity (which starts
* the normal stack.)
*
* Some possible stacks:
* Password -> Group ( -> Group (subgroups) ... ) -> EntryView -> EntryEdit
@ -184,6 +187,7 @@ namespace keepass2android
private static String LIB_DALVIK = "libdvm.so";
private static String LIB_ART = "libart.so";
private static String LIB_ART_D = "libartd.so";
public static string StartWithTask = "keepass2android.ACTION_START_WITH_TASK";
public KeePass()
{

View File

@ -159,11 +159,11 @@ namespace keepass2android
Intent i = new Intent(act, typeof(PasswordActivity));
i.SetFlags(ActivityFlags.ClearTask | ActivityFlags.NewTask);
i.SetFlags(ActivityFlags.ForwardResult);
i.PutExtra(KeyFilename, fileName);
appTask.ToIntent(i);
act.StartActivityForResult(i, 0);
act.StartActivity(i);
}
@ -179,7 +179,7 @@ namespace keepass2android
Intent i = new Intent(act, typeof(PasswordActivity));
PutIoConnectionToIntent(ioc, i);
i.SetFlags(ActivityFlags.ClearTask | ActivityFlags.ForwardResult);
i.SetFlags(ActivityFlags.ForwardResult);
appTask.ToIntent(i);
@ -355,7 +355,7 @@ namespace keepass2android
// 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)))
if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
{
AppTask = new NullTask();
}

View File

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Keepass2android.Pluginsdk;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/Base")]
[IntentFilter(new[] { Strings.ActionQueryCredentials},
Categories = new[] { Intent.CategoryDefault })]
[IntentFilter(new[] { Strings.ActionQueryCredentialsForOwnPackage },
Categories = new[] { Intent.CategoryDefault })]
public class QueryCredentialsActivity : Activity
{
private const int RequestCodePluginAccess = 1;
private const int RequestCodeQuery = 2;
private const string IsRecreate = "isRecreate";
private const string StartedQuery = "startedQuery";
private bool _startedQuery;
private string _requiredScope;
private string _requestedUrl;
private string _pluginPackage;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
//if launched from history, don't re-use the task. Proceed to FileSelect instead.
if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
{
Kp2aLog.Log("Forwarding to FileSelect. QueryCredentialsActivity started from history.");
Intent intent = new Intent(this, typeof(FileSelectActivity));
intent.AddFlags(ActivityFlags.ForwardResult);
StartActivity(intent);
Finish();
return;
}
_pluginPackage = null;
if (CallingActivity != null)
_pluginPackage = CallingActivity.PackageName;
if (_pluginPackage == null)
{
Kp2aLog.Log("Couldn't retrieve calling package. Probably activity was started without startActivityForResult()");
Finish();
return;
}
if (Intent.Action == Strings.ActionQueryCredentialsForOwnPackage)
{
_requiredScope = Strings.ScopeQueryCredentialsForOwnPackage;
_requestedUrl = "androidapp://" + _pluginPackage;
}
else if (Intent.Action == Strings.ActionQueryCredentials)
{
_requiredScope = Strings.ScopeQueryCredentials;
_requestedUrl = Intent.GetStringExtra(Strings.ExtraQueryString);
}
else
{
Kp2aLog.Log("Invalid action for QueryCredentialsActivity: " + Intent.Action);
SetResult(Result.FirstUser);
Finish();
return;
}
//only start the query or request plugin access when creating the first time.
//if we're restarting (after config change or low memory), we will get onActivityResult() later
//which will either start the next activity or finish this one.
if ((savedInstanceState == null) || (savedInstanceState.GetBoolean(IsRecreate, false) == false))
{
ShowToast();
if (new PluginDatabase(this).HasAcceptedScope(_pluginPackage,_requiredScope))
{
StartQuery();
}
else
{
RequestPluginAccess();
}
}
}
private void ShowToast()
{
string pluginDisplayName = _pluginPackage;
try
{
pluginDisplayName = PackageManager.GetApplicationLabel(PackageManager.GetApplicationInfo(_pluginPackage, 0));
}
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
}
if (String.IsNullOrEmpty(_requestedUrl))
Toast.MakeText(this, GetString(Resource.String.query_credentials, new Java.Lang.Object[] {pluginDisplayName}), ToastLength.Long).Show();
else
Toast.MakeText(this,
GetString(Resource.String.query_credentials_for_url,
new Java.Lang.Object[] { pluginDisplayName, _requestedUrl }), ToastLength.Long).Show(); ;
}
private void StartQuery()
{
//launch FileSelectActivity (which is root of the stack (exception: we're even below!)) with the appropriate task.
//will return the results later
Intent i = new Intent(this, typeof (FileSelectActivity));
//don't show user notifications when an entry is opened.
var task = new SearchUrlTask() {UrlToSearchFor = _requestedUrl, ShowUserNotifications = false};
task.ToIntent(i);
StartActivityForResult(i, RequestCodeQuery);
_startedQuery = true;
}
private void RequestPluginAccess()
{
Intent i = new Intent(this, typeof(PluginDetailsActivity));
i.SetAction(Strings.ActionEditPluginSettings);
i.PutExtra(Strings.ExtraPluginPackage, _pluginPackage);
StartActivityForResult(i, RequestCodePluginAccess);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == RequestCodePluginAccess)
{
if (new PluginDatabase(this).HasAcceptedScope(_pluginPackage, _requiredScope))
{
//user granted access. Search for the requested credentials:
StartQuery();
}
else
{
//user didn't grant access
SetResult(Result.Canceled);
Finish();
}
}
if (requestCode == RequestCodeQuery)
{
if (resultCode == KeePass.ExitCloseAfterTaskComplete)
{
//double check we really have the permission
if (!new PluginDatabase(this).HasAcceptedScope(_pluginPackage, _requiredScope))
{
Kp2aLog.Log("Ohoh! Scope not available, shouldn't get here. Malicious app somewhere?");
SetResult(Result.Canceled);
Finish();
return;
}
//return credentials to caller:
Intent credentialData = new Intent();
PluginHost.AddEntryToIntent(credentialData, App.Kp2a.GetDb().LastOpenedEntry);
SetResult(Result.Ok, credentialData);
Finish();
}
else
{
SetResult(Result.Canceled);
Finish();
}
}
}
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutBoolean(StartedQuery, _startedQuery);
outState.PutBoolean(IsRecreate, true);
}
}
}

View File

@ -3431,59 +3431,59 @@ namespace keepass2android
// aapt resource value: 0x7f090271
public const int CannotMoveGroupHere = 2131296881;
// aapt resource value: 0x7f0902b2
public const int CertificateFailure = 2131296946;
// aapt resource value: 0x7f0902b1
public const int CertificateWarning = 2131296945;
// aapt resource value: 0x7f0902c3
public const int ChangeLog = 2131296963;
// aapt resource value: 0x7f0902c2
public const int ChangeLog_0_7 = 2131296962;
// aapt resource value: 0x7f0902c0
public const int ChangeLog_0_8 = 2131296960;
// aapt resource value: 0x7f0902bf
public const int ChangeLog_0_8_1 = 2131296959;
// aapt resource value: 0x7f0902be
public const int ChangeLog_0_8_2 = 2131296958;
// aapt resource value: 0x7f0902bd
public const int ChangeLog_0_8_3 = 2131296957;
// aapt resource value: 0x7f0902bc
public const int ChangeLog_0_8_4 = 2131296956;
// aapt resource value: 0x7f0902bb
public const int ChangeLog_0_8_5 = 2131296955;
// aapt resource value: 0x7f0902ba
public const int ChangeLog_0_8_6 = 2131296954;
// aapt resource value: 0x7f0902b9
public const int ChangeLog_0_9 = 2131296953;
// aapt resource value: 0x7f0902b8
public const int ChangeLog_0_9_1 = 2131296952;
public const int CertificateFailure = 2131296952;
// aapt resource value: 0x7f0902b7
public const int ChangeLog_0_9_2 = 2131296951;
public const int CertificateWarning = 2131296951;
// aapt resource value: 0x7f0902b6
public const int ChangeLog_0_9_3 = 2131296950;
// aapt resource value: 0x7f0902c9
public const int ChangeLog = 2131296969;
// aapt resource value: 0x7f0902b5
public const int ChangeLog_0_9_3_r5 = 2131296949;
// aapt resource value: 0x7f0902c8
public const int ChangeLog_0_7 = 2131296968;
// aapt resource value: 0x7f0902c6
public const int ChangeLog_0_8 = 2131296966;
// aapt resource value: 0x7f0902c5
public const int ChangeLog_0_8_1 = 2131296965;
// aapt resource value: 0x7f0902c4
public const int ChangeLog_0_8_2 = 2131296964;
// aapt resource value: 0x7f0902c3
public const int ChangeLog_0_8_3 = 2131296963;
// aapt resource value: 0x7f0902c2
public const int ChangeLog_0_8_4 = 2131296962;
// aapt resource value: 0x7f0902c1
public const int ChangeLog_keptDonate = 2131296961;
public const int ChangeLog_0_8_5 = 2131296961;
// aapt resource value: 0x7f0902b4
public const int ChangeLog_title = 2131296948;
// aapt resource value: 0x7f0902c0
public const int ChangeLog_0_8_6 = 2131296960;
// aapt resource value: 0x7f0902bf
public const int ChangeLog_0_9 = 2131296959;
// aapt resource value: 0x7f0902be
public const int ChangeLog_0_9_1 = 2131296958;
// aapt resource value: 0x7f0902bd
public const int ChangeLog_0_9_2 = 2131296957;
// aapt resource value: 0x7f0902bc
public const int ChangeLog_0_9_3 = 2131296956;
// aapt resource value: 0x7f0902bb
public const int ChangeLog_0_9_3_r5 = 2131296955;
// aapt resource value: 0x7f0902c7
public const int ChangeLog_keptDonate = 2131296967;
// aapt resource value: 0x7f0902ba
public const int ChangeLog_title = 2131296954;
// aapt resource value: 0x7f090123
public const int CheckForFileChangesOnSave_key = 2131296547;
@ -3722,17 +3722,29 @@ namespace keepass2android
// aapt resource value: 0x7f09025f
public const int RestoringRemoteFile = 2131296863;
// aapt resource value: 0x7f0902b1
public const int SCOPE_CURRENT_ENTRY_explanation = 2131296945;
// aapt resource value: 0x7f0902b0
public const int SCOPE_CURRENT_ENTRY_title = 2131296944;
// aapt resource value: 0x7f0902af
public const int SCOPE_CURRENT_ENTRY_explanation = 2131296943;
public const int SCOPE_DATABASE_ACTIONS_explanation = 2131296943;
// aapt resource value: 0x7f0902ae
public const int SCOPE_CURRENT_ENTRY_title = 2131296942;
public const int SCOPE_DATABASE_ACTIONS_title = 2131296942;
// aapt resource value: 0x7f0902ad
public const int SCOPE_DATABASE_ACTIONS_explanation = 2131296941;
// aapt resource value: 0x7f0902b3
public const int SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_explanation = 2131296947;
// aapt resource value: 0x7f0902ac
public const int SCOPE_DATABASE_ACTIONS_title = 2131296940;
// aapt resource value: 0x7f0902b2
public const int SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_title = 2131296946;
// aapt resource value: 0x7f0902b5
public const int SCOPE_QUERY_CREDENTIALS_explanation = 2131296949;
// aapt resource value: 0x7f0902b4
public const int SCOPE_QUERY_CREDENTIALS_title = 2131296948;
// aapt resource value: 0x7f090210
public const int SaveAttachmentDialog_open = 2131296784;
@ -4247,11 +4259,11 @@ namespace keepass2android
// aapt resource value: 0x7f090151
public const int brackets = 2131296593;
// aapt resource value: 0x7f0902c8
public const int browser_intall_text = 2131296968;
// aapt resource value: 0x7f0902ce
public const int browser_intall_text = 2131296974;
// aapt resource value: 0x7f0902c9
public const int building_search_idx = 2131296969;
// aapt resource value: 0x7f0902cf
public const int building_search_idx = 2131296975;
// aapt resource value: 0x7f09028c
public const int button_change_location = 2131296908;
@ -4382,14 +4394,14 @@ namespace keepass2android
// aapt resource value: 0x7f090101
public const int db_key = 2131296513;
// aapt resource value: 0x7f0902ca
public const int decrypting_db = 2131296970;
// aapt resource value: 0x7f0902d0
public const int decrypting_db = 2131296976;
// aapt resource value: 0x7f0902cb
public const int decrypting_entry = 2131296971;
// aapt resource value: 0x7f0902d1
public const int decrypting_entry = 2131296977;
// aapt resource value: 0x7f0902cc
public const int default_checkbox = 2131296972;
// aapt resource value: 0x7f0902d2
public const int default_checkbox = 2131296978;
// aapt resource value: 0x7f0900f3
public const int default_file_path = 2131296499;
@ -4412,8 +4424,8 @@ namespace keepass2android
// aapt resource value: 0x7f090108
public const int design_key = 2131296520;
// aapt resource value: 0x7f0902c4
public const int design_title = 2131296964;
// aapt resource value: 0x7f0902ca
public const int design_title = 2131296970;
// aapt resource value: 0x7f09015e
public const int digits = 2131296606;
@ -4445,11 +4457,11 @@ namespace keepass2android
// aapt resource value: 0x7f090160
public const int ellipsis = 2131296608;
// aapt resource value: 0x7f0902c6
public const int enable_plugin_question = 2131296966;
// aapt resource value: 0x7f0902cc
public const int enable_plugin_question = 2131296972;
// aapt resource value: 0x7f0902c5
public const int enable_plugin_title = 2131296965;
// aapt resource value: 0x7f0902cb
public const int enable_plugin_title = 2131296971;
// aapt resource value: 0x7f0901fe
public const int enable_quickunlock = 2131296766;
@ -4481,8 +4493,8 @@ namespace keepass2android
// aapt resource value: 0x7f090162
public const int entry_accessed = 2131296610;
// aapt resource value: 0x7f0902cd
public const int entry_and_or = 2131296973;
// aapt resource value: 0x7f0902d3
public const int entry_and_or = 2131296979;
// aapt resource value: 0x7f090172
public const int entry_binaries = 2131296626;
@ -4535,8 +4547,8 @@ namespace keepass2android
// aapt resource value: 0x7f090294
public const int error_adding_keyfile = 2131296916;
// aapt resource value: 0x7f0902ce
public const int error_arc4 = 2131296974;
// aapt resource value: 0x7f0902d4
public const int error_arc4 = 2131296980;
// aapt resource value: 0x7f090173
public const int error_can_not_handle_uri = 2131296627;
@ -4577,8 +4589,8 @@ namespace keepass2android
// aapt resource value: 0x7f09017e
public const int error_nopass = 2131296638;
// aapt resource value: 0x7f0902cf
public const int error_out_of_memory = 2131296975;
// aapt resource value: 0x7f0902d5
public const int error_out_of_memory = 2131296981;
// aapt resource value: 0x7f09017f
public const int error_pass_gen_type = 2131296639;
@ -4589,8 +4601,8 @@ namespace keepass2android
// aapt resource value: 0x7f090181
public const int error_rounds_not_number = 2131296641;
// aapt resource value: 0x7f0902d0
public const int error_rounds_too_large = 2131296976;
// aapt resource value: 0x7f0902d6
public const int error_rounds_too_large = 2131296982;
// aapt resource value: 0x7f090216
public const int error_string_key = 2131296790;
@ -4607,8 +4619,8 @@ namespace keepass2android
// aapt resource value: 0x7f0901d1
public const int export_database_successful = 2131296721;
// aapt resource value: 0x7f0902b3
public const int export_fileformats_title = 2131296947;
// aapt resource value: 0x7f0902b9
public const int export_fileformats_title = 2131296953;
// aapt resource value: 0x7f090150
public const int export_prefs = 2131296592;
@ -4673,8 +4685,8 @@ namespace keepass2android
// aapt resource value: 0x7f090186
public const int generate_password = 2131296646;
// aapt resource value: 0x7f0902b0
public const int get_regular_version = 2131296944;
// aapt resource value: 0x7f0902b6
public const int get_regular_version = 2131296950;
// aapt resource value: 0x7f090187
public const int group = 2131296647;
@ -4778,11 +4790,11 @@ namespace keepass2android
// aapt resource value: 0x7f0901dd
public const int insert_element_here = 2131296733;
// aapt resource value: 0x7f0902d1
public const int install_from_market = 2131296977;
// aapt resource value: 0x7f0902d7
public const int install_from_market = 2131296983;
// aapt resource value: 0x7f0902d2
public const int install_from_website = 2131296978;
// aapt resource value: 0x7f0902d8
public const int install_from_website = 2131296984;
// aapt resource value: 0x7f090196
public const int invalid_algorithm = 2131296662;
@ -4991,8 +5003,8 @@ namespace keepass2android
// aapt resource value: 0x7f0901ad
public const int menu_hide_password = 2131296685;
// aapt resource value: 0x7f0902d3
public const int menu_homepage = 2131296979;
// aapt resource value: 0x7f0902d9
public const int menu_homepage = 2131296985;
// aapt resource value: 0x7f0901ae
public const int menu_lock = 2131296686;
@ -5129,8 +5141,8 @@ namespace keepass2android
// aapt resource value: 0x7f0902a8
public const int plugin_enabled = 2131296936;
// aapt resource value: 0x7f0902ab
public const int plugin_enabled_checkbox = 2131296939;
// aapt resource value: 0x7f0902ad
public const int plugin_enabled_checkbox = 2131296941;
// aapt resource value: 0x7f0902a5
public const int plugin_packagename = 2131296933;
@ -5141,8 +5153,8 @@ namespace keepass2android
// aapt resource value: 0x7f0902a4
public const int plugins = 2131296932;
// aapt resource value: 0x7f0902c7
public const int plugins_text = 2131296967;
// aapt resource value: 0x7f0902cd
public const int plugins_text = 2131296973;
// aapt resource value: 0x7f09008d
public const int popular_domain_0 = 2131296397;
@ -5213,6 +5225,12 @@ namespace keepass2android
// aapt resource value: 0x7f090219
public const int protection = 2131296793;
// aapt resource value: 0x7f0902ac
public const int query_credentials = 2131296940;
// aapt resource value: 0x7f0902ab
public const int query_credentials_for_url = 2131296939;
// aapt resource value: 0x7f09004b
public const int quick_fixes = 2131296331;

View File

@ -420,6 +420,9 @@
<string name="plugin_disabled">disabled</string>
<string name="plugin_web">Find plug-ins online</string>
<string name="query_credentials_for_url">%1$s is requesting credentials for %2$s.</string>
<string name="query_credentials">%1$s is requesting credentials. Please select an entry.</string>
<string name="plugin_enabled_checkbox">Enabled</string>
<string name="SCOPE_DATABASE_ACTIONS_title">Database action notifications</string>
@ -428,6 +431,12 @@
<string name="SCOPE_CURRENT_ENTRY_title">Current entry data</string>
<string name="SCOPE_CURRENT_ENTRY_explanation">Plugin will receive all data about the current database entry and will be allowed to offer actions and modify the display of it.</string>
<string name="SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_title">Query own credentials</string>
<string name="SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_explanation">Plugin will be allowed to query the credentials associated with its own application package.</string>
<string name="SCOPE_QUERY_CREDENTIALS_title">Query credentials</string>
<string name="SCOPE_QUERY_CREDENTIALS_explanation">Plugin will be allowed to query credentials for deliberate web sites or applications.</string>
<string name="get_regular_version">Get more storage types</string>

View File

@ -59,7 +59,8 @@ namespace keepass2android
{
base.OnCreate(savedInstanceState);
SetResult(KeePass.ExitCloseAfterTaskComplete);
//if user presses back to leave this activity:
SetResult(Result.Canceled);
_db = App.Kp2a.GetDb();
if (App.Kp2a.DatabaseIsUnlocked)
@ -77,12 +78,6 @@ namespace keepass2android
AppTask.ToBundle(outState);
}
public override void LaunchActivityForEntry(KeePassLib.PwEntry pwEntry, int pos)
{
base.LaunchActivityForEntry(pwEntry, pos);
Finish();
}
private void Query(String url)
{
try
@ -105,13 +100,11 @@ namespace keepass2android
} catch (Exception e)
{
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
SetResult(Result.Canceled);
Finish();
return;
}
//if there is exactly one match: open the entry
if (Group.Entries.Count() == 1)
{
@ -153,22 +146,15 @@ namespace keepass2android
{
createUrlEntry.Visibility = ViewStates.Gone;
}
}
public override bool OnSearchRequested()
{
if (base.OnSearchRequested())
{
Finish();
return true;
}
else
{
return false;
}
Intent i = new Intent(this, typeof(SearchActivity));
AppTask.ToIntent(i);
i.SetFlags(ActivityFlags.ForwardResult);
StartActivity(i);
return true;
}
}}

View File

@ -49,23 +49,23 @@ namespace keepass2android
}
/// <summary>
/// represents data stored in an intent or bundle as extra string array
/// represents data stored in an intent or bundle as extra string array list
/// </summary>
public class StringArrayExtra : IExtra
public class StringArrayListExtra : IExtra
{
public string Key { get; set; }
public string[] Value { get; set; }
public IList<string> Value { get; set; }
#region IExtra implementation
public void ToBundle(Bundle b)
{
b.PutStringArray(Key, Value);
b.PutStringArrayList(Key, Value);
}
public void ToIntent(Intent i)
{
i.PutExtra(Key, Value);
i.PutStringArrayListExtra(Key, Value);
}
#endregion
@ -268,7 +268,9 @@ namespace keepass2android
/// <summary>
/// User is about to search an entry for a given URL
/// </summary>
public class SearchUrlTask: AppTask
/// Derive from SelectEntryTask. This means that as soon as an Entry is opened, we're returning with
/// ExitAfterTaskComplete. This also allows te specify the flag if we need to display the user notifications.
public class SearchUrlTask: SelectEntryTask
{
public const String UrlToSearchKey = "UrlToSearch";
@ -280,18 +282,29 @@ namespace keepass2android
public override void Setup(Bundle b)
{
base.Setup(b);
UrlToSearchFor = b.GetString(UrlToSearchKey);
}
public override IEnumerable<IExtra> Extras
{
get
{
foreach (IExtra e in base.Extras)
yield return e;
yield return new StringExtra { Key=UrlToSearchKey, Value = UrlToSearchFor };
}
}
public override void AfterUnlockDatabase(PasswordActivity act)
{
ShareUrlResults.Launch(act, this);
if (String.IsNullOrEmpty(UrlToSearchFor))
{
GroupActivity.Launch(act, new SelectEntryTask() { ShowUserNotifications = ShowUserNotifications});
}
else
{
ShareUrlResults.Launch(act, this);
}
//removed. this causes an issue in the following workflow:
//When the user wants to find an entry for a URL but has the wrong database open he needs
@ -308,13 +321,6 @@ namespace keepass2android
//act.AppTask = new NullTask();
}
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
//show the notifications
activity.StartNotificationsService(true);
//close
activity.CloseAfterTaskComplete();
}
}
@ -324,12 +330,59 @@ namespace keepass2android
/// </summary>
public class SelectEntryTask: AppTask
{
public SelectEntryTask()
{
ShowUserNotifications = true;
CloseAfterCreate = true;
}
public const String ShowUserNotificationsKey = "ShowUserNotifications";
public bool ShowUserNotifications { get; set; }
public const String CloseAfterCreateKey = "CloseAfterCreate";
public bool CloseAfterCreate { get; set; }
public override void Setup(Bundle b)
{
ShowUserNotifications = GetBoolFromBundle(b, ShowUserNotificationsKey, true);
CloseAfterCreate = GetBoolFromBundle(b, CloseAfterCreateKey, true);
}
private static bool GetBoolFromBundle(Bundle b, string key, bool defaultValue)
{
bool boolValue;
if (!Boolean.TryParse(b.GetString(key), out boolValue))
{
boolValue = defaultValue;
}
return boolValue;
}
public override IEnumerable<IExtra> Extras
{
get
{
yield return new StringExtra { Key = ShowUserNotificationsKey, Value = ShowUserNotifications.ToString() };
yield return new StringExtra { Key = CloseAfterCreateKey, Value = CloseAfterCreate.ToString() };
}
}
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
//show the notifications
activity.StartNotificationsService(true);
//close
activity.CloseAfterTaskComplete();
if (ShowUserNotifications)
{
//show the notifications
activity.StartNotificationsService(CloseAfterCreate);
}
if (CloseAfterCreate)
{
//close
activity.CloseAfterTaskComplete();
}
}
}
@ -337,7 +390,7 @@ namespace keepass2android
/// User is about to select an entry. When selected, ask whether the url he was searching for earlier should be stored
/// in the selected entry for later use.
/// </summary>
public class SelectEntryForUrlTask: AppTask
public class SelectEntryForUrlTask: SelectEntryTask
{
/// <summary>
/// default constructor for creating from Bundle
@ -350,6 +403,7 @@ namespace keepass2android
public SelectEntryForUrlTask(string url)
{
UrlToSearchFor = url;
ShowUserNotifications = true;
}
public const String UrlToSearchKey = "UrlToSearch";
@ -362,22 +416,25 @@ namespace keepass2android
public override void Setup(Bundle b)
{
base.Setup(b);
UrlToSearchFor = b.GetString(UrlToSearchKey);
}
public override IEnumerable<IExtra> Extras
{
get
{
foreach (IExtra e in base.Extras)
yield return e;
yield return new StringExtra { Key = UrlToSearchKey, Value = UrlToSearchFor };
}
}
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
//if the database is readonly, don't offer to modify the URL
if (App.Kp2a.GetDb().CanWrite == false)
//if the database is readonly (or no URL exists), don't offer to modify the URL
if ((App.Kp2a.GetDb().CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor)))
{
ShowNotificationsAndClose(activity);
base.CompleteOnCreateEntryActivity(activity);
return;
}
@ -386,12 +443,6 @@ namespace keepass2android
}
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>
@ -404,17 +455,16 @@ namespace keepass2android
builder.SetPositiveButton(activity.GetString(Resource.String.yes), (dlgSender, dlgEvt) =>
{
activity.AddUrlToEntry(url, () => ShowNotificationsAndClose(activity));
activity.AddUrlToEntry(url, () => base.CompleteOnCreateEntryActivity(activity));
});
builder.SetNegativeButton(activity.GetString(Resource.String.no), (dlgSender, dlgEvt) =>
{
ShowNotificationsAndClose(activity);
base.CompleteOnCreateEntryActivity(activity);
});
Dialog dialog = builder.Create();
dialog.Show();
}
}
@ -477,17 +527,35 @@ namespace keepass2android
/// </summary>
public const String ProtectedFieldsListKey = Keepass2android.Pluginsdk.Strings.ExtraProtectedFieldsList;
/// <summary>
/// Extra key to specify whether user notifications (e.g. for copy password or keyboard) should be displayed when the entry
/// is selected after creating.
/// </summary>
public const String ShowUserNotificationsKey = "ShowUserNotifications";
public string Url { get; set; }
public string AllFields { get; set; }
public string[] ProtectedFieldsList { get; set; }
public IList<string> ProtectedFieldsList { get; set; }
public bool ShowUserNotifications { get; set; }
public override void Setup(Bundle b)
{
bool showUserNotification;
if (!Boolean.TryParse(b.GetString(ShowUserNotificationsKey), out showUserNotification))
{
showUserNotification = true; //default to true
}
ShowUserNotifications = showUserNotification;
Url = b.GetString(UrlKey);
AllFields = b.GetString(AllFieldsKey);
ProtectedFieldsList = b.GetStringArray(ProtectedFieldsListKey);
ProtectedFieldsList = b.GetStringArrayList(ProtectedFieldsListKey);
}
public override IEnumerable<IExtra> Extras
{
@ -498,7 +566,9 @@ namespace keepass2android
if (AllFields != null)
yield return new StringExtra { Key = AllFieldsKey, Value = AllFields };
if (ProtectedFieldsList != null)
yield return new StringArrayExtra { Key = ProtectedFieldsListKey, Value = ProtectedFieldsList };
yield return new StringArrayListExtra { Key = ProtectedFieldsListKey, Value = ProtectedFieldsList };
yield return new StringExtra { Key = ShowUserNotificationsKey, Value = ShowUserNotifications.ToString() };
}
}
@ -511,17 +581,14 @@ namespace keepass2android
}
if (AllFields != null)
{
IList<string> protectedFieldsKeys = new List<string>();
if (ProtectedFieldsList != null)
{
protectedFieldsKeys = new Org.Json.JSONArray(ProtectedFieldsList).ToArray<string>();
}
var allFields = new Org.Json.JSONObject(AllFields);
for (var iter = allFields.Keys(); iter.HasNext; )
{
string key = iter.Next().ToString();
string value = allFields.Get(key).ToString();
bool isProtected = protectedFieldsKeys.Contains(key) || key == PwDefs.PasswordField;
bool isProtected = ((ProtectedFieldsList != null) && (ProtectedFieldsList.Contains(key)))
|| (key == PwDefs.PasswordField);
newEntry.Strings.Set(key, new ProtectedString(isProtected, value));
}
@ -531,7 +598,9 @@ namespace keepass2android
public override void AfterAddNewEntry(EntryEditActivity entryEditActivity, PwEntry newEntry)
{
EntryActivity.Launch(entryEditActivity, newEntry, -1, new SelectEntryTask(), ActivityFlags.ForwardResult);
EntryActivity.Launch(entryEditActivity, newEntry, -1,
new SelectEntryTask() { ShowUserNotifications = this.ShowUserNotifications},
ActivityFlags.ForwardResult);
//no need to call Finish here, that's done in EntryEditActivity ("closeOrShowError")
}

View File

@ -26,6 +26,7 @@ using Android.Views;
using Android.Widget;
using Android.Content.PM;
using KeePassLib.Serialization;
using Keepass2android.Pluginsdk;
using keepass2android.Io;
using Environment = Android.OS.Environment;
@ -42,6 +43,8 @@ namespace keepass2android
Label = "@string/kp2a_findUrl",
Categories=new[]{Intent.CategoryDefault},
DataMimeType="text/plain")]
[IntentFilter(new[] { Strings.ActionStartWithTask },
Categories = new[] { Intent.CategoryDefault })]
public class FileSelectActivity : ListActivity
{
private readonly ActivityDesign _design;
@ -84,7 +87,7 @@ namespace keepass2android
else
{
//see PasswordActivity for an explanation
if ((savedInstanceState == null) && (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)))
if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
{
AppTask = new NullTask();
}

View File

@ -127,6 +127,7 @@
<Compile Include="pluginhost\PluginDetailsActivity.cs" />
<Compile Include="pluginhost\PluginHost.cs" />
<Compile Include="pluginhost\PluginListActivity.cs" />
<Compile Include="QueryCredentialsActivity.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="KeePass.cs" />

View File

@ -6,7 +6,6 @@ using Android.Content;
using Android.Views;
using System.Collections.Generic;
using Android.App;
using PluginHostTest;
namespace keepass2android
{

View File

@ -171,5 +171,30 @@ namespace keepass2android
var prefs = _ctx.GetSharedPreferences("KP2A.Plugin." + plugin, FileCreationMode.Private);
prefs.Edit().Clear().Commit();
}
/// <summary>
/// Checks if the given pluginPackage has been granted the requiredScope
/// </summary>
public bool HasAcceptedScope(string pluginPackage, string requiredScope)
{
if (pluginPackage == null)
{
Log.Warn(_tag, "No pluginPackage specified!");
return false;
}
var prefs = GetPreferencesForPlugin(pluginPackage);
if (prefs.GetString(_accessToken, null) == null)
{
Log.Info(_tag, "No access token for " + pluginPackage);
return false;
}
if (!AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(requiredScope))
{
Log.Info(_tag, "Scope " + requiredScope + " not granted for " + pluginPackage);
return false;
}
return true;
}
}
}

View File

@ -15,10 +15,9 @@ using Android.Views;
using Android.Widget;
using Java.Util;
using Keepass2android.Pluginsdk;
using keepass2android;
using keepass2android.views;
namespace PluginHostTest
namespace keepass2android
{
[Activity(Label = AppNames.AppName)]
[IntentFilter(new[] { Strings.ActionEditPluginSettings },

View File

@ -22,7 +22,10 @@ namespace keepass2android
private const string _tag = "KP2A_PluginHost";
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions, Strings.ScopeCurrentEntry };
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions,
Strings.ScopeCurrentEntry,
Strings.ScopeQueryCredentials,
Strings.ScopeQueryCredentialsForOwnPackage};
public static IEnumerable<string> GetAllPlugins(Context ctx)
{
@ -154,9 +157,16 @@ namespace keepass2android
//add the output string array (placeholders replaced taking into account the db context)
Dictionary<string, string> outputFields = entry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString());
JSONObject json = new JSONObject(outputFields);
var jsonStr = json.ToString();
intent.PutExtra(Strings.ExtraEntryOutputData, jsonStr);
JSONObject jsonOutput = new JSONObject(outputFields);
var jsonOutputStr = jsonOutput.ToString();
intent.PutExtra(Strings.ExtraEntryOutputData, jsonOutputStr);
JSONArray jsonProtectedFields = new JSONArray(
entry.OutputStrings
.Where(pair => pair.Value.IsProtected)
.Select(pair => pair.Key)
.ToArray());
intent.PutExtra(Strings.ExtraProtectedFieldsList, jsonProtectedFields.ToString());
intent.PutExtra(Strings.ExtraEntryId, entry.Uuid.ToHexString());

View File

@ -6,7 +6,6 @@ using Android.Content.PM;
using Android.OS;
using Android.Widget;
using Keepass2android.Pluginsdk;
using PluginHostTest;
namespace keepass2android
{

View File

@ -120,12 +120,11 @@ namespace keepass2android.search
public override bool OnSearchRequested()
{
if (base.OnSearchRequested())
{
Finish();
return true;
}
return false;
Intent i = new Intent(this, typeof(SearchActivity));
AppTask.ToIntent(i);
i.SetFlags(ActivityFlags.ForwardResult);
StartActivity(i);
return true;
}
}
}