show entry notification when autofilling an entry with TOTP (to allow copying TOTP to clipboard)
fixes https://github.com/PhilippC/keepass2android/issues/1272
This commit is contained in:
		| @@ -91,7 +91,29 @@ namespace keepass2android | |||||||
| 			 | 			 | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private static String ExtractHost(String url) |         public PwGroup SearchForUuid(Database database, string uuid) | ||||||
|  |         { | ||||||
|  |             SearchParameters sp = SearchParameters.None; | ||||||
|  |             sp.SearchInUuids = true; | ||||||
|  |             sp.SearchString = uuid; | ||||||
|  |  | ||||||
|  |             if (sp.RegularExpression) // Validate regular expression | ||||||
|  |             { | ||||||
|  |                 new Regex(sp.SearchString); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             string strGroupName = _app.GetResourceString(UiStringKey.search_results); | ||||||
|  |             PwGroup pgResults = new PwGroup(true, true, strGroupName, PwIcon.EMailSearch) { IsVirtual = true }; | ||||||
|  |  | ||||||
|  |             PwObjectList<PwEntry> listResults = pgResults.Entries; | ||||||
|  |  | ||||||
|  |             database.Root.SearchEntries(sp, listResults, new NullStatusLogger()); | ||||||
|  |  | ||||||
|  |             return pgResults; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private static String ExtractHost(String url) | ||||||
| 		{ | 		{ | ||||||
| 			return UrlUtil.GetHost(url.Trim()); | 			return UrlUtil.GetHost(url.Trim()); | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -174,10 +174,17 @@ namespace keepass2android | |||||||
| 			PwGroup group = SearchHelper.SearchForExactUrl(this, url); | 			PwGroup group = SearchHelper.SearchForExactUrl(this, url); | ||||||
| 			 | 			 | ||||||
| 			return group; | 			return group; | ||||||
| 			 |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		public PwGroup SearchForHost(String url, bool allowSubdomains) { |         } | ||||||
|  |         public PwGroup SearchForUuid(String uuid) | ||||||
|  |         { | ||||||
|  |             PwGroup group = SearchHelper.SearchForUuid(this, uuid); | ||||||
|  |  | ||||||
|  |             return group; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public PwGroup SearchForHost(String url, bool allowSubdomains) { | ||||||
| 			PwGroup group = SearchHelper.SearchForHost(this, url, allowSubdomains); | 			PwGroup group = SearchHelper.SearchForHost(this, url, allowSubdomains); | ||||||
| 			 | 			 | ||||||
| 			return group; | 			return group; | ||||||
|   | |||||||
| @@ -488,10 +488,11 @@ namespace keepass2android | |||||||
| 			_pluginFieldReceiver = new PluginFieldReceiver(this); | 			_pluginFieldReceiver = new PluginFieldReceiver(this); | ||||||
| 			RegisterReceiver(_pluginFieldReceiver, new IntentFilter(Strings.ActionSetEntryField)); | 			RegisterReceiver(_pluginFieldReceiver, new IntentFilter(Strings.ActionSetEntryField)); | ||||||
|  |  | ||||||
| 			new Thread(NotifyPluginsOnOpen).Start(); |             var notifyPluginsOnOpenThread = new Thread(NotifyPluginsOnOpen); | ||||||
|  |             notifyPluginsOnOpenThread.Start(); | ||||||
|  |  | ||||||
| 			//the rest of the things to do depends on the current app task: | 			//the rest of the things to do depends on the current app task: | ||||||
| 			AppTask.CompleteOnCreateEntryActivity(this); | 			AppTask.CompleteOnCreateEntryActivity(this, notifyPluginsOnOpenThread); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|         private void RemoveFromHistory() |         private void RemoveFromHistory() | ||||||
|   | |||||||
| @@ -63,6 +63,12 @@ namespace keepass2android | |||||||
| 		    launchMode.Launch(act, i); | 		    launchMode.Launch(act, i); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  |         public static void Launch(Activity act, OpenSpecificEntryTask task, ActivityLaunchMode launchMode) | ||||||
|  |         { | ||||||
|  |             Intent i = new Intent(act, typeof(ShareUrlResults)); | ||||||
|  |             task.ToIntent(i); | ||||||
|  |             launchMode.Launch(act, i); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public override bool IsSearchResult |         public override bool IsSearchResult | ||||||
|         { |         { | ||||||
| @@ -83,9 +89,8 @@ namespace keepass2android | |||||||
|  |  | ||||||
|             if (App.Kp2a.DatabaseIsUnlocked) |             if (App.Kp2a.DatabaseIsUnlocked) | ||||||
| 			{ | 			{ | ||||||
| 			    var searchUrlTask = ((SearchUrlTask)AppTask); | 			     | ||||||
| 			    String searchUrl = searchUrlTask.UrlToSearchFor; | 				Query();	 | ||||||
| 				Query(searchUrl, searchUrlTask.AutoReturnFromQuery);	 |  | ||||||
| 			} | 			} | ||||||
|             // else: LockCloseListActivity.OnResume will trigger a broadcast (LockDatabase) which will cause the activity to be finished. |             // else: LockCloseListActivity.OnResume will trigger a broadcast (LockDatabase) which will cause the activity to be finished. | ||||||
|  |  | ||||||
| @@ -99,12 +104,25 @@ namespace keepass2android | |||||||
| 			AppTask.ToBundle(outState); | 			AppTask.ToBundle(outState); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private void Query(string url, bool autoReturnFromQuery) | 		private void Query() | ||||||
|         { |         { | ||||||
|              |             bool canAutoReturnFromQuery = true; | ||||||
|  |             bool shouldAutoReturnFromQuery = true; | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 Group = GetSearchResultsForUrl(url); | 				if (AppTask is SearchUrlTask searchUrlTask) | ||||||
|  |                 { | ||||||
|  |                     String searchUrl = searchUrlTask.UrlToSearchFor; | ||||||
|  |                     canAutoReturnFromQuery = searchUrlTask.AutoReturnFromQuery; | ||||||
|  |                     shouldAutoReturnFromQuery = PreferenceManager.GetDefaultSharedPreferences(this) | ||||||
|  |                         .GetBoolean(GetString(Resource.String.AutoReturnFromQuery_key), true); | ||||||
|  |                     Group = GetSearchResultsForUrl(searchUrl); | ||||||
|  |                 } | ||||||
|  | 				else if (AppTask is OpenSpecificEntryTask openEntryTask) | ||||||
|  |                 { | ||||||
|  |                     Group = GetSearchResultsForUuid(openEntryTask.EntryUuid); | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|             } catch (Exception e) |             } catch (Exception e) | ||||||
| 			{ | 			{ | ||||||
| 				Toast.MakeText(this, e.Message, ToastLength.Long).Show(); | 				Toast.MakeText(this, e.Message, ToastLength.Long).Show(); | ||||||
| @@ -114,7 +132,7 @@ namespace keepass2android | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			//if there is exactly one match: open the entry | 			//if there is exactly one match: open the entry | ||||||
| 			if ((Group.Entries.Count() == 1) && autoReturnFromQuery && PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(GetString(Resource.String.AutoReturnFromQuery_key),true)) | 			if ((Group.Entries.Count() == 1) && canAutoReturnFromQuery && shouldAutoReturnFromQuery) | ||||||
| 			{ | 			{ | ||||||
| 				LaunchActivityForEntry(Group.Entries.Single(),0); | 				LaunchActivityForEntry(Group.Entries.Single(),0); | ||||||
| 				return; | 				return; | ||||||
| @@ -131,32 +149,49 @@ namespace keepass2android | |||||||
|             FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group); |             FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group); | ||||||
|  |  | ||||||
| 			View selectOtherEntry = FindViewById (Resource.Id.select_other_entry); | 			View selectOtherEntry = FindViewById (Resource.Id.select_other_entry); | ||||||
|  |             View createUrlEntry = FindViewById(Resource.Id.add_url_entry); | ||||||
|  |  | ||||||
|             var newTask = new SearchUrlTask() {AutoReturnFromQuery = false, UrlToSearchFor = url}; |             if (AppTask is OpenSpecificEntryTask) | ||||||
| 		    if (AppTask is SelectEntryTask currentSelectTask) |             { | ||||||
| 		        newTask.ShowUserNotifications = currentSelectTask.ShowUserNotifications; |                 selectOtherEntry.Visibility =  ViewStates.Gone; | ||||||
|              |                 createUrlEntry.Visibility = ViewStates.Gone; | ||||||
|             selectOtherEntry.Click += (sender, e) => { |             } | ||||||
| 				GroupActivity.Launch (this, newTask, new ActivityLaunchModeRequestCode(0)); |             else | ||||||
|  |             { | ||||||
|  |                 var searchUrlTask = AppTask as SearchUrlTask; | ||||||
|  |                 String searchUrl = searchUrlTask.UrlToSearchFor; | ||||||
|  |                 selectOtherEntry.Visibility =  ViewStates.Visible; | ||||||
|  |  | ||||||
|  |                 var newTask = new SearchUrlTask() { AutoReturnFromQuery = false, UrlToSearchFor = searchUrl }; | ||||||
|  |                 if (AppTask is SelectEntryTask currentSelectTask) | ||||||
|  |                     newTask.ShowUserNotifications = currentSelectTask.ShowUserNotifications; | ||||||
|  |  | ||||||
|  |                 selectOtherEntry.Click += (sender, e) => { | ||||||
|  |                     GroupActivity.Launch(this, newTask, new ActivityLaunchModeRequestCode(0)); | ||||||
|  |  | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                  | ||||||
|  |  | ||||||
|  |                 if (App.Kp2a.OpenDatabases.Any(db => db.CanWrite)) | ||||||
|  |                 { | ||||||
|  |                     createUrlEntry.Visibility = ViewStates.Visible; | ||||||
|  |                     createUrlEntry.Click += (sender, e) => | ||||||
|  |                     { | ||||||
|  |                         GroupActivity.Launch(this, new CreateEntryThenCloseTask { Url = searchUrl, ShowUserNotifications = (AppTask as SelectEntryTask)?.ShowUserNotifications ?? ShowUserNotificationsMode.Always }, new ActivityLaunchModeRequestCode(0)); | ||||||
|  |                         Toast.MakeText(this, GetString(Resource.String.select_group_then_add, new Java.Lang.Object[] { GetString(Resource.String.add_entry) }), ToastLength.Long).Show(); | ||||||
|  |                     }; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     createUrlEntry.Visibility = ViewStates.Gone; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
| 			}; |  | ||||||
|  |  | ||||||
| 			 |  | ||||||
| 			View createUrlEntry = FindViewById (Resource.Id.add_url_entry); |  | ||||||
|  |  | ||||||
| 			if (App.Kp2a.OpenDatabases.Any(db => db.CanWrite)) |  | ||||||
| 			{ |  | ||||||
| 				createUrlEntry.Visibility = ViewStates.Visible; |  | ||||||
| 				createUrlEntry.Click += (sender, e) => |  | ||||||
| 				{ |  | ||||||
| 					GroupActivity.Launch(this, new CreateEntryThenCloseTask { Url = url, ShowUserNotifications = (AppTask as SelectEntryTask)?.ShowUserNotifications ?? ShowUserNotificationsMode.Always }, new ActivityLaunchModeRequestCode(0)); |  | ||||||
| 					Toast.MakeText(this, GetString(Resource.String.select_group_then_add, new Java.Lang.Object[] { GetString(Resource.String.add_entry) }), ToastLength.Long).Show(); |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| 				createUrlEntry.Visibility = ViewStates.Gone; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			Util.MoveBottomBarButtons(Resource.Id.select_other_entry, Resource.Id.add_url_entry, Resource.Id.bottom_bar, this); | 			Util.MoveBottomBarButtons(Resource.Id.select_other_entry, Resource.Id.add_url_entry, Resource.Id.bottom_bar, this); | ||||||
| 		} | 		} | ||||||
| @@ -201,6 +236,31 @@ namespace keepass2android | |||||||
|             return resultsGroup; |             return resultsGroup; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         public static PwGroup GetSearchResultsForUuid(string uuid) | ||||||
|  |         { | ||||||
|  |             PwGroup resultsGroup = null; | ||||||
|  |             foreach (var db in App.Kp2a.OpenDatabases) | ||||||
|  |             { | ||||||
|  |                  | ||||||
|  |                 var resultsForThisDb = db.SearchForUuid(uuid); | ||||||
|  |                  | ||||||
|  |                 if (resultsGroup == null) | ||||||
|  |                 { | ||||||
|  |                     resultsGroup = resultsForThisDb; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     foreach (var entry in resultsForThisDb.Entries) | ||||||
|  |                     { | ||||||
|  |                         resultsGroup.AddEntry(entry, false, false); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return resultsGroup; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public override bool OnSearchRequested() |         public override bool OnSearchRequested() | ||||||
| 		{ | 		{ | ||||||
| 			Intent i = new Intent(this, typeof(SearchActivity)); | 			Intent i = new Intent(this, typeof(SearchActivity)); | ||||||
|   | |||||||
| @@ -46,7 +46,7 @@ namespace keepass2android | |||||||
|                 foreach (ITotpPluginAdapter adapter in _pluginAdapters) |                 foreach (ITotpPluginAdapter adapter in _pluginAdapters) | ||||||
|                 { |                 { | ||||||
|                     TotpData totpData = adapter.GetTotpData( |                     TotpData totpData = adapter.GetTotpData( | ||||||
|                         App.Kp2a.LastOpenedEntry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), |                         entry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), | ||||||
|                             pair => pair.Value.ReadString()), LocaleManager.LocalizedAppContext, false); |                             pair => pair.Value.ReadString()), LocaleManager.LocalizedAppContext, false); | ||||||
|                     if (totpData.IsTotpEntry) |                     if (totpData.IsTotpEntry) | ||||||
|                     { |                     { | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ using Android.OS; | |||||||
| using Android.Widget; | using Android.Widget; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Linq; | using System.Linq; | ||||||
|  | using System.Threading; | ||||||
| using KeePassLib; | using KeePassLib; | ||||||
| using KeePassLib.Security; | using KeePassLib.Security; | ||||||
| using KeePassLib.Utility; | using KeePassLib.Utility; | ||||||
| @@ -339,7 +340,7 @@ namespace keepass2android | |||||||
|  |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		public virtual void CompleteOnCreateEntryActivity(EntryActivity activity) | 		public virtual void CompleteOnCreateEntryActivity(EntryActivity activity, Thread notifyPluginsOnOpenThread) | ||||||
| 		{ | 		{ | ||||||
| 			activity.StartNotificationsService(false); | 			activity.StartNotificationsService(false); | ||||||
| 		} | 		} | ||||||
| @@ -453,7 +454,7 @@ namespace keepass2android | |||||||
| 			intent.PutExtra(UrlToSearchKey, UrlToSearchFor); | 			intent.PutExtra(UrlToSearchKey, UrlToSearchFor); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	    public override void CompleteOnCreateEntryActivity(EntryActivity activity) | 	    public override void CompleteOnCreateEntryActivity(EntryActivity activity, Thread notifyPluginsOnOpenThread) | ||||||
| 	    { | 	    { | ||||||
|             if (App.Kp2a.LastOpenedEntry != null) |             if (App.Kp2a.LastOpenedEntry != null) | ||||||
| 	            App.Kp2a.LastOpenedEntry.SearchUrl = UrlToSearchFor; | 	            App.Kp2a.LastOpenedEntry.SearchUrl = UrlToSearchFor; | ||||||
| @@ -462,18 +463,18 @@ namespace keepass2android | |||||||
|             //if the database is readonly (or no URL exists), don't offer to modify the URL |             //if the database is readonly (or no URL exists), don't offer to modify the URL | ||||||
|             if ((App.Kp2a.CurrentDb.CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor) || keepass2android.ShareUrlResults.GetSearchResultsForUrl(UrlToSearchFor).Entries.Any(e => e == activity.Entry) )) |             if ((App.Kp2a.CurrentDb.CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor) || keepass2android.ShareUrlResults.GetSearchResultsForUrl(UrlToSearchFor).Entries.Any(e => e == activity.Entry) )) | ||||||
|             { |             { | ||||||
|                 base.CompleteOnCreateEntryActivity(activity); |                 base.CompleteOnCreateEntryActivity(activity, notifyPluginsOnOpenThread); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             AskAddUrlThenCompleteCreate(activity, UrlToSearchFor); |             AskAddUrlThenCompleteCreate(activity, UrlToSearchFor, notifyPluginsOnOpenThread); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// brings up a dialog asking the user whether he wants to add the given URL to the entry for automatic finding |         /// brings up a dialog asking the user whether he wants to add the given URL to the entry for automatic finding | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public void AskAddUrlThenCompleteCreate(EntryActivity activity, string url) |         public void AskAddUrlThenCompleteCreate(EntryActivity activity, string url, Thread notifyPluginsOnOpenThread) | ||||||
|         { |         { | ||||||
|             AlertDialog.Builder builder = new AlertDialog.Builder(activity); |             AlertDialog.Builder builder = new AlertDialog.Builder(activity); | ||||||
|             builder.SetTitle(activity.GetString(Resource.String.AddUrlToEntryDialog_title)); |             builder.SetTitle(activity.GetString(Resource.String.AddUrlToEntryDialog_title)); | ||||||
| @@ -482,12 +483,13 @@ namespace keepass2android | |||||||
|  |  | ||||||
|             builder.SetPositiveButton(activity.GetString(Resource.String.yes), (dlgSender, dlgEvt) => |             builder.SetPositiveButton(activity.GetString(Resource.String.yes), (dlgSender, dlgEvt) => | ||||||
|             { |             { | ||||||
|                 activity.AddUrlToEntry(url, (EntryActivity thenActiveActivity) => base.CompleteOnCreateEntryActivity(thenActiveActivity)); |                 activity.AddUrlToEntry(url, (EntryActivity thenActiveActivity) => base.CompleteOnCreateEntryActivity(thenActiveActivity, notifyPluginsOnOpenThread | ||||||
|  | )); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             builder.SetNegativeButton(activity.GetString(Resource.String.no), (dlgSender, dlgEvt) => |             builder.SetNegativeButton(activity.GetString(Resource.String.no), (dlgSender, dlgEvt) => | ||||||
|             { |             { | ||||||
|                 base.CompleteOnCreateEntryActivity(activity); |                 base.CompleteOnCreateEntryActivity(activity, notifyPluginsOnOpenThread); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             Dialog dialog = builder.Create(); |             Dialog dialog = builder.Create(); | ||||||
| @@ -495,6 +497,44 @@ namespace keepass2android | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public class OpenSpecificEntryTask : SelectEntryTask | ||||||
|  |     { | ||||||
|  |         public OpenSpecificEntryTask() | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public const String EntryUuidKey = "EntryUuid"; | ||||||
|  |  | ||||||
|  |         public string EntryUuid | ||||||
|  |         { | ||||||
|  |             get; | ||||||
|  |             set; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override void Setup(Bundle b) | ||||||
|  |         { | ||||||
|  |             base.Setup(b); | ||||||
|  |             EntryUuid = b.GetString(EntryUuidKey); | ||||||
|  |              | ||||||
|  |         } | ||||||
|  |         public override IEnumerable<IExtra> Extras | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  |                 foreach (IExtra e in base.Extras) | ||||||
|  |                     yield return e; | ||||||
|  |                  | ||||||
|  |                 yield return new StringExtra { Key = EntryUuidKey, Value = EntryUuid }; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override void LaunchFirstGroupActivity(Activity act) | ||||||
|  |         { | ||||||
|  |             ShareUrlResults.Launch(act, this, new ActivityLaunchModeRequestCode(0)); | ||||||
|  |         } | ||||||
|  | 		 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     public enum ShowUserNotificationsMode |     public enum ShowUserNotificationsMode | ||||||
|     { |     { | ||||||
| @@ -545,7 +585,7 @@ namespace keepass2android | |||||||
|             } |             } | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		public override void CompleteOnCreateEntryActivity(EntryActivity activity) | 		public override void CompleteOnCreateEntryActivity(EntryActivity activity, Thread notifyPluginsOnOpenThread) | ||||||
| 		{ | 		{ | ||||||
| 		    Context ctx = activity; | 		    Context ctx = activity; | ||||||
| 		    if (ctx == null) | 		    if (ctx == null) | ||||||
| @@ -563,9 +603,11 @@ namespace keepass2android | |||||||
| 				CopyToClipboardService.CancelNotifications(activity); | 				CopyToClipboardService.CancelNotifications(activity); | ||||||
| 			} | 			} | ||||||
| 			if (CloseAfterCreate) | 			if (CloseAfterCreate) | ||||||
| 			{ |             { | ||||||
| 				//close | 				//give plugins and TOTP time to do their work: | ||||||
| 				activity.CloseAfterTaskComplete();	 |                 notifyPluginsOnOpenThread.Join(TimeSpan.FromSeconds(1)); | ||||||
|  |                 //close | ||||||
|  |                 activity.CloseAfterTaskComplete();	 | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -729,10 +771,10 @@ namespace keepass2android | |||||||
| 			//no need to call Finish here, that's done in EntryEditActivity ("closeOrShowError")	 | 			//no need to call Finish here, that's done in EntryEditActivity ("closeOrShowError")	 | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		public override void CompleteOnCreateEntryActivity(EntryActivity activity) | 		public override void CompleteOnCreateEntryActivity(EntryActivity activity, Thread notifyPluginsOnOpenThread) | ||||||
| 		{ | 		{ | ||||||
| 			//if the user selects an entry before creating the new one, we're not closing the app | 			//if the user selects an entry before creating the new one, we're not closing the app | ||||||
| 			base.CompleteOnCreateEntryActivity(activity); | 			base.CompleteOnCreateEntryActivity(activity, notifyPluginsOnOpenThread); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ using AndroidX.AutoFill.Inline; | |||||||
| using AndroidX.AutoFill.Inline.V1; | using AndroidX.AutoFill.Inline.V1; | ||||||
| using Java.Util.Concurrent.Atomic; | using Java.Util.Concurrent.Atomic; | ||||||
| using keepass2android.services.AutofillBase.model; | using keepass2android.services.AutofillBase.model; | ||||||
|  | using KeePassLib; | ||||||
| using Kp2aAutofillParser; | using Kp2aAutofillParser; | ||||||
|  |  | ||||||
| namespace keepass2android.services.AutofillBase | namespace keepass2android.services.AutofillBase | ||||||
| @@ -29,7 +30,7 @@ namespace keepass2android.services.AutofillBase | |||||||
|         PendingIntent GetAuthPendingIntentForResponse(Context context, string query, string queryDomain, string queryPackage, |         PendingIntent GetAuthPendingIntentForResponse(Context context, string query, string queryDomain, string queryPackage, | ||||||
|             bool isManualRequest, bool autoReturnFromQuery, AutofillServiceBase.DisplayWarning warning); |             bool isManualRequest, bool autoReturnFromQuery, AutofillServiceBase.DisplayWarning warning); | ||||||
|  |  | ||||||
|         PendingIntent GetAuthPendingIntentForWarning(Context context, string query, string queryDomain, string queryPackage, AutofillServiceBase.DisplayWarning warning); |         PendingIntent GetAuthPendingIntentForWarning(Context context, PwUuid entryUuid, AutofillServiceBase.DisplayWarning warning); | ||||||
|  |  | ||||||
|         PendingIntent GetDisablePendingIntentForResponse(Context context, string query,  |         PendingIntent GetDisablePendingIntentForResponse(Context context, string query,  | ||||||
|             bool isManualRequest, bool isDisable); |             bool isManualRequest, bool isDisable); | ||||||
| @@ -262,33 +263,42 @@ namespace keepass2android.services.AutofillBase | |||||||
|         { |         { | ||||||
|             List<Dataset> result = new List<Dataset>(); |             List<Dataset> result = new List<Dataset>(); | ||||||
|             Kp2aLog.Log("AF: BuildEntryDatasets"); |             Kp2aLog.Log("AF: BuildEntryDatasets"); | ||||||
|             var suggestedEntries = GetSuggestedEntries(query).ToDictionary(e => e.DatasetName, e => e); |             Dictionary<PwEntryOutput, FilledAutofillFieldCollection<ViewNodeInputField>> suggestedEntries = GetSuggestedEntries(query); | ||||||
|             Kp2aLog.Log("AF: BuildEntryDatasets found " + suggestedEntries.Count + " entries"); |             Kp2aLog.Log("AF: BuildEntryDatasets found " + suggestedEntries.Count + " entries"); | ||||||
|  |              | ||||||
|             int count = 0; |             int count = 0; | ||||||
|             foreach (var filledAutofillFieldCollection in suggestedEntries.Values) |  | ||||||
|  |             var totpHelper = new Kp2aTotp(); | ||||||
|  |  | ||||||
|  |             foreach (var kvp in suggestedEntries) | ||||||
|             { |             { | ||||||
|  |                 var filledAutofillFieldCollection = kvp.Value; | ||||||
|  |                 PwEntryOutput entry = kvp.Key; | ||||||
|  |  | ||||||
|                 if (filledAutofillFieldCollection == null) |                 if (filledAutofillFieldCollection == null) | ||||||
|                     continue; |                     continue; | ||||||
|  |  | ||||||
|                 var inlinePresentationSpec = AutofillHelper.ExtractSpec(inlinePresentationSpecs, count); |                 var inlinePresentationSpec = AutofillHelper.ExtractSpec(inlinePresentationSpecs, count); | ||||||
|  |  | ||||||
|                 if (warning == DisplayWarning.None) |                 if ((warning == DisplayWarning.None) | ||||||
|  |                     && (totpHelper.TryGetAdapter(entry) == null)) | ||||||
|                 { |                 { | ||||||
|            |                     //no special dataset, we can immediately return the field collection | ||||||
|                     FilledAutofillFieldCollection<ViewNodeInputField> partitionData = |                     FilledAutofillFieldCollection<ViewNodeInputField> partitionData = | ||||||
|                         AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, parser.AutofillFields.FocusedAutofillCanonicalHints); |                         AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, parser.AutofillFields.FocusedAutofillCanonicalHints); | ||||||
|  |  | ||||||
|                     Kp2aLog.Log("AF: Add dataset"); |                     Kp2aLog.Log("AF: Add dataset"); | ||||||
|  |  | ||||||
|                     result.Add(AutofillHelper.NewDataset(this, parser.AutofillFields, partitionData, IntentBuilder,  |                     result.Add(AutofillHelper.NewDataset(this, parser.AutofillFields, partitionData, IntentBuilder, | ||||||
|                         inlinePresentationSpec)); |                         inlinePresentationSpec)); | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     //return an "auth" dataset (actually for just warning the user in case domain/package dont match) |  | ||||||
|  |                     //return an "auth" dataset (actually for just warning the user in case domain/package dont match and/or to make sure that we open the EntryActivity, | ||||||
|  |                     // thus opening the entry notification in case of TOTP) | ||||||
|                     PendingIntent pendingIntent = |                     PendingIntent pendingIntent = | ||||||
|                         IntentBuilder.GetAuthPendingIntentForWarning(this, query, queryDomain, queryPackage, warning); |                         IntentBuilder.GetAuthPendingIntentForWarning(this, entry.Uuid, warning); | ||||||
|                     var datasetName = filledAutofillFieldCollection.DatasetName; |                     var datasetName = filledAutofillFieldCollection.DatasetName; | ||||||
|                     if (datasetName == null) |                     if (datasetName == null) | ||||||
|                     { |                     { | ||||||
| @@ -320,7 +330,7 @@ namespace keepass2android.services.AutofillBase | |||||||
|  |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected abstract List<FilledAutofillFieldCollection<ViewNodeInputField>> GetSuggestedEntries(string query); |         protected abstract Dictionary<PwEntryOutput, FilledAutofillFieldCollection<ViewNodeInputField>> GetSuggestedEntries(string query); | ||||||
|  |  | ||||||
|         public enum DisplayWarning |         public enum DisplayWarning | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ namespace keepass2android.services.AutofillBase | |||||||
|     { |     { | ||||||
|         protected Intent ReplyIntent; |         protected Intent ReplyIntent; | ||||||
|  |  | ||||||
|  |         public static string ExtraUuidString => "EXTRA_UUID_STRING"; | ||||||
|         public static string ExtraQueryString => "EXTRA_QUERY_STRING"; |         public static string ExtraQueryString => "EXTRA_QUERY_STRING"; | ||||||
|         public static string ExtraQueryPackageString => "EXTRA_QUERY_PACKAGE_STRING"; |         public static string ExtraQueryPackageString => "EXTRA_QUERY_PACKAGE_STRING"; | ||||||
|         public static string ExtraQueryDomainString => "EXTRA_QUERY_DOMAIN_STRING"; |         public static string ExtraQueryDomainString => "EXTRA_QUERY_DOMAIN_STRING"; | ||||||
| @@ -50,9 +50,10 @@ namespace keepass2android.services.AutofillBase | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             string requestedUrl = Intent.GetStringExtra(ExtraQueryString); |             string requestedUrl = Intent.GetStringExtra(ExtraQueryString); | ||||||
|             if (requestedUrl == null) |             string requestedUuid = Intent.GetStringExtra(ExtraUuidString); | ||||||
|  |             if (requestedUrl == null && requestedUuid == null) | ||||||
|             { |             { | ||||||
|                 Kp2aLog.Log("ChooseForAutofillActivityBase: no requestedUrl "); |                 Kp2aLog.Log("ChooseForAutofillActivityBase: no requestedUrl and no requestedUuid"); | ||||||
|                 Toast.MakeText(this, "Cannot execute query for null.", ToastLength.Long).Show(); |                 Toast.MakeText(this, "Cannot execute query for null.", ToastLength.Long).Show(); | ||||||
|                 RestartApp(); |                 RestartApp(); | ||||||
|                 return; |                 return; | ||||||
| @@ -134,18 +135,30 @@ namespace keepass2android.services.AutofillBase | |||||||
|         private void Proceed() |         private void Proceed() | ||||||
|         { |         { | ||||||
|             string requestedUrl = Intent.GetStringExtra(ExtraQueryString); |             string requestedUrl = Intent.GetStringExtra(ExtraQueryString); | ||||||
|  |             string requestedUuid = Intent.GetStringExtra(ExtraUuidString); | ||||||
|  |  | ||||||
|             var i = GetQueryIntent(requestedUrl, Intent.GetBooleanExtra(ExtraAutoReturnFromQuery, true), Intent.GetBooleanExtra(ExtraUseLastOpenedEntry, false)); |             if (requestedUuid != null) | ||||||
|             if (i == null) |  | ||||||
|             { |             { | ||||||
|                 //GetQueryIntent returns null if no query is required |                 var i = GetOpenEntryIntent(requestedUuid); | ||||||
|                 ReturnSuccess(); |                 StartActivityForResult(i, RequestCodeQuery); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|                 StartActivityForResult(i, RequestCodeQuery); |             { | ||||||
|  |                 var i = GetQueryIntent(requestedUrl, Intent.GetBooleanExtra(ExtraAutoReturnFromQuery, true), Intent.GetBooleanExtra(ExtraUseLastOpenedEntry, false)); | ||||||
|  |                 if (i == null) | ||||||
|  |                 { | ||||||
|  |                     //GetQueryIntent returns null if no query is required | ||||||
|  |                     ReturnSuccess(); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                     StartActivityForResult(i, RequestCodeQuery); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |              | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected abstract Intent GetQueryIntent(string requestedUrl, bool autoReturnFromQuery, bool useLastOpenedEntry); |         protected abstract Intent GetQueryIntent(string requestedUrl, bool autoReturnFromQuery, bool useLastOpenedEntry); | ||||||
|  |         protected abstract Intent GetOpenEntryIntent(string entryUuid); | ||||||
|  |  | ||||||
|         protected void RestartApp() |         protected void RestartApp() | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -41,6 +41,15 @@ namespace keepass2android.services.Kp2aAutofill | |||||||
|             return i; |             return i; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         protected override Intent GetOpenEntryIntent(string entryUuid) | ||||||
|  |         { | ||||||
|  |             Intent i = new Intent(this, typeof(SelectCurrentDbActivity)); | ||||||
|  |             //don't show user notifications when an entry is opened. | ||||||
|  |             var task = new OpenSpecificEntryTask() { EntryUuid = entryUuid, ShowUserNotifications = ShowUserNotificationsMode.WhenTotp, ActivateKeyboard = false}; | ||||||
|  |             task.ToIntent(i); | ||||||
|  |             return i; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         protected override Result ExpectedActivityResult => KeePass.ExitCloseAfterTaskComplete; |         protected override Result ExpectedActivityResult => KeePass.ExitCloseAfterTaskComplete; | ||||||
|  |  | ||||||
|         protected override FilledAutofillFieldCollection<ViewNodeInputField> GetDataset() |         protected override FilledAutofillFieldCollection<ViewNodeInputField> GetDataset() | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ using System.Linq; | |||||||
| using Android; | using Android; | ||||||
| using Android.App; | using Android.App; | ||||||
| using Android.Content; | using Android.Content; | ||||||
|  | using Android.Preferences; | ||||||
| using Android.Runtime; | using Android.Runtime; | ||||||
| using keepass2android.services.AutofillBase; | using keepass2android.services.AutofillBase; | ||||||
| using keepass2android.services.AutofillBase.model; | using keepass2android.services.AutofillBase.model; | ||||||
| @@ -34,24 +35,29 @@ namespace keepass2android.services | |||||||
|         { |         { | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected override List<FilledAutofillFieldCollection<ViewNodeInputField>> GetSuggestedEntries(string query) |         protected override Dictionary<PwEntryOutput, FilledAutofillFieldCollection<ViewNodeInputField>> GetSuggestedEntries(string query) | ||||||
|         { |         { | ||||||
|             if (!App.Kp2a.DatabaseIsUnlocked) |             if (!App.Kp2a.DatabaseIsUnlocked) | ||||||
|                 return new List<FilledAutofillFieldCollection<ViewNodeInputField>>(); |                 return new Dictionary<PwEntryOutput, FilledAutofillFieldCollection<ViewNodeInputField>>(); | ||||||
|             var foundEntries = (ShareUrlResults.GetSearchResultsForUrl(query)?.Entries ?? new PwObjectList<PwEntry>()) |             var foundEntries = (ShareUrlResults.GetSearchResultsForUrl(query)?.Entries ?? new PwObjectList<PwEntry>()) | ||||||
|                 .Select(e => new PwEntryOutput(e, App.Kp2a.FindDatabaseForElement(e))) |  | ||||||
|                 .ToList(); |                 .ToList(); | ||||||
|  |  | ||||||
|             if (App.Kp2a.LastOpenedEntry?.SearchUrl == query) |             if (App.Kp2a.LastOpenedEntry?.SearchUrl == query) | ||||||
|             { |             { | ||||||
|                 foundEntries.Clear(); |                 foundEntries.Clear(); | ||||||
|                 foundEntries.Add(App.Kp2a.LastOpenedEntry); |                 foundEntries.Add(App.Kp2a.LastOpenedEntry?.Entry); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             //it seems like at least with Firefox we can have at most 3 datasets. Reserve space for the disable/enable dataset and the "fill with KP2A" which allows to select another item |             int numDisableDatasets = 0; | ||||||
|             //so take only 1: |             if (!PreferenceManager.GetDefaultSharedPreferences(this) | ||||||
|             return foundEntries.Take(1).Select(e => ChooseForAutofillActivity.GetFilledAutofillFieldCollectionFromEntry(e, this)) |                     .GetBoolean(GetString(Resource.String.NoAutofillDisabling_key), false)) | ||||||
|                 .ToList(); |                 numDisableDatasets = 1; | ||||||
|  |  | ||||||
|  |                 //it seems like at least with Firefox we can have at most 3 datasets. Reserve space for the disable dataset and the "fill with KP2A" which allows to select another item | ||||||
|  |                 return foundEntries.Take(2-numDisableDatasets) | ||||||
|  |                     .Select(e => new PwEntryOutput(e, App.Kp2a.FindDatabaseForElement(e))) | ||||||
|  |                     .ToDictionary(e => e, | ||||||
|  |                                 e => ChooseForAutofillActivity.GetFilledAutofillFieldCollectionFromEntry(e, this)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected override void HandleSaveRequest(StructureParser parser, StructureParser.AutofillTargetId query) |         protected override void HandleSaveRequest(StructureParser parser, StructureParser.AutofillTargetId query) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ using Android.Views; | |||||||
| using Android.Widget; | using Android.Widget; | ||||||
| using keepass2android.services.AutofillBase; | using keepass2android.services.AutofillBase; | ||||||
| using keepass2android.services.Kp2aAutofill; | using keepass2android.services.Kp2aAutofill; | ||||||
|  | using KeePassLib; | ||||||
|  |  | ||||||
| namespace keepass2android.services | namespace keepass2android.services | ||||||
| { | { | ||||||
| @@ -29,16 +30,14 @@ namespace keepass2android.services | |||||||
|             return PendingIntent.GetActivity(context, _pendingIntentRequestCode++, intent, Util.AddMutabilityFlag(PendingIntentFlags.CancelCurrent, PendingIntentFlags.Mutable)); |             return PendingIntent.GetActivity(context, _pendingIntentRequestCode++, intent, Util.AddMutabilityFlag(PendingIntentFlags.CancelCurrent, PendingIntentFlags.Mutable)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public PendingIntent GetAuthPendingIntentForWarning(Context context, string query, string queryDomain, string queryPackage, |         public PendingIntent GetAuthPendingIntentForWarning(Context context,PwUuid entryUuid, | ||||||
|             AutofillServiceBase.DisplayWarning warning) |             AutofillServiceBase.DisplayWarning warning) | ||||||
|         { |         { | ||||||
|             Intent intent = new Intent(context, typeof(ChooseForAutofillActivity)); |             Intent intent = new Intent(context, typeof(ChooseForAutofillActivity)); | ||||||
|             intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryString, query); |             intent.PutExtra(ChooseForAutofillActivityBase.ExtraUuidString, entryUuid.ToHexString()); | ||||||
|             intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryDomainString, queryDomain); |  | ||||||
|             intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryPackageString, queryPackage); |  | ||||||
|             intent.PutExtra(ChooseForAutofillActivityBase.ExtraDisplayWarning, (int)warning); |             intent.PutExtra(ChooseForAutofillActivityBase.ExtraDisplayWarning, (int)warning); | ||||||
|             intent.PutExtra(ChooseForAutofillActivityBase.ExtraUseLastOpenedEntry, true); |             intent.PutExtra(ChooseForAutofillActivityBase.ExtraUseLastOpenedEntry, true); | ||||||
|             return PendingIntent.GetActivity(context, _pendingIntentRequestCode++, intent, Util.AddMutabilityFlag(PendingIntentFlags.CancelCurrent, PendingIntentFlags.Immutable)); |             return PendingIntent.GetActivity(context, _pendingIntentRequestCode++, intent, Util.AddMutabilityFlag(PendingIntentFlags.CancelCurrent, PendingIntentFlags.Mutable)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public PendingIntent GetDisablePendingIntentForResponse(Context context, string query, |         public PendingIntent GetDisablePendingIntentForResponse(Context context, string query, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Philipp Crocoll
					Philipp Crocoll