can now view entry history and remove/restore previous versions. closes #298
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -172,3 +172,4 @@ src/java/Keepass2AndroidPluginSDK2/build/generated/mockable-Google-Inc.-Google-A
|
||||
/src/java/KP2AKdbLibrary/app/.cxx
|
||||
/src/ActionViewFilterTest
|
||||
/docs/gdrive-verification
|
||||
/src/MegaTest
|
||||
|
8518
src/Kp2aBusinessLogic/Resources/Resource.Designer.cs
generated
8518
src/Kp2aBusinessLogic/Resources/Resource.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
@@ -93,6 +93,7 @@ namespace keepass2android
|
||||
{
|
||||
public const String KeyEntry = "entry";
|
||||
public const String KeyRefreshPos = "refresh_pos";
|
||||
public const String KeyEntryHistoryIndex = "entry_history_index";
|
||||
public const String KeyActivateKeyboard = "activate_keyboard";
|
||||
public const String KeyGroupFullPath = "groupfullpath_key";
|
||||
|
||||
@@ -101,13 +102,14 @@ namespace keepass2android
|
||||
|
||||
|
||||
|
||||
public static void Launch(Activity act, PwEntry pw, int pos, AppTask appTask, ActivityFlags? flags = null)
|
||||
public static void Launch(Activity act, PwEntry pw, int pos, AppTask appTask, ActivityFlags? flags = null, int historyIndex=-1)
|
||||
{
|
||||
Intent i = new Intent(act, typeof(EntryActivity));
|
||||
|
||||
var db = App.Kp2a.FindDatabaseForElement(pw);
|
||||
i.PutExtra(KeyEntry, new ElementAndDatabaseId(db, pw).FullId);
|
||||
i.PutExtra(KeyRefreshPos, pos);
|
||||
i.PutExtra(KeyEntryHistoryIndex, historyIndex);
|
||||
|
||||
if (App.Kp2a.CurrentDb != db)
|
||||
{
|
||||
@@ -135,7 +137,10 @@ namespace keepass2android
|
||||
_activityDesign = new ActivityDesign(this);
|
||||
}
|
||||
|
||||
//this is the entry we display. Note that it might be an element from a History list in case _historyIndex >= 0
|
||||
public PwEntry Entry;
|
||||
//if _historyIndex >=0, _historyParentEntry stores the PwEntry which contains the history entry "Entry"
|
||||
private PwEntry _historyParentEntry;
|
||||
|
||||
private PasswordFont _passwordFont = new PasswordFont();
|
||||
|
||||
@@ -183,7 +188,7 @@ namespace keepass2android
|
||||
|
||||
protected void SetupEditButtons() {
|
||||
View edit = FindViewById(Resource.Id.entry_edit);
|
||||
if (App.Kp2a.CurrentDb.CanWrite)
|
||||
if (App.Kp2a.CurrentDb.CanWrite && _historyIndex < 0)
|
||||
{
|
||||
edit.Visibility = ViewStates.Visible;
|
||||
edit.Click += (sender, e) =>
|
||||
@@ -421,16 +426,41 @@ namespace keepass2android
|
||||
ElementAndDatabaseId dbAndElementId = new ElementAndDatabaseId(i.GetStringExtra(KeyEntry));
|
||||
PwUuid uuid = new PwUuid(MemUtil.HexStringToByteArray(dbAndElementId.ElementIdString));
|
||||
_pos = i.GetIntExtra(KeyRefreshPos, -1);
|
||||
_historyIndex = i.GetIntExtra(KeyEntryHistoryIndex, -1);
|
||||
|
||||
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
|
||||
|
||||
Entry = db.EntriesById[uuid];
|
||||
|
||||
// Refresh Menu contents in case onCreateMenuOptions was called before Entry was set
|
||||
|
||||
if (_historyIndex >= 0 && _historyIndex < Entry.History.UCount)
|
||||
{
|
||||
_historyParentEntry = Entry;
|
||||
Entry = Entry.History.Skip(_historyIndex).First();
|
||||
FindViewById<Button>(Resource.Id.btn_restore_history).Click += (sender, args) =>
|
||||
{
|
||||
RestoreFromHistory();
|
||||
SaveHistoryChangeAndFinish();
|
||||
};
|
||||
FindViewById<Button>(Resource.Id.btn_remove_history).Click += (sender, args) =>
|
||||
{
|
||||
RemoveFromHistory();
|
||||
SaveHistoryChangeAndFinish();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update last access time.
|
||||
Entry.Touch(false);
|
||||
FindViewById<Button>(Resource.Id.btn_restore_history).Visibility = ViewStates.Gone;
|
||||
FindViewById<Button>(Resource.Id.btn_remove_history).Visibility = ViewStates.Gone;
|
||||
}
|
||||
|
||||
// Refresh Menu contents in case onCreateMenuOptions was called before Entry was set
|
||||
ActivityCompat.InvalidateOptionsMenu(this);
|
||||
|
||||
// Update last access time.
|
||||
Entry.Touch(false);
|
||||
|
||||
|
||||
if (PwDefs.IsTanEntry(Entry)
|
||||
&& prefs.GetBoolean(GetString(Resource.String.TanExpiresOnUse_key), Resources.GetBoolean(Resource.Boolean.TanExpiresOnUse_default))
|
||||
@@ -462,7 +492,39 @@ namespace keepass2android
|
||||
AppTask.CompleteOnCreateEntryActivity(this);
|
||||
}
|
||||
|
||||
private void NotifyPluginsOnOpen()
|
||||
private void RemoveFromHistory()
|
||||
{
|
||||
_historyParentEntry.History.RemoveAt((uint)_historyIndex);
|
||||
_historyParentEntry.Touch(true, false);
|
||||
}
|
||||
|
||||
private void RestoreFromHistory()
|
||||
{
|
||||
var db = App.Kp2a.FindDatabaseForElement(_historyParentEntry);
|
||||
_historyParentEntry.RestoreFromBackup((uint)_historyIndex, db.KpDatabase);
|
||||
_historyParentEntry.Touch(true, false);
|
||||
}
|
||||
|
||||
private void SaveHistoryChangeAndFinish()
|
||||
{
|
||||
PwGroup parent = _historyParentEntry.ParentGroup;
|
||||
if (parent != null)
|
||||
{
|
||||
// Mark parent group dirty (title might have changed etc.)
|
||||
App.Kp2a.DirtyGroups.Add(parent);
|
||||
}
|
||||
|
||||
var saveTask = new SaveDb(this, App.Kp2a, App.Kp2a.FindDatabaseForElement(Entry), new ActionOnFinish(this, (success, message, activity) =>
|
||||
{
|
||||
activity.SetResult(KeePass.ExitRefresh);
|
||||
activity.Finish();
|
||||
}));
|
||||
|
||||
ProgressTask pt = new ProgressTask(App.Kp2a, this, saveTask);
|
||||
pt.Run();
|
||||
}
|
||||
|
||||
private void NotifyPluginsOnOpen()
|
||||
{
|
||||
Intent i = new Intent(Strings.ActionOpenEntry);
|
||||
i.PutExtra(Strings.ExtraSender, PackageName);
|
||||
@@ -854,12 +916,41 @@ namespace keepass2android
|
||||
|
||||
PopulateBinaries();
|
||||
|
||||
PopulatePreviousVersions();
|
||||
|
||||
SetPasswordStyle();
|
||||
}
|
||||
|
||||
|
||||
private void PopulatePreviousVersions()
|
||||
{
|
||||
ViewGroup historyGroup = (ViewGroup)FindViewById(Resource.Id.previous_versions);
|
||||
int index = 0;
|
||||
foreach (var previousVersion in Entry.History)
|
||||
{
|
||||
|
||||
|
||||
protected override void OnDestroy()
|
||||
|
||||
Button btn = new Button(this);
|
||||
btn.Text = getDateTime(previousVersion.LastModificationTime);
|
||||
|
||||
//copy variable from outer scope for capturing it below.
|
||||
var index1 = index;
|
||||
btn.Click += (sender, args) =>
|
||||
{
|
||||
EntryActivity.Launch(this, this.Entry, this._pos, this.AppTask, null, index1);
|
||||
};
|
||||
|
||||
historyGroup.AddView(btn);
|
||||
|
||||
index++;
|
||||
|
||||
|
||||
}
|
||||
FindViewById(Resource.Id.entry_history_container).Visibility = Entry.History.Any() ? ViewStates.Visible : ViewStates.Gone;
|
||||
}
|
||||
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
NotifyPluginsOnClose();
|
||||
if (_pluginActionReceiver != null)
|
||||
@@ -1083,6 +1174,7 @@ namespace keepass2android
|
||||
private ExportBinaryProcessManager _exportBinaryProcessManager;
|
||||
private bool _showPasswordDefault;
|
||||
private bool _showTotpDefault;
|
||||
private int _historyIndex;
|
||||
|
||||
protected override void OnSaveInstanceState(Bundle outState)
|
||||
{
|
||||
|
@@ -797,7 +797,7 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void Reload() {
|
||||
//this reload ìs necessary to overcome a strange problem with the extra string fields which get lost
|
||||
//somehow after re-creating the activity. Maybe a Mono for Android bug?
|
||||
|
BIN
src/keepass2android/Resources/drawable-mdpi/ic_entry_history.png
Normal file
BIN
src/keepass2android/Resources/drawable-mdpi/ic_entry_history.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/entry_table"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
@@ -348,32 +349,7 @@
|
||||
style="@style/EntryItem" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/entryfield_container_modified"
|
||||
style="@style/EntryEditSingleLine_container">
|
||||
<ImageView
|
||||
style="@style/EntryEditSingleLine_ImageView"
|
||||
android:src="@drawable/ic_entry_modified" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="vertical">
|
||||
<!-- Modified -->
|
||||
<TextView
|
||||
android:id="@+id/entry_modified_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_modified"
|
||||
style="@style/EntryFieldHeader" />
|
||||
<TextView
|
||||
android:id="@+id/entry_modified"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/EntryItem" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/entryfield_container_expires"
|
||||
style="@style/EntryEditSingleLine_container">
|
||||
@@ -400,4 +376,77 @@
|
||||
style="@style/EntryItem" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/entryfield_container_modified"
|
||||
style="@style/EntryEditSingleLine_container">
|
||||
<ImageView
|
||||
style="@style/EntryEditSingleLine_ImageView"
|
||||
android:src="@drawable/ic_entry_modified" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="vertical">
|
||||
<!-- Modified -->
|
||||
<TextView
|
||||
android:id="@+id/entry_modified_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_modified"
|
||||
style="@style/EntryFieldHeader" />
|
||||
<TextView
|
||||
android:id="@+id/entry_modified"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/EntryItem" />
|
||||
|
||||
<Button android:id="@+id/btn_restore_history"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@drawable/ic_entry_history"
|
||||
android:text="@string/restore_history"
|
||||
|
||||
/>
|
||||
|
||||
<Button android:id="@+id/btn_remove_history"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@android:drawable/ic_menu_delete"
|
||||
android:text="@string/remove_history"
|
||||
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/entry_history_container"
|
||||
style="@style/EntryEditSingleLine_container">
|
||||
<ImageView
|
||||
style="@style/EntryEditSingleLine_ImageView"
|
||||
android:src="@drawable/ic_entry_history" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- previous versions-->
|
||||
<TextView
|
||||
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_history"
|
||||
style="@style/EntryFieldHeader" />
|
||||
<LinearLayout
|
||||
android:id="@+id/previous_versions"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
@@ -136,6 +136,7 @@
|
||||
<string name="entry_user_name">User Name</string>
|
||||
<string name="entry_extra_strings">Extra string fields</string>
|
||||
<string name="entry_binaries">File attachments</string>
|
||||
<string name="entry_history">Previous versions</string>
|
||||
<string name="error_can_not_handle_uri">Keepass2Android cannot handle this URI.</string>
|
||||
<string name="error_could_not_create_group">Error creating group.</string>
|
||||
<string name="error_could_not_create_parent">Could not create parent directory.</string>
|
||||
@@ -828,8 +829,9 @@
|
||||
<string name="child_db_Enabled_title">Open automatically</string>
|
||||
<string name="database_file_heading">Database file</string>
|
||||
<string name="if_device_text">Enable for %1$s</string>
|
||||
|
||||
|
||||
<string name="restore_history">Restore this version</string>
|
||||
<string name="remove_history">Remove this version</string>
|
||||
|
||||
<string name="DbUnlockedChannel_name">Database unlocked</string>
|
||||
<string name="DbUnlockedChannel_desc">Notification about the database being unlocked</string>
|
||||
|
@@ -1138,7 +1138,8 @@ namespace keepass2android
|
||||
{
|
||||
foreach (var db in OpenDatabases)
|
||||
{
|
||||
if (db.Elements.Contains(element))
|
||||
//we compare UUIDs and not by reference. this is more robust and works with history items as well
|
||||
if (db.Elements.Any(e => e.Uuid?.Equals(element.Uuid) == true))
|
||||
return db;
|
||||
}
|
||||
return null;
|
||||
|
@@ -399,6 +399,7 @@
|
||||
<AndroidResource Include="Resources\drawable-mdpi\ic_arrow_back_white_24dp.png" />
|
||||
<AndroidResource Include="Resources\drawable-mdpi\ic_cross.png" />
|
||||
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_attachments.png" />
|
||||
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_history.png" />
|
||||
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_comments.png" />
|
||||
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_created.png" />
|
||||
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_expires.png" />
|
||||
@@ -987,6 +988,9 @@
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_attachments.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_history.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_comments.png" />
|
||||
</ItemGroup>
|
||||
|
Reference in New Issue
Block a user