Merge branch 'master' into 2507--dialog-behind-keyboard
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());
|
||||
}
|
||||
|
@@ -174,10 +174,17 @@ namespace keepass2android
|
||||
PwGroup group = SearchHelper.SearchForExactUrl(this, url);
|
||||
|
||||
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);
|
||||
|
||||
return group;
|
||||
|
@@ -32,6 +32,7 @@ using Android.Text.Method;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Content.PM;
|
||||
using Android.Webkit;
|
||||
using Android.Graphics;
|
||||
@@ -49,7 +50,9 @@ using PluginTOTP;
|
||||
using File = Java.IO.File;
|
||||
using Uri = Android.Net.Uri;
|
||||
using keepass2android.fileselect;
|
||||
using KeeTrayTOTP.Libraries;
|
||||
using Boolean = Java.Lang.Boolean;
|
||||
using Android.Util;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
@@ -286,6 +289,8 @@ namespace keepass2android
|
||||
extraGroup.AddView(view.View);
|
||||
}
|
||||
|
||||
SetPasswordStyle();
|
||||
|
||||
//update the Entry output in the App database and notify the CopyToClipboard service
|
||||
|
||||
if (App.Kp2a.LastOpenedEntry != null)
|
||||
@@ -488,10 +493,11 @@ namespace keepass2android
|
||||
_pluginFieldReceiver = new PluginFieldReceiver(this);
|
||||
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:
|
||||
AppTask.CompleteOnCreateEntryActivity(this);
|
||||
AppTask.CompleteOnCreateEntryActivity(this, notifyPluginsOnOpenThread);
|
||||
}
|
||||
|
||||
private void RemoveFromHistory()
|
||||
@@ -664,7 +670,7 @@ namespace keepass2android
|
||||
EditModeBase editMode = new DefaultEdit();
|
||||
if (KpEntryTemplatedEdit.IsTemplated(App.Kp2a.CurrentDb, this.Entry))
|
||||
editMode = new KpEntryTemplatedEdit(App.Kp2a.CurrentDb, this.Entry);
|
||||
foreach (var key in editMode.SortExtraFieldKeys(Entry.Strings.GetKeys().Where(key=> !PwDefs.IsStandardField(key))))
|
||||
foreach (var key in editMode.SortExtraFieldKeys(Entry.Strings.GetKeys().Where(key=> !PwDefs.IsStandardField(key) && key != Kp2aTotp.TotpKey)))
|
||||
{
|
||||
if (editMode.IsVisible(key))
|
||||
{
|
||||
@@ -840,7 +846,7 @@ namespace keepass2android
|
||||
{
|
||||
if (!_showPassword.ContainsKey(protectedTextView))
|
||||
{
|
||||
_showPassword[protectedTextView] = fieldKey == UpdateTotpTimerTask.TotpKey ? _showTotpDefault : _showPasswordDefault;
|
||||
_showPassword[protectedTextView] = fieldKey == Kp2aTotp.TotpKey ? _showTotpDefault : _showPasswordDefault;
|
||||
}
|
||||
var protectedTextviewGroup = new ProtectedTextviewGroup { ProtectedField = protectedTextView, VisibleProtectedField = visibleTextView};
|
||||
_protectedTextViews.Add(protectedTextviewGroup);
|
||||
@@ -946,11 +952,13 @@ namespace keepass2android
|
||||
|
||||
PopulateStandardText(Resource.Id.entry_user_name, Resource.Id.entryfield_container_username, PwDefs.UserNameField);
|
||||
PopulateStandardText(Resource.Id.entry_url, Resource.Id.entryfield_container_url, PwDefs.UrlField);
|
||||
PopulateStandardText(new List<int> { Resource.Id.entry_password, Resource.Id.entry_password_visible}, Resource.Id.entryfield_container_password, PwDefs.PasswordField);
|
||||
PopulateStandardText(new List<int> { Resource.Id.entry_totp, Resource.Id.entry_totp_visible }, Resource.Id.entryfield_container_totp, Kp2aTotp.TotpKey);
|
||||
PopulateStandardText(new List<int> { Resource.Id.entry_password, Resource.Id.entry_password_visible}, Resource.Id.entryfield_container_password, PwDefs.PasswordField);
|
||||
|
||||
RegisterProtectedTextView(PwDefs.PasswordField, FindViewById<TextView>(Resource.Id.entry_password), FindViewById<TextView>(Resource.Id.entry_password_visible));
|
||||
RegisterProtectedTextView(Kp2aTotp.TotpKey, FindViewById<TextView>(Resource.Id.entry_totp), FindViewById<TextView>(Resource.Id.entry_totp_visible));
|
||||
|
||||
RegisterTextPopup(FindViewById<RelativeLayout> (Resource.Id.groupname_container),
|
||||
RegisterTextPopup(FindViewById<RelativeLayout> (Resource.Id.groupname_container),
|
||||
FindViewById (Resource.Id.entry_group_name), KeyGroupFullPath);
|
||||
|
||||
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.username_container),
|
||||
@@ -961,9 +969,11 @@ namespace keepass2android
|
||||
.Add(new GotoUrlMenuItem(this, PwDefs.UrlField));
|
||||
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.password_container),
|
||||
FindViewById(Resource.Id.password_vdots), PwDefs.PasswordField);
|
||||
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.totp_container),
|
||||
FindViewById(Resource.Id.totp_vdots), Kp2aTotp.TotpKey);
|
||||
|
||||
|
||||
PopulateText(Resource.Id.entry_created, Resource.Id.entryfield_container_created, getDateTime(Entry.CreationTime));
|
||||
PopulateText(Resource.Id.entry_created, Resource.Id.entryfield_container_created, getDateTime(Entry.CreationTime));
|
||||
PopulateText(Resource.Id.entry_modified, Resource.Id.entryfield_container_modified, getDateTime(Entry.LastModificationTime));
|
||||
|
||||
if (Entry.Expires)
|
||||
@@ -990,6 +1000,40 @@ namespace keepass2android
|
||||
|
||||
SetPasswordStyle();
|
||||
}
|
||||
|
||||
private async Task UpdateTotpCountdown()
|
||||
{
|
||||
if (App.Kp2a.LastOpenedEntry == null)
|
||||
return;
|
||||
var totpData = new Kp2aTotp().TryGetTotpData(App.Kp2a.LastOpenedEntry);
|
||||
|
||||
if (totpData == null || !totpData.IsTotpEntry)
|
||||
return;
|
||||
|
||||
var totpProvider = new TOTPProvider(totpData);
|
||||
|
||||
var progressBar = FindViewById<ProgressBar>(Resource.Id.TotpCountdownProgressBar);
|
||||
|
||||
int lastSecondsLeft = -1;
|
||||
while (!isPaused && progressBar != null)
|
||||
{
|
||||
|
||||
int secondsLeft = totpProvider.Timer;
|
||||
|
||||
if (secondsLeft != lastSecondsLeft)
|
||||
{
|
||||
lastSecondsLeft = secondsLeft;
|
||||
// Update the progress bar on the UI thread
|
||||
RunOnUiThread(() =>
|
||||
{
|
||||
progressBar.Progress = secondsLeft;
|
||||
progressBar.Max = totpProvider.Duration;
|
||||
});
|
||||
}
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulatePreviousVersions()
|
||||
{
|
||||
@@ -1042,7 +1086,7 @@ namespace keepass2android
|
||||
}
|
||||
private List<IPopupMenuItem> RegisterTextPopup(View container, View anchor, string fieldKey)
|
||||
{
|
||||
return RegisterTextPopup(container, anchor, fieldKey, Entry.Strings.GetSafe(fieldKey).IsProtected);
|
||||
return RegisterTextPopup(container, anchor, fieldKey, Entry.Strings.GetSafe(fieldKey).IsProtected || fieldKey == Kp2aTotp.TotpKey);
|
||||
}
|
||||
|
||||
private List<IPopupMenuItem> RegisterTextPopup(View container, View anchor, string fieldKey, bool isProtected)
|
||||
@@ -1055,7 +1099,12 @@ namespace keepass2android
|
||||
popupItems.Add(new CopyToClipboardPopupMenuIcon(this, _stringViews[fieldKey], isProtected));
|
||||
if (isProtected)
|
||||
{
|
||||
var valueView = container.FindViewById<TextView>(fieldKey == PwDefs.PasswordField ? Resource.Id.entry_password : Resource.Id.entry_extra);
|
||||
var valueView = container.FindViewById<TextView>(fieldKey switch
|
||||
{
|
||||
PwDefs.PasswordField => Resource.Id.entry_password,
|
||||
Kp2aTotp.TotpKey => Resource.Id.entry_totp,
|
||||
_ => Resource.Id.entry_extra
|
||||
});
|
||||
popupItems.Add(new ToggleVisibilityPopupMenuItem(this, valueView));
|
||||
}
|
||||
|
||||
@@ -1282,11 +1331,16 @@ namespace keepass2android
|
||||
return base.OnPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
|
||||
bool isPaused = false;
|
||||
|
||||
|
||||
protected override void OnPause()
|
||||
{
|
||||
base.OnPause();
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
private void UpdateTogglePasswordMenu()
|
||||
|
||||
private void UpdateTogglePasswordMenu()
|
||||
{
|
||||
IMenuItem togglePassword = _menu.FindItem(Resource.Id.menu_toggle_pass);
|
||||
if (_showPassword.Values.All(x => x))
|
||||
@@ -1323,7 +1377,9 @@ namespace keepass2android
|
||||
ClearCache();
|
||||
base.OnResume();
|
||||
_activityDesign.ReapplyTheme();
|
||||
}
|
||||
isPaused = false;
|
||||
Task.Run(UpdateTotpCountdown);
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
|
@@ -140,7 +140,7 @@ namespace keepass2android
|
||||
//will return the results later
|
||||
Intent i = new Intent(this, typeof (SelectCurrentDbActivity));
|
||||
//don't show user notifications when an entry is opened.
|
||||
var task = new SearchUrlTask() {UrlToSearchFor = _requestedUrl, ShowUserNotifications = ShowUserNotificationsMode.WhenTotp};
|
||||
var task = new SearchUrlTask() {UrlToSearchFor = _requestedUrl, ShowUserNotifications = ActivationCondition.WhenTotp, ActivateKeyboard = ActivationCondition.Never };
|
||||
task.ToIntent(i);
|
||||
StartActivityForResult(i, RequestCodeQuery);
|
||||
_startedQuery = true;
|
||||
|
BIN
src/keepass2android/Resources/drawable-mdpi/ic_entry_totp.png
Normal file
BIN
src/keepass2android/Resources/drawable-mdpi/ic_entry_totp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
BIN
src/keepass2android/Resources/drawable-xhdpi/ic_entry_totp.png
Normal file
BIN
src/keepass2android/Resources/drawable-xhdpi/ic_entry_totp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
@@ -184,6 +184,68 @@
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/entryfield_container_totp"
|
||||
style="@style/EntryEditSingleLine_container">
|
||||
<ImageView
|
||||
style="@style/EntryEditSingleLine_ImageView"
|
||||
android:src="@drawable/ic_entry_totp" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="vertical">
|
||||
<!-- TOTP -->
|
||||
<TextView
|
||||
android:id="@+id/entry_totp_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/TOTP"
|
||||
style="@style/EntryFieldHeader" />
|
||||
<RelativeLayout
|
||||
android:id="@+id/totp_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="horizontal"
|
||||
android:clickable="true"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
<ImageView
|
||||
android:id="@+id/totp_vdots"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="15dp"
|
||||
android:src="@drawable/vdots"
|
||||
android:gravity="right|bottom"
|
||||
android:layout_alignParentRight="true" />
|
||||
<TextView
|
||||
android:id="@+id/entry_totp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:password="true"
|
||||
android:typeface="monospace"
|
||||
android:layout_toLeftOf="@id/totp_vdots"
|
||||
style="@style/EntryItem" />
|
||||
<TextView
|
||||
android:id="@+id/entry_totp_visible"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toLeftOf="@id/totp_vdots"
|
||||
style="@style/EntryItem" />
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/TotpCountdownProgressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginRight="30dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/entryfield_container_comment"
|
||||
style="@style/EntryEditSingleLine_container">
|
||||
|
@@ -47,6 +47,7 @@
|
||||
<string name="oi_filemanager_web">https://openintents.googlecode.com/files/FileManager-2.0.2.apk</string>
|
||||
<string name="permission_desc2">KP2A Search</string>
|
||||
<string name="permission_desc3">KP2A Choose autofill dataset</string>
|
||||
<string name="AutoFillTotp_prefs_screen_key">AutoFillTotp_prefs_screen_key</string>
|
||||
|
||||
|
||||
<!-- Preference settings -->
|
||||
|
@@ -402,6 +402,16 @@
|
||||
<string name="ShowSeparateNotifications_summary">Show separate notifications for copying username and password to clipboard and activating the keyboard.</string>
|
||||
<string name="AccServiceAutoFill_prefs">AutoFill Accessibility-Service</string>
|
||||
<string name="AutoFill_prefs">AutoFill Service</string>
|
||||
<string name="AutoFillTotp_prefs_ShowNotification_summary">When autofilling an entry with TOTP, show the entry notification with a Copy TOTP button</string>
|
||||
<string name="AutoFillTotp_prefs_ShowNotification_title">Show entry notification</string>
|
||||
<string name="AutoFillTotp_prefs_title">Autofill for TOTP entries</string>
|
||||
<string name="AutoFillTotp_prefs_CopyTotpToClipboard_title">Copy TOTP to clipboard</string>
|
||||
<string name="AutoFillTotp_prefs_CopyTotpToClipboard_summary">When autofilling an entry with TOTP, copy the TOTP to the clipboard</string>
|
||||
<string name="AutoFillTotp_prefs_ActivateKeyboard_summary">When autofilling an entry with TOTP, activate the built-in keyboard. The keyboard has a TOTP button.</string>
|
||||
<string name="AutoFillTotp_prefs_ActivateKeyboard_title">Activate built-in keyboard</string>
|
||||
|
||||
<string name="TotpCopiedToClipboard">Copied TOTP to clipboard</string>
|
||||
|
||||
<string name="ShowKp2aKeyboardNotification_title">KP2A keyboard notification</string>
|
||||
<string name="ShowKp2aKeyboardNotification_summary">Make full entry accessible through the KP2A keyboard (recommended).</string>
|
||||
<string name="OpenKp2aKeyboardAutomatically_title">Switch keyboard</string>
|
||||
@@ -589,6 +599,7 @@
|
||||
<string name="CouldntLoadChalAuxFile_Hint">Please use the KeeChallenge plugin in KeePass 2.x (PC) to configure your database for use with challenge-response!</string>
|
||||
<string name="ErrorUpdatingChalAuxFile">Error updating OTP auxiliary file!</string>
|
||||
<string name="TrayTotp_SeedField_title">TOTP Seed field name</string>
|
||||
<string name="TOTP">TOTP</string>
|
||||
<string name="TrayTotp_SeedField_summary">If you are using the Keepass 2 plugin "TrayTotp" with non-default settings, enter the field name for the seed field here according to the settings on the PC.</string>
|
||||
<string name="TrayTotp_SettingsField_title">TOTP Settings field name</string>
|
||||
<string name="TrayTotp_SettingsField_summary">Enter the field name of the settings field for TrayTotp here.</string>
|
||||
|
@@ -461,7 +461,8 @@
|
||||
android:defaultValue="false"
|
||||
android:title="@string/LogAutofillView_title"
|
||||
android:key="@string/LogAutofillView_key" />
|
||||
|
||||
|
||||
|
||||
|
||||
<CheckBoxPreference
|
||||
android:enabled="true"
|
||||
@@ -476,6 +477,38 @@
|
||||
android:summary="@string/AutofillDisabledQueriesPreference_summary"
|
||||
android:persistent="false"
|
||||
android:key="AutofillDisabledQueriesPreference_key"/>
|
||||
|
||||
<PreferenceScreen
|
||||
android:key="@string/AutoFillTotp_prefs_screen_key"
|
||||
android:title="@string/AutoFillTotp_prefs_title"
|
||||
>
|
||||
<keepass2android.ToolbarPreference
|
||||
android:key="@string/AutoFillTotp_prefs_screen_key"
|
||||
android:title="@string/AutoFillTotp_prefs_title" />
|
||||
|
||||
<CheckBoxPreference android:key="AutoFillTotp_prefs_ShowNotification_key"
|
||||
android:enabled="true"
|
||||
android:persistent="true"
|
||||
android:summary="@string/AutoFillTotp_prefs_ShowNotification_summary"
|
||||
android:defaultValue="true"
|
||||
android:title="@string/AutoFillTotp_prefs_ShowNotification_title"
|
||||
/>
|
||||
<CheckBoxPreference android:key="AutoFillTotp_prefs_CopyTotpToClipboard_key"
|
||||
android:enabled="true"
|
||||
android:persistent="true"
|
||||
android:summary="@string/AutoFillTotp_prefs_CopyTotpToClipboard_summary"
|
||||
android:defaultValue="true"
|
||||
android:title="@string/AutoFillTotp_prefs_CopyTotpToClipboard_title"
|
||||
/>
|
||||
<CheckBoxPreference android:key="AutoFillTotp_prefs_ActivateKeyboard_key"
|
||||
android:enabled="true"
|
||||
android:persistent="true"
|
||||
android:summary="@string/AutoFillTotp_prefs_ActivateKeyboard_summary"
|
||||
android:defaultValue="false"
|
||||
android:title="@string/AutoFillTotp_prefs_ActivateKeyboard_title"
|
||||
/>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -306,7 +307,27 @@ namespace keepass2android
|
||||
}
|
||||
else if (Intent.Action == Intent.ActionSend)
|
||||
{
|
||||
AppTask = new SearchUrlTask { UrlToSearchFor = Intent.GetStringExtra(Intent.ExtraText) };
|
||||
ActivationCondition activationCondition = ActivationCondition.Never;
|
||||
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
|
||||
if (prefs.GetBoolean("kp2a_switch_rooted", false))
|
||||
{
|
||||
activationCondition = ActivationCondition.Always;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if the app is about to be closed again (e.g. after searching for a URL and returning to the browser:
|
||||
// automatically bring up the Keyboard selection dialog
|
||||
if (prefs.GetBoolean(this.GetString(Resource.String.OpenKp2aKeyboardAutomatically_key), this.Resources.GetBoolean(Resource.Boolean.OpenKp2aKeyboardAutomatically_default)))
|
||||
{
|
||||
activationCondition = ActivationCondition.Always;
|
||||
}
|
||||
}
|
||||
|
||||
AppTask = new SearchUrlTask()
|
||||
{
|
||||
UrlToSearchFor = Intent.GetStringExtra(Intent.ExtraText),
|
||||
ActivateKeyboard = activationCondition
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -63,6 +63,12 @@ namespace keepass2android
|
||||
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
|
||||
{
|
||||
@@ -76,21 +82,15 @@ namespace keepass2android
|
||||
//if user presses back to leave this activity:
|
||||
SetResult(Result.Canceled);
|
||||
|
||||
|
||||
UpdateBottomBarElementVisibility(Resource.Id.select_other_entry, true);
|
||||
UpdateBottomBarElementVisibility(Resource.Id.add_url_entry, true);
|
||||
|
||||
|
||||
if (App.Kp2a.DatabaseIsUnlocked)
|
||||
{
|
||||
var searchUrlTask = ((SearchUrlTask)AppTask);
|
||||
String searchUrl = searchUrlTask.UrlToSearchFor;
|
||||
Query(searchUrl, searchUrlTask.AutoReturnFromQuery);
|
||||
Query();
|
||||
}
|
||||
// else: LockCloseListActivity.OnResume will trigger a broadcast (LockDatabase) which will cause the activity to be finished.
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected override void OnSaveInstanceState(Bundle outState)
|
||||
@@ -99,12 +99,25 @@ namespace keepass2android
|
||||
AppTask.ToBundle(outState);
|
||||
}
|
||||
|
||||
private void Query(string url, bool autoReturnFromQuery)
|
||||
private void Query()
|
||||
{
|
||||
|
||||
bool canAutoReturnFromQuery = true;
|
||||
bool shouldAutoReturnFromQuery = true;
|
||||
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)
|
||||
{
|
||||
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
|
||||
@@ -114,7 +127,7 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
//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);
|
||||
return;
|
||||
@@ -131,32 +144,57 @@ namespace keepass2android
|
||||
FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group);
|
||||
|
||||
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 SelectEntryTask currentSelectTask)
|
||||
newTask.ShowUserNotifications = currentSelectTask.ShowUserNotifications;
|
||||
|
||||
selectOtherEntry.Click += (sender, e) => {
|
||||
GroupActivity.Launch (this, newTask, new ActivityLaunchModeRequestCode(0));
|
||||
if (AppTask is OpenSpecificEntryTask)
|
||||
{
|
||||
selectOtherEntry.Visibility = ViewStates.Gone;
|
||||
createUrlEntry.Visibility = ViewStates.Gone;
|
||||
}
|
||||
else
|
||||
{
|
||||
var searchUrlTask = AppTask as SearchUrlTask;
|
||||
String searchUrl = searchUrlTask.UrlToSearchFor;
|
||||
selectOtherEntry.Visibility = ViewStates.Visible;
|
||||
|
||||
SearchUrlTask newTask;
|
||||
if (AppTask is SelectEntryTask currentSelectTask)
|
||||
{
|
||||
newTask = new SearchUrlTask() { AutoReturnFromQuery = false, UrlToSearchFor = searchUrl, ActivateKeyboard = currentSelectTask.ActivateKeyboard };
|
||||
newTask.ShowUserNotifications = currentSelectTask.ShowUserNotifications;
|
||||
newTask.ActivateKeyboard = currentSelectTask.ActivateKeyboard;
|
||||
newTask.CopyTotpToClipboard = currentSelectTask.CopyTotpToClipboard;
|
||||
}
|
||||
else
|
||||
newTask = new SearchUrlTask() { AutoReturnFromQuery = false, UrlToSearchFor = searchUrl, ActivateKeyboard = ActivationCondition.Never };
|
||||
|
||||
|
||||
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 ?? ActivationCondition.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);
|
||||
}
|
||||
@@ -201,6 +239,31 @@ namespace keepass2android
|
||||
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()
|
||||
{
|
||||
Intent i = new Intent(this, typeof(SearchActivity));
|
||||
|
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using Android.Content;
|
||||
using keepass2android;
|
||||
using KeePassLib.Collections;
|
||||
|
||||
namespace PluginTOTP
|
||||
@@ -39,23 +40,34 @@ namespace PluginTOTP
|
||||
{
|
||||
TotpData res = new TotpData();
|
||||
string data;
|
||||
if (!_entryFields.TryGetValue("otp", out data))
|
||||
var otpKey = "otp";
|
||||
if (!_entryFields.TryGetValue(otpKey, out data))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
NameValueCollection parameters = ParseQueryString(data);
|
||||
res.InternalFields.Add(otpKey);
|
||||
|
||||
if (parameters[KeyParameter] == null)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
res.TotpSeed = parameters[KeyParameter];
|
||||
|
||||
try
|
||||
{
|
||||
res.TotpSeed = parameters[KeyParameter];
|
||||
|
||||
|
||||
res.Duration = GetIntOrDefault(parameters, StepParameter, 30).ToString();
|
||||
res.Length = GetIntOrDefault(parameters, SizeParameter, 6).ToString();
|
||||
|
||||
res.IsTotpEntry = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.Log("Cannot parse seed");
|
||||
}
|
||||
|
||||
res.Duration = GetIntOrDefault(parameters, StepParameter, 30).ToString();
|
||||
res.Length = GetIntOrDefault(parameters, SizeParameter, 6).ToString();
|
||||
|
||||
res.IsTotpEntry = true;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ namespace keepass2android
|
||||
{
|
||||
return res;
|
||||
}
|
||||
res.InternalFields.Add("otp");
|
||||
|
||||
string otpUriStart = "otpauth://totp/";
|
||||
|
||||
|
@@ -14,14 +14,18 @@ namespace PluginTOTP
|
||||
public TotpData GetTotpData(IDictionary<string, string> entryFields, Context ctx, bool muteWarnings)
|
||||
{
|
||||
TotpData res = new TotpData();
|
||||
byte[] pbSecret = (GetOtpSecret(entryFields, "TimeOtp-") ?? MemUtil.EmptyByteArray);
|
||||
byte[] pbSecret = (GetOtpSecret(entryFields, "TimeOtp-", out string secretFieldKey) ?? MemUtil.EmptyByteArray);
|
||||
|
||||
if (pbSecret.Length == 0)
|
||||
return res;
|
||||
|
||||
res.InternalFields.Add(secretFieldKey);
|
||||
|
||||
string strPeriod;
|
||||
uint uPeriod = 0;
|
||||
if (entryFields.TryGetValue("TimeOtp-Period", out strPeriod))
|
||||
{
|
||||
res.InternalFields.Add("TimeOtp-Period");
|
||||
uint.TryParse(strPeriod, out uPeriod);
|
||||
}
|
||||
|
||||
@@ -34,6 +38,7 @@ namespace PluginTOTP
|
||||
uint uLength = 0;
|
||||
if (entryFields.TryGetValue("TimeOtp-Length", out strLength))
|
||||
{
|
||||
res.InternalFields.Add("TimeOtp-Length");
|
||||
uint.TryParse(strLength, out uLength);
|
||||
}
|
||||
|
||||
@@ -42,6 +47,8 @@ namespace PluginTOTP
|
||||
|
||||
string strAlg;
|
||||
entryFields.TryGetValue("TimeOtp-Algorithm", out strAlg);
|
||||
if (!string.IsNullOrEmpty(strAlg))
|
||||
res.InternalFields.Add("TimeOtp-Algorithm");
|
||||
|
||||
res.HashAlgorithm = strAlg;
|
||||
res.TotpSecret = pbSecret;
|
||||
@@ -52,32 +59,37 @@ namespace PluginTOTP
|
||||
}
|
||||
|
||||
|
||||
private static byte[] GetOtpSecret(IDictionary<string, string> entryFields, string strPrefix)
|
||||
private static byte[] GetOtpSecret(IDictionary<string, string> entryFields, string strPrefix, out string secretFieldKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
string str;
|
||||
entryFields.TryGetValue(strPrefix + "Secret", out str);
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
secretFieldKey = strPrefix + "Secret";
|
||||
entryFields.TryGetValue(secretFieldKey, out str);
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
return StrUtil.Utf8.GetBytes(str);
|
||||
|
||||
entryFields.TryGetValue(strPrefix + "Secret-Hex", out str);
|
||||
|
||||
secretFieldKey = strPrefix + "Secret-Hex";
|
||||
entryFields.TryGetValue(secretFieldKey, out str);
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
return MemUtil.HexStringToByteArray(str);
|
||||
|
||||
entryFields.TryGetValue(strPrefix + "Secret-Base32", out str);
|
||||
|
||||
secretFieldKey = strPrefix + "Secret-Base32";
|
||||
entryFields.TryGetValue(secretFieldKey, out str);
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
return Base32.Decode(str);
|
||||
|
||||
entryFields.TryGetValue(strPrefix + "Secret-Base64", out str);
|
||||
|
||||
secretFieldKey = strPrefix + "Secret-Base64";
|
||||
entryFields.TryGetValue(secretFieldKey, out str);
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
return Convert.FromBase64String(str);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.LogUnexpectedError(e);
|
||||
}
|
||||
|
||||
secretFieldKey = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -10,8 +10,9 @@ namespace keepass2android
|
||||
{
|
||||
class Kp2aTotp
|
||||
{
|
||||
public const string TotpKey = "TOTP";
|
||||
|
||||
readonly ITotpPluginAdapter[] _pluginAdapters = new ITotpPluginAdapter[]
|
||||
readonly ITotpPluginAdapter[] _pluginAdapters = new ITotpPluginAdapter[]
|
||||
{
|
||||
new TrayTotpPluginAdapter(),
|
||||
new KeeOtpPluginAdapter(),
|
||||
@@ -46,7 +47,7 @@ namespace keepass2android
|
||||
foreach (ITotpPluginAdapter adapter in _pluginAdapters)
|
||||
{
|
||||
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);
|
||||
if (totpData.IsTotpEntry)
|
||||
{
|
||||
|
@@ -31,12 +31,14 @@ namespace PluginTOTP
|
||||
public string TimeCorrectionUrl { get; set; }
|
||||
|
||||
public string HashAlgorithm { get; set; }
|
||||
|
||||
|
||||
public bool IsDefaultRfc6238
|
||||
{
|
||||
get { return Length == "6" && Duration == "30" && (HashAlgorithm == null || HashAlgorithm == HashSha1); }
|
||||
}
|
||||
|
||||
public List<string> InternalFields { get; set; } = new List<string>();
|
||||
|
||||
public static TotpData MakeDefaultRfc6238()
|
||||
{
|
||||
return new TotpData()
|
||||
|
@@ -106,6 +106,8 @@ namespace PluginTOTP
|
||||
{
|
||||
bool NoTimeCorrection = false;
|
||||
string[] Settings = SettingsGet(entryFields);
|
||||
res.InternalFields.Add(SettingsFieldName);
|
||||
res.InternalFields.Add(SeedFieldName);
|
||||
res.Duration = Settings[0];
|
||||
res.Length = Settings[1];
|
||||
if (res.Length == "S")
|
||||
|
@@ -13,7 +13,7 @@ namespace PluginTOTP
|
||||
{
|
||||
class UpdateTotpTimerTask: TimerTask
|
||||
{
|
||||
public const string TotpKey = "TOTP";
|
||||
public const string TotpKey = Kp2aTotp.TotpKey;
|
||||
private readonly Context _context;
|
||||
private readonly ITotpPluginAdapter _adapter;
|
||||
|
||||
|
@@ -6,9 +6,13 @@ using Android.OS;
|
||||
using Android.Widget;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Security;
|
||||
using KeePassLib.Utility;
|
||||
using KeeTrayTOTP.Libraries;
|
||||
using Android.Content.Res;
|
||||
using Android.Preferences;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
@@ -339,9 +343,17 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
public virtual void CompleteOnCreateEntryActivity(EntryActivity activity)
|
||||
public virtual void CompleteOnCreateEntryActivity(EntryActivity activity, Thread notifyPluginsOnOpenThread)
|
||||
{
|
||||
activity.StartNotificationsService(false);
|
||||
//this default implementation is executed when we're opening an entry manually, i.e. without search/autofill.
|
||||
//We only activate the keyboard if this is enabled in "silent mode"
|
||||
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(activity);
|
||||
bool activateKeyboard = prefs.GetBoolean("kp2a_switch_rooted", false) &&
|
||||
!prefs.GetBoolean(
|
||||
activity.GetString(Resource.String
|
||||
.OpenKp2aKeyboardAutomaticallyOnlyAfterSearch_key), false);
|
||||
|
||||
activity.StartNotificationsService(activateKeyboard);
|
||||
}
|
||||
|
||||
public virtual void PopulatePasswordAccessServiceIntent(Intent intent)
|
||||
@@ -353,7 +365,8 @@ namespace keepass2android
|
||||
protected static bool GetBoolFromBundle(Bundle b, string key, bool defaultValue)
|
||||
{
|
||||
bool boolValue;
|
||||
if (!Boolean.TryParse(b.GetString(key), out boolValue))
|
||||
string stringValue = b.GetString(key);
|
||||
if (!Boolean.TryParse(stringValue, out boolValue))
|
||||
{
|
||||
boolValue = defaultValue;
|
||||
}
|
||||
@@ -363,7 +376,8 @@ namespace keepass2android
|
||||
protected static int GetIntFromBundle(Bundle b, string key, int defaultValue)
|
||||
{
|
||||
int intValue;
|
||||
if (!Int32.TryParse(b.GetString(key), out intValue))
|
||||
var strValue = b.GetString(key);
|
||||
if (!Int32.TryParse(strValue, out intValue))
|
||||
{
|
||||
intValue = defaultValue;
|
||||
}
|
||||
@@ -383,7 +397,7 @@ namespace keepass2android
|
||||
/// User is about to search an entry for a given URL
|
||||
/// </summary>
|
||||
/// 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.
|
||||
/// ExitAfterTaskComplete. This also allows to specify the flag if we need to display the user notifications.
|
||||
public class SearchUrlTask: SelectEntryTask
|
||||
{
|
||||
public SearchUrlTask()
|
||||
@@ -392,8 +406,9 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
public const String UrlToSearchKey = "UrlToSearch";
|
||||
public const String AutoReturnFromQueryKey = "AutoReturnFromQuery";
|
||||
|
||||
public string UrlToSearchFor
|
||||
public string UrlToSearchFor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@@ -416,7 +431,7 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
public const String AutoReturnFromQueryKey = "AutoReturnFromQuery";
|
||||
|
||||
|
||||
public bool AutoReturnFromQuery { get; set; }
|
||||
|
||||
@@ -424,15 +439,19 @@ namespace keepass2android
|
||||
{
|
||||
if (String.IsNullOrEmpty(UrlToSearchFor))
|
||||
{
|
||||
GroupActivity.Launch(act, new SelectEntryTask() { ShowUserNotifications = ShowUserNotifications}, new ActivityLaunchModeRequestCode(0));
|
||||
GroupActivity.Launch(act, new SelectEntryTask() {
|
||||
ShowUserNotifications = ShowUserNotifications,
|
||||
CopyTotpToClipboard = CopyTotpToClipboard,
|
||||
ActivateKeyboard = ActivateKeyboard
|
||||
},
|
||||
new ActivityLaunchModeRequestCode(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
ShareUrlResults.Launch(act, this, new ActivityLaunchModeRequestCode(0));
|
||||
}
|
||||
|
||||
|
||||
//removed. this causes an issue in the following workflow:
|
||||
//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
|
||||
//to switch to another database. But the Task is removed already the first time when going through PasswordActivity
|
||||
// (with the wrong db).
|
||||
@@ -453,7 +472,7 @@ namespace keepass2android
|
||||
intent.PutExtra(UrlToSearchKey, UrlToSearchFor);
|
||||
}
|
||||
|
||||
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
|
||||
public override void CompleteOnCreateEntryActivity(EntryActivity activity, Thread notifyPluginsOnOpenThread)
|
||||
{
|
||||
if (App.Kp2a.LastOpenedEntry != null)
|
||||
App.Kp2a.LastOpenedEntry.SearchUrl = UrlToSearchFor;
|
||||
@@ -462,18 +481,18 @@ namespace keepass2android
|
||||
//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) ))
|
||||
{
|
||||
base.CompleteOnCreateEntryActivity(activity);
|
||||
base.CompleteOnCreateEntryActivity(activity, notifyPluginsOnOpenThread);
|
||||
return;
|
||||
}
|
||||
|
||||
AskAddUrlThenCompleteCreate(activity, UrlToSearchFor);
|
||||
AskAddUrlThenCompleteCreate(activity, UrlToSearchFor, notifyPluginsOnOpenThread);
|
||||
}
|
||||
|
||||
|
||||
/// <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)
|
||||
public void AskAddUrlThenCompleteCreate(EntryActivity activity, string url, Thread notifyPluginsOnOpenThread)
|
||||
{
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.SetTitle(activity.GetString(Resource.String.AddUrlToEntryDialog_title));
|
||||
@@ -482,12 +501,13 @@ namespace keepass2android
|
||||
|
||||
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) =>
|
||||
{
|
||||
base.CompleteOnCreateEntryActivity(activity);
|
||||
base.CompleteOnCreateEntryActivity(activity, notifyPluginsOnOpenThread);
|
||||
});
|
||||
|
||||
Dialog dialog = builder.Create();
|
||||
@@ -495,8 +515,46 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
public class OpenSpecificEntryTask : SelectEntryTask
|
||||
{
|
||||
public OpenSpecificEntryTask()
|
||||
{
|
||||
}
|
||||
|
||||
public enum ShowUserNotificationsMode
|
||||
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 ActivationCondition
|
||||
{
|
||||
Never,
|
||||
WhenTotp,
|
||||
@@ -509,29 +567,34 @@ namespace keepass2android
|
||||
{
|
||||
public SelectEntryTask()
|
||||
{
|
||||
ShowUserNotifications = ShowUserNotificationsMode.Always;
|
||||
ShowUserNotifications = ActivationCondition.Always;
|
||||
CloseAfterCreate = true;
|
||||
ActivateKeyboard = true;
|
||||
ActivateKeyboard = ActivationCondition.Never;
|
||||
CopyTotpToClipboard = false;
|
||||
}
|
||||
|
||||
public const String ShowUserNotificationsKey = "ShowUserNotifications";
|
||||
|
||||
|
||||
|
||||
public ShowUserNotificationsMode ShowUserNotifications { get; set; }
|
||||
public ActivationCondition ShowUserNotifications { get; set; }
|
||||
|
||||
public const String CloseAfterCreateKey = "CloseAfterCreate";
|
||||
public const String ActivateKeyboardKey = "ActivateKeyboard";
|
||||
public const String CopyTotpToClipboardKey = "CopyTotpToClipboard";
|
||||
|
||||
public bool CloseAfterCreate { get; set; }
|
||||
public bool ActivateKeyboard { get; set; }
|
||||
public ActivationCondition ActivateKeyboard { get; set; }
|
||||
|
||||
public bool CopyTotpToClipboard { get; set; }
|
||||
|
||||
|
||||
public override void Setup(Bundle b)
|
||||
{
|
||||
ShowUserNotifications = (ShowUserNotificationsMode) GetIntFromBundle(b, ShowUserNotificationsKey, (int)ShowUserNotificationsMode.Always);
|
||||
ShowUserNotifications = (ActivationCondition) GetIntFromBundle(b, ShowUserNotificationsKey, (int)ActivationCondition.Always);
|
||||
CloseAfterCreate = GetBoolFromBundle(b, CloseAfterCreateKey, true);
|
||||
ActivateKeyboard = GetBoolFromBundle(b, ActivateKeyboardKey, true);
|
||||
ActivateKeyboard = (ActivationCondition)GetIntFromBundle(b, ActivateKeyboardKey, (int)ActivationCondition.Always);
|
||||
CopyTotpToClipboard = GetBoolFromBundle(b, CopyTotpToClipboardKey, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -541,31 +604,59 @@ namespace keepass2android
|
||||
{
|
||||
yield return new StringExtra { Key = ShowUserNotificationsKey, Value = ((int)ShowUserNotifications).ToString() };
|
||||
yield return new StringExtra { Key = CloseAfterCreateKey, Value = CloseAfterCreate.ToString() };
|
||||
yield return new StringExtra { Key = ActivateKeyboardKey, Value = ActivateKeyboard.ToString() };
|
||||
yield return new StringExtra { Key = ActivateKeyboardKey, Value = ((int)ActivateKeyboard).ToString() };
|
||||
yield return new StringExtra { Key = CopyTotpToClipboardKey, Value = CopyTotpToClipboard.ToString() };
|
||||
}
|
||||
}
|
||||
|
||||
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
|
||||
public override void CompleteOnCreateEntryActivity(EntryActivity activity, Thread notifyPluginsOnOpenThread)
|
||||
{
|
||||
Context ctx = activity;
|
||||
if (ctx == null)
|
||||
ctx = LocaleManager.LocalizedAppContext;
|
||||
|
||||
if ((ShowUserNotifications == ShowUserNotificationsMode.Always)
|
||||
|| ((ShowUserNotifications == ShowUserNotificationsMode.WhenTotp) && new Kp2aTotp().TryGetAdapter(new PwEntryOutput(activity.Entry, App.Kp2a.CurrentDb)) != null))
|
||||
{
|
||||
//show the notifications
|
||||
activity.StartNotificationsService(ActivateKeyboard);
|
||||
}
|
||||
var pwEntryOutput = new PwEntryOutput(activity.Entry, App.Kp2a.CurrentDb);
|
||||
var totpPluginAdapter = new Kp2aTotp().TryGetAdapter(pwEntryOutput);
|
||||
bool isTotpEntry = totpPluginAdapter != null;
|
||||
|
||||
bool activateKeyboard = ActivateKeyboard == ActivationCondition.Always || (ActivateKeyboard == ActivationCondition.WhenTotp && isTotpEntry);
|
||||
|
||||
if ((ShowUserNotifications == ActivationCondition.Always)
|
||||
|| ((ShowUserNotifications == ActivationCondition.WhenTotp) && isTotpEntry)
|
||||
|| activateKeyboard)
|
||||
{
|
||||
//show the notifications
|
||||
activity.StartNotificationsService(activateKeyboard);
|
||||
}
|
||||
else
|
||||
{
|
||||
//to avoid getting into inconsistent state (LastOpenedEntry and Notifications): clear notifications:
|
||||
CopyToClipboardService.CancelNotifications(activity);
|
||||
}
|
||||
if (CloseAfterCreate)
|
||||
{
|
||||
//close
|
||||
activity.CloseAfterTaskComplete();
|
||||
|
||||
if (CopyTotpToClipboard && isTotpEntry)
|
||||
{
|
||||
Dictionary<string, string> entryFields = pwEntryOutput.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString());
|
||||
var totpData= totpPluginAdapter.GetTotpData(entryFields, activity, true);
|
||||
if (totpData.IsTotpEntry)
|
||||
{
|
||||
TOTPProvider prov = new TOTPProvider(totpData);
|
||||
string totp = prov.GenerateByByte(totpData.TotpSecret);
|
||||
CopyToClipboardService.CopyValueToClipboardWithTimeout(activity, totp, true);
|
||||
|
||||
Toast.MakeText(activity, activity.GetString(Resource.String.TotpCopiedToClipboard),
|
||||
ToastLength.Long).Show();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (CloseAfterCreate)
|
||||
{
|
||||
//give plugins and TOTP time to do their work:
|
||||
notifyPluginsOnOpenThread.Join(TimeSpan.FromSeconds(1));
|
||||
//close
|
||||
activity.CloseAfterTaskComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,8 +720,8 @@ namespace keepass2android
|
||||
public class CreateEntryThenCloseTask: AppTask
|
||||
{
|
||||
public CreateEntryThenCloseTask()
|
||||
{
|
||||
ShowUserNotifications = ShowUserNotificationsMode.Always;
|
||||
{
|
||||
ShowUserNotifications = ActivationCondition.Always;
|
||||
}
|
||||
|
||||
public override bool CanActivateSearchViewOnStart
|
||||
@@ -670,13 +761,13 @@ namespace keepass2android
|
||||
|
||||
public IList<string> ProtectedFieldsList { get; set; }
|
||||
|
||||
public ShowUserNotificationsMode ShowUserNotifications { get; set; }
|
||||
public ActivationCondition ShowUserNotifications { get; set; }
|
||||
|
||||
|
||||
public override void Setup(Bundle b)
|
||||
{
|
||||
|
||||
ShowUserNotifications = (ShowUserNotificationsMode)GetIntFromBundle(b,ShowUserNotificationsKey, (int)ShowUserNotificationsMode.Always);
|
||||
ShowUserNotifications = (ActivationCondition)GetIntFromBundle(b,ShowUserNotificationsKey, (int)ActivationCondition.Always);
|
||||
|
||||
Url = b.GetString(UrlKey);
|
||||
AllFields = b.GetString(AllFieldsKey);
|
||||
@@ -724,15 +815,15 @@ namespace keepass2android
|
||||
public override void AfterAddNewEntry(EntryEditActivity entryEditActivity, PwEntry newEntry)
|
||||
{
|
||||
EntryActivity.Launch(entryEditActivity, newEntry, -1,
|
||||
new SelectEntryTask { ShowUserNotifications = this.ShowUserNotifications},
|
||||
new SelectEntryTask() { ShowUserNotifications = this.ShowUserNotifications, ActivateKeyboard = ActivationCondition.Never },
|
||||
ActivityFlags.ForwardResult);
|
||||
//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
|
||||
base.CompleteOnCreateEntryActivity(activity);
|
||||
base.CompleteOnCreateEntryActivity(activity, notifyPluginsOnOpenThread);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1985,6 +1985,9 @@
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_pcloudall.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_totp.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
@@ -20,6 +20,7 @@ using AndroidX.AutoFill.Inline;
|
||||
using AndroidX.AutoFill.Inline.V1;
|
||||
using Java.Util.Concurrent.Atomic;
|
||||
using keepass2android.services.AutofillBase.model;
|
||||
using KeePassLib;
|
||||
using Kp2aAutofillParser;
|
||||
|
||||
namespace keepass2android.services.AutofillBase
|
||||
@@ -29,7 +30,7 @@ namespace keepass2android.services.AutofillBase
|
||||
PendingIntent GetAuthPendingIntentForResponse(Context context, string query, string queryDomain, string queryPackage,
|
||||
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,
|
||||
bool isManualRequest, bool isDisable);
|
||||
@@ -262,33 +263,42 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
List<Dataset> result = new List<Dataset>();
|
||||
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");
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
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 =
|
||||
AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, parser.AutofillFields.FocusedAutofillCanonicalHints);
|
||||
|
||||
Kp2aLog.Log("AF: Add dataset");
|
||||
|
||||
result.Add(AutofillHelper.NewDataset(this, parser.AutofillFields, partitionData, IntentBuilder,
|
||||
result.Add(AutofillHelper.NewDataset(this, parser.AutofillFields, partitionData, IntentBuilder,
|
||||
inlinePresentationSpec));
|
||||
}
|
||||
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 =
|
||||
IntentBuilder.GetAuthPendingIntentForWarning(this, query, queryDomain, queryPackage, warning);
|
||||
IntentBuilder.GetAuthPendingIntentForWarning(this, entry.Uuid, warning);
|
||||
var datasetName = filledAutofillFieldCollection.DatasetName;
|
||||
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
|
||||
{
|
||||
|
@@ -24,7 +24,7 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
protected Intent ReplyIntent;
|
||||
|
||||
|
||||
public static string ExtraUuidString => "EXTRA_UUID_STRING";
|
||||
public static string ExtraQueryString => "EXTRA_QUERY_STRING";
|
||||
public static string ExtraQueryPackageString => "EXTRA_QUERY_PACKAGE_STRING";
|
||||
public static string ExtraQueryDomainString => "EXTRA_QUERY_DOMAIN_STRING";
|
||||
@@ -50,9 +50,10 @@ namespace keepass2android.services.AutofillBase
|
||||
}
|
||||
|
||||
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();
|
||||
RestartApp();
|
||||
return;
|
||||
@@ -134,18 +135,30 @@ namespace keepass2android.services.AutofillBase
|
||||
private void Proceed()
|
||||
{
|
||||
string requestedUrl = Intent.GetStringExtra(ExtraQueryString);
|
||||
string requestedUuid = Intent.GetStringExtra(ExtraUuidString);
|
||||
|
||||
var i = GetQueryIntent(requestedUrl, Intent.GetBooleanExtra(ExtraAutoReturnFromQuery, true), Intent.GetBooleanExtra(ExtraUseLastOpenedEntry, false));
|
||||
if (i == null)
|
||||
if (requestedUuid != null)
|
||||
{
|
||||
//GetQueryIntent returns null if no query is required
|
||||
ReturnSuccess();
|
||||
var i = GetOpenEntryIntent(requestedUuid);
|
||||
StartActivityForResult(i, RequestCodeQuery);
|
||||
}
|
||||
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 GetOpenEntryIntent(string entryUuid);
|
||||
|
||||
protected void RestartApp()
|
||||
{
|
||||
|
@@ -501,24 +501,8 @@ namespace keepass2android
|
||||
if (hasKeyboardDataNow)
|
||||
{
|
||||
notBuilder.AddKeyboardAccess();
|
||||
if (prefs.GetBoolean("kp2a_switch_rooted", false))
|
||||
{
|
||||
//switch rooted
|
||||
bool onlySwitchOnSearch = prefs.GetBoolean(GetString(Resource.String.OpenKp2aKeyboardAutomaticallyOnlyAfterSearch_key), false);
|
||||
if (activateKeyboard || (!onlySwitchOnSearch))
|
||||
{
|
||||
ActivateKp2aKeyboard();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if the app is about to be closed again (e.g. after searching for a URL and returning to the browser:
|
||||
// automatically bring up the Keyboard selection dialog
|
||||
if ((activateKeyboard) && prefs.GetBoolean(GetString(Resource.String.OpenKp2aKeyboardAutomatically_key), Resources.GetBoolean(Resource.Boolean.OpenKp2aKeyboardAutomatically_default)))
|
||||
{
|
||||
ActivateKp2aKeyboard();
|
||||
}
|
||||
}
|
||||
if (activateKeyboard)
|
||||
ActivateKp2aKeyboard();
|
||||
|
||||
}
|
||||
|
||||
@@ -548,31 +532,6 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
public void ActivateKeyboardIfAppropriate(bool closeAfterCreate, ISharedPreferences prefs)
|
||||
{
|
||||
if (prefs.GetBoolean("kp2a_switch_rooted", false))
|
||||
{
|
||||
//switch rooted
|
||||
bool onlySwitchOnSearch = prefs.GetBoolean(
|
||||
GetString(Resource.String.OpenKp2aKeyboardAutomaticallyOnlyAfterSearch_key), false);
|
||||
if (closeAfterCreate || (!onlySwitchOnSearch))
|
||||
{
|
||||
ActivateKp2aKeyboard();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if the app is about to be closed again (e.g. after searching for a URL and returning to the browser:
|
||||
// automatically bring up the Keyboard selection dialog
|
||||
if ((closeAfterCreate) &&
|
||||
prefs.GetBoolean(GetString(Resource.String.OpenKp2aKeyboardAutomatically_key),
|
||||
Resources.GetBoolean(Resource.Boolean.OpenKp2aKeyboardAutomatically_default)))
|
||||
{
|
||||
ActivateKp2aKeyboard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool ClearNotifications()
|
||||
{
|
||||
// Notification Manager
|
||||
@@ -587,6 +546,7 @@ namespace keepass2android
|
||||
return hadKeyboardData;
|
||||
}
|
||||
|
||||
|
||||
bool MakeAccessibleForKeyboard(PwEntryOutput entry, string searchUrl)
|
||||
{
|
||||
#if EXCLUDE_KEYBOARD
|
||||
@@ -595,38 +555,41 @@ namespace keepass2android
|
||||
bool hasData = false;
|
||||
Keepass2android.Kbbridge.KeyboardDataBuilder kbdataBuilder = new Keepass2android.Kbbridge.KeyboardDataBuilder();
|
||||
|
||||
String[] keys = {PwDefs.UserNameField,
|
||||
String[] standardKeys = {PwDefs.UserNameField,
|
||||
PwDefs.PasswordField,
|
||||
Kp2aTotp.TotpKey,
|
||||
PwDefs.UrlField,
|
||||
PwDefs.NotesField,
|
||||
PwDefs.TitleField
|
||||
};
|
||||
int[] resIds = {Resource.String.entry_user_name,
|
||||
Resource.String.entry_password,
|
||||
0,
|
||||
Resource.String.entry_url,
|
||||
Resource.String.entry_comment,
|
||||
Resource.String.entry_title };
|
||||
|
||||
//add standard fields:
|
||||
int i = 0;
|
||||
foreach (string key in keys)
|
||||
foreach (string key in standardKeys)
|
||||
{
|
||||
String value = entry.OutputStrings.ReadSafe(key);
|
||||
|
||||
if (value.Length > 0)
|
||||
{
|
||||
kbdataBuilder.AddString(key, GetString(resIds[i]), value);
|
||||
kbdataBuilder.AddString(key, resIds[i] > 0 ? GetString(resIds[i]) : key, value);
|
||||
hasData = true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
//add additional fields:
|
||||
var totpData = new Kp2aTotp().TryGetTotpData(entry);
|
||||
foreach (var pair in entry.OutputStrings)
|
||||
{
|
||||
var key = pair.Key;
|
||||
var value = pair.Value.ReadString();
|
||||
|
||||
if (!PwDefs.IsStandardField(key))
|
||||
if (!standardKeys.Contains(key) && totpData?.InternalFields.Contains(key) != true)
|
||||
{
|
||||
kbdataBuilder.AddString(pair.Key, pair.Key, value);
|
||||
hasData = true;
|
||||
@@ -865,14 +828,9 @@ namespace keepass2android
|
||||
{
|
||||
//let's bring up the keyboard switching dialog.
|
||||
//Unfortunately this no longer works starting with Android 9 if our app is not in foreground.
|
||||
bool mustUseHelperActivity = false;
|
||||
if ((int)Build.VERSION.SdkInt >= 28)
|
||||
{
|
||||
ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
|
||||
ActivityManager.GetMyMemoryState(appProcessInfo);
|
||||
//at least on Samsung devices, we always need the helper activity
|
||||
mustUseHelperActivity = (appProcessInfo.Importance != Importance.Foreground) || (Build.Manufacturer != "Google");
|
||||
}
|
||||
//first it seemed to be required for Samsung mostly, but there are use cases where it is required for other devices as well.
|
||||
//Let's be sure and use the helper activity.
|
||||
bool mustUseHelperActivity = (int)Build.VERSION.SdkInt >= 28;
|
||||
if (mustUseHelperActivity)
|
||||
{
|
||||
try
|
||||
|
@@ -10,6 +10,7 @@ using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using AndroidX.Preference;
|
||||
using KeePass.Util.Spr;
|
||||
using keepass2android.services.AutofillBase;
|
||||
using keepass2android.services.AutofillBase.model;
|
||||
@@ -27,6 +28,32 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
Permission = "keepass2android." + AppNames.PackagePart + ".permission.Kp2aChooseAutofill")]
|
||||
public class ChooseForAutofillActivity : ChooseForAutofillActivityBase
|
||||
{
|
||||
public bool ActivateKeyboardWhenTotpPreference
|
||||
{
|
||||
get
|
||||
{
|
||||
return PreferenceManager.GetDefaultSharedPreferences(this)
|
||||
.GetBoolean("AutoFillTotp_prefs_ActivateKeyboard_key", false);
|
||||
}
|
||||
}
|
||||
public bool CopyTotpToClipboardPreference
|
||||
{
|
||||
get
|
||||
{
|
||||
return PreferenceManager.GetDefaultSharedPreferences(this)
|
||||
.GetBoolean("AutoFillTotp_prefs_CopyTotpToClipboard_key", true);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowNotificationPreference
|
||||
{
|
||||
get
|
||||
{
|
||||
return PreferenceManager.GetDefaultSharedPreferences(this)
|
||||
.GetBoolean("AutoFillTotp_prefs_ShowNotification_key", true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Intent GetQueryIntent(string requestedUrl, bool autoReturnFromQuery, bool useLastOpenedEntry)
|
||||
{
|
||||
if (useLastOpenedEntry && (App.Kp2a.LastOpenedEntry?.SearchUrl == requestedUrl))
|
||||
@@ -37,7 +64,33 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
//will return the results later
|
||||
Intent i = new Intent(this, typeof(SelectCurrentDbActivity));
|
||||
//don't show user notifications when an entry is opened.
|
||||
var task = new SearchUrlTask() { UrlToSearchFor = requestedUrl, ShowUserNotifications = ShowUserNotificationsMode.WhenTotp, AutoReturnFromQuery = autoReturnFromQuery, ActivateKeyboard = false };
|
||||
var task = new SearchUrlTask()
|
||||
{
|
||||
UrlToSearchFor = requestedUrl,
|
||||
AutoReturnFromQuery = autoReturnFromQuery
|
||||
};
|
||||
SetTotpDependantActionsOnTask(task);
|
||||
|
||||
task.ToIntent(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
private void SetTotpDependantActionsOnTask(SelectEntryTask task)
|
||||
{
|
||||
task.ShowUserNotifications =
|
||||
ShowNotificationPreference ? ActivationCondition.WhenTotp : ActivationCondition.Never;
|
||||
task.CopyTotpToClipboard = CopyTotpToClipboardPreference;
|
||||
task.ActivateKeyboard = ActivateKeyboardWhenTotpPreference
|
||||
? ActivationCondition.WhenTotp
|
||||
: ActivationCondition.Never;
|
||||
}
|
||||
|
||||
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 };
|
||||
SetTotpDependantActionsOnTask(task);
|
||||
task.ToIntent(i);
|
||||
return i;
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using Android;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Preferences;
|
||||
using Android.Runtime;
|
||||
using keepass2android.services.AutofillBase;
|
||||
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)
|
||||
return new List<FilledAutofillFieldCollection<ViewNodeInputField>>();
|
||||
return new Dictionary<PwEntryOutput, FilledAutofillFieldCollection<ViewNodeInputField>>();
|
||||
var foundEntries = (ShareUrlResults.GetSearchResultsForUrl(query)?.Entries ?? new PwObjectList<PwEntry>())
|
||||
.Select(e => new PwEntryOutput(e, App.Kp2a.FindDatabaseForElement(e)))
|
||||
.ToList();
|
||||
|
||||
if (App.Kp2a.LastOpenedEntry?.SearchUrl == query)
|
||||
{
|
||||
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
|
||||
//so take only 1:
|
||||
return foundEntries.Take(1).Select(e => ChooseForAutofillActivity.GetFilledAutofillFieldCollectionFromEntry(e, this))
|
||||
.ToList();
|
||||
int numDisableDatasets = 0;
|
||||
if (!PreferenceManager.GetDefaultSharedPreferences(this)
|
||||
.GetBoolean(GetString(Resource.String.NoAutofillDisabling_key), false))
|
||||
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)
|
||||
|
@@ -9,6 +9,7 @@ using Android.Views;
|
||||
using Android.Widget;
|
||||
using keepass2android.services.AutofillBase;
|
||||
using keepass2android.services.Kp2aAutofill;
|
||||
using KeePassLib;
|
||||
|
||||
namespace keepass2android.services
|
||||
{
|
||||
@@ -29,16 +30,14 @@ namespace keepass2android.services
|
||||
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)
|
||||
{
|
||||
Intent intent = new Intent(context, typeof(ChooseForAutofillActivity));
|
||||
intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryString, query);
|
||||
intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryDomainString, queryDomain);
|
||||
intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryPackageString, queryPackage);
|
||||
intent.PutExtra(ChooseForAutofillActivityBase.ExtraUuidString, entryUuid.ToHexString());
|
||||
intent.PutExtra(ChooseForAutofillActivityBase.ExtraDisplayWarning, (int)warning);
|
||||
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,
|
||||
|
Reference in New Issue
Block a user