move TOTP field up in EntryActivity and add a progress bar to indicate the time left for using the TOTP

closes https://github.com/PhilippC/keepass2android/issues/2315
This commit is contained in:
Philipp Crocoll
2024-01-05 09:01:29 +01:00
parent e189776ba9
commit 4fea731c87
6 changed files with 132 additions and 11 deletions

View File

@@ -32,6 +32,7 @@ using Android.Text.Method;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading.Tasks;
using Android.Content.PM; using Android.Content.PM;
using Android.Webkit; using Android.Webkit;
using Android.Graphics; using Android.Graphics;
@@ -49,7 +50,9 @@ using PluginTOTP;
using File = Java.IO.File; using File = Java.IO.File;
using Uri = Android.Net.Uri; using Uri = Android.Net.Uri;
using keepass2android.fileselect; using keepass2android.fileselect;
using KeeTrayTOTP.Libraries;
using Boolean = Java.Lang.Boolean; using Boolean = Java.Lang.Boolean;
using Android.Util;
namespace keepass2android namespace keepass2android
{ {
@@ -286,6 +289,8 @@ namespace keepass2android
extraGroup.AddView(view.View); extraGroup.AddView(view.View);
} }
SetPasswordStyle();
//update the Entry output in the App database and notify the CopyToClipboard service //update the Entry output in the App database and notify the CopyToClipboard service
if (App.Kp2a.LastOpenedEntry != null) if (App.Kp2a.LastOpenedEntry != null)
@@ -665,7 +670,7 @@ namespace keepass2android
EditModeBase editMode = new DefaultEdit(); EditModeBase editMode = new DefaultEdit();
if (KpEntryTemplatedEdit.IsTemplated(App.Kp2a.CurrentDb, this.Entry)) if (KpEntryTemplatedEdit.IsTemplated(App.Kp2a.CurrentDb, this.Entry))
editMode = new KpEntryTemplatedEdit(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)) if (editMode.IsVisible(key))
{ {
@@ -841,7 +846,7 @@ namespace keepass2android
{ {
if (!_showPassword.ContainsKey(protectedTextView)) 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}; var protectedTextviewGroup = new ProtectedTextviewGroup { ProtectedField = protectedTextView, VisibleProtectedField = visibleTextView};
_protectedTextViews.Add(protectedTextviewGroup); _protectedTextViews.Add(protectedTextviewGroup);
@@ -947,11 +952,13 @@ namespace keepass2android
PopulateStandardText(Resource.Id.entry_user_name, Resource.Id.entryfield_container_username, PwDefs.UserNameField); PopulateStandardText(Resource.Id.entry_user_name, Resource.Id.entryfield_container_username, PwDefs.UserNameField);
PopulateStandardText(Resource.Id.entry_url, Resource.Id.entryfield_container_url, PwDefs.UrlField); PopulateStandardText(Resource.Id.entry_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(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); FindViewById (Resource.Id.entry_group_name), KeyGroupFullPath);
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.username_container), RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.username_container),
@@ -962,9 +969,11 @@ namespace keepass2android
.Add(new GotoUrlMenuItem(this, PwDefs.UrlField)); .Add(new GotoUrlMenuItem(this, PwDefs.UrlField));
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.password_container), RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.password_container),
FindViewById(Resource.Id.password_vdots), PwDefs.PasswordField); 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)); PopulateText(Resource.Id.entry_modified, Resource.Id.entryfield_container_modified, getDateTime(Entry.LastModificationTime));
if (Entry.Expires) if (Entry.Expires)
@@ -991,6 +1000,40 @@ namespace keepass2android
SetPasswordStyle(); 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() private void PopulatePreviousVersions()
{ {
@@ -1043,7 +1086,7 @@ namespace keepass2android
} }
private List<IPopupMenuItem> RegisterTextPopup(View container, View anchor, string fieldKey) 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) private List<IPopupMenuItem> RegisterTextPopup(View container, View anchor, string fieldKey, bool isProtected)
@@ -1056,7 +1099,12 @@ namespace keepass2android
popupItems.Add(new CopyToClipboardPopupMenuIcon(this, _stringViews[fieldKey], isProtected)); popupItems.Add(new CopyToClipboardPopupMenuIcon(this, _stringViews[fieldKey], isProtected));
if (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)); popupItems.Add(new ToggleVisibilityPopupMenuItem(this, valueView));
} }
@@ -1283,11 +1331,16 @@ namespace keepass2android
return base.OnPrepareOptionsMenu(menu); 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); IMenuItem togglePassword = _menu.FindItem(Resource.Id.menu_toggle_pass);
if (_showPassword.Values.All(x => x)) if (_showPassword.Values.All(x => x))
@@ -1324,7 +1377,9 @@ namespace keepass2android
ClearCache(); ClearCache();
base.OnResume(); base.OnResume();
_activityDesign.ReapplyTheme(); _activityDesign.ReapplyTheme();
} isPaused = false;
Task.Run(UpdateTotpCountdown);
}
public void ClearCache() public void ClearCache()
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -184,6 +184,68 @@
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>
</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 <LinearLayout
android:id="@+id/entryfield_container_comment" android:id="@+id/entryfield_container_comment"
style="@style/EntryEditSingleLine_container"> style="@style/EntryEditSingleLine_container">

View File

@@ -599,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="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="ErrorUpdatingChalAuxFile">Error updating OTP auxiliary file!</string>
<string name="TrayTotp_SeedField_title">TOTP Seed field name</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_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_title">TOTP Settings field name</string>
<string name="TrayTotp_SettingsField_summary">Enter the field name of the settings field for TrayTotp here.</string> <string name="TrayTotp_SettingsField_summary">Enter the field name of the settings field for TrayTotp here.</string>

View File

@@ -1985,6 +1985,9 @@
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_pcloudall.png" /> <AndroidResource Include="Resources\drawable-mdpi\ic_storage_pcloudall.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_totp.png" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.