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/java/KP2AKdbLibrary/app/.cxx
|
||||||
/src/ActionViewFilterTest
|
/src/ActionViewFilterTest
|
||||||
/docs/gdrive-verification
|
/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 KeyEntry = "entry";
|
||||||
public const String KeyRefreshPos = "refresh_pos";
|
public const String KeyRefreshPos = "refresh_pos";
|
||||||
|
public const String KeyEntryHistoryIndex = "entry_history_index";
|
||||||
public const String KeyActivateKeyboard = "activate_keyboard";
|
public const String KeyActivateKeyboard = "activate_keyboard";
|
||||||
public const String KeyGroupFullPath = "groupfullpath_key";
|
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));
|
Intent i = new Intent(act, typeof(EntryActivity));
|
||||||
|
|
||||||
var db = App.Kp2a.FindDatabaseForElement(pw);
|
var db = App.Kp2a.FindDatabaseForElement(pw);
|
||||||
i.PutExtra(KeyEntry, new ElementAndDatabaseId(db, pw).FullId);
|
i.PutExtra(KeyEntry, new ElementAndDatabaseId(db, pw).FullId);
|
||||||
i.PutExtra(KeyRefreshPos, pos);
|
i.PutExtra(KeyRefreshPos, pos);
|
||||||
|
i.PutExtra(KeyEntryHistoryIndex, historyIndex);
|
||||||
|
|
||||||
if (App.Kp2a.CurrentDb != db)
|
if (App.Kp2a.CurrentDb != db)
|
||||||
{
|
{
|
||||||
@@ -135,7 +137,10 @@ namespace keepass2android
|
|||||||
_activityDesign = new ActivityDesign(this);
|
_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;
|
public PwEntry Entry;
|
||||||
|
//if _historyIndex >=0, _historyParentEntry stores the PwEntry which contains the history entry "Entry"
|
||||||
|
private PwEntry _historyParentEntry;
|
||||||
|
|
||||||
private PasswordFont _passwordFont = new PasswordFont();
|
private PasswordFont _passwordFont = new PasswordFont();
|
||||||
|
|
||||||
@@ -183,7 +188,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
protected void SetupEditButtons() {
|
protected void SetupEditButtons() {
|
||||||
View edit = FindViewById(Resource.Id.entry_edit);
|
View edit = FindViewById(Resource.Id.entry_edit);
|
||||||
if (App.Kp2a.CurrentDb.CanWrite)
|
if (App.Kp2a.CurrentDb.CanWrite && _historyIndex < 0)
|
||||||
{
|
{
|
||||||
edit.Visibility = ViewStates.Visible;
|
edit.Visibility = ViewStates.Visible;
|
||||||
edit.Click += (sender, e) =>
|
edit.Click += (sender, e) =>
|
||||||
@@ -421,16 +426,41 @@ namespace keepass2android
|
|||||||
ElementAndDatabaseId dbAndElementId = new ElementAndDatabaseId(i.GetStringExtra(KeyEntry));
|
ElementAndDatabaseId dbAndElementId = new ElementAndDatabaseId(i.GetStringExtra(KeyEntry));
|
||||||
PwUuid uuid = new PwUuid(MemUtil.HexStringToByteArray(dbAndElementId.ElementIdString));
|
PwUuid uuid = new PwUuid(MemUtil.HexStringToByteArray(dbAndElementId.ElementIdString));
|
||||||
_pos = i.GetIntExtra(KeyRefreshPos, -1);
|
_pos = i.GetIntExtra(KeyRefreshPos, -1);
|
||||||
|
_historyIndex = i.GetIntExtra(KeyEntryHistoryIndex, -1);
|
||||||
|
|
||||||
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
|
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
|
||||||
|
|
||||||
Entry = db.EntriesById[uuid];
|
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);
|
ActivityCompat.InvalidateOptionsMenu(this);
|
||||||
|
|
||||||
// Update last access time.
|
|
||||||
Entry.Touch(false);
|
|
||||||
|
|
||||||
if (PwDefs.IsTanEntry(Entry)
|
if (PwDefs.IsTanEntry(Entry)
|
||||||
&& prefs.GetBoolean(GetString(Resource.String.TanExpiresOnUse_key), Resources.GetBoolean(Resource.Boolean.TanExpiresOnUse_default))
|
&& prefs.GetBoolean(GetString(Resource.String.TanExpiresOnUse_key), Resources.GetBoolean(Resource.Boolean.TanExpiresOnUse_default))
|
||||||
@@ -462,7 +492,39 @@ namespace keepass2android
|
|||||||
AppTask.CompleteOnCreateEntryActivity(this);
|
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);
|
Intent i = new Intent(Strings.ActionOpenEntry);
|
||||||
i.PutExtra(Strings.ExtraSender, PackageName);
|
i.PutExtra(Strings.ExtraSender, PackageName);
|
||||||
@@ -854,12 +916,41 @@ namespace keepass2android
|
|||||||
|
|
||||||
PopulateBinaries();
|
PopulateBinaries();
|
||||||
|
|
||||||
|
PopulatePreviousVersions();
|
||||||
|
|
||||||
SetPasswordStyle();
|
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();
|
NotifyPluginsOnClose();
|
||||||
if (_pluginActionReceiver != null)
|
if (_pluginActionReceiver != null)
|
||||||
@@ -1083,6 +1174,7 @@ namespace keepass2android
|
|||||||
private ExportBinaryProcessManager _exportBinaryProcessManager;
|
private ExportBinaryProcessManager _exportBinaryProcessManager;
|
||||||
private bool _showPasswordDefault;
|
private bool _showPasswordDefault;
|
||||||
private bool _showTotpDefault;
|
private bool _showTotpDefault;
|
||||||
|
private int _historyIndex;
|
||||||
|
|
||||||
protected override void OnSaveInstanceState(Bundle outState)
|
protected override void OnSaveInstanceState(Bundle outState)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -797,7 +797,7 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reload() {
|
public void Reload() {
|
||||||
//this reload ìs necessary to overcome a strange problem with the extra string fields which get lost
|
//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?
|
//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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/entry_table"
|
android:id="@+id/entry_table"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
@@ -348,32 +349,7 @@
|
|||||||
style="@style/EntryItem" />
|
style="@style/EntryItem" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</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
|
<LinearLayout
|
||||||
android:id="@+id/entryfield_container_expires"
|
android:id="@+id/entryfield_container_expires"
|
||||||
style="@style/EntryEditSingleLine_container">
|
style="@style/EntryEditSingleLine_container">
|
||||||
@@ -400,4 +376,77 @@
|
|||||||
style="@style/EntryItem" />
|
style="@style/EntryItem" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</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>
|
</LinearLayout>
|
||||||
@@ -136,6 +136,7 @@
|
|||||||
<string name="entry_user_name">User Name</string>
|
<string name="entry_user_name">User Name</string>
|
||||||
<string name="entry_extra_strings">Extra string fields</string>
|
<string name="entry_extra_strings">Extra string fields</string>
|
||||||
<string name="entry_binaries">File attachments</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_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_group">Error creating group.</string>
|
||||||
<string name="error_could_not_create_parent">Could not create parent directory.</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="child_db_Enabled_title">Open automatically</string>
|
||||||
<string name="database_file_heading">Database file</string>
|
<string name="database_file_heading">Database file</string>
|
||||||
<string name="if_device_text">Enable for %1$s</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_name">Database unlocked</string>
|
||||||
<string name="DbUnlockedChannel_desc">Notification about the database being unlocked</string>
|
<string name="DbUnlockedChannel_desc">Notification about the database being unlocked</string>
|
||||||
|
|||||||
@@ -1138,7 +1138,8 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
foreach (var db in OpenDatabases)
|
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 db;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -399,6 +399,7 @@
|
|||||||
<AndroidResource Include="Resources\drawable-mdpi\ic_arrow_back_white_24dp.png" />
|
<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_cross.png" />
|
||||||
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_attachments.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_comments.png" />
|
||||||
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_created.png" />
|
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_created.png" />
|
||||||
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_expires.png" />
|
<AndroidResource Include="Resources\drawable-mdpi\ic_entry_expires.png" />
|
||||||
@@ -987,6 +988,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_attachments.png" />
|
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_attachments.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_history.png" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_comments.png" />
|
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_comments.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user