Implemented UI for sync

Fixed bugs
This commit is contained in:
Philipp Crocoll 2013-07-11 17:27:10 +02:00
parent 16c08cbe8a
commit deeaa673a5
14 changed files with 630 additions and 548 deletions

View File

@ -27,10 +27,16 @@ namespace keepass2android.Io
{
if (!ioc.IsLocalFile())
return false;
DateTime previousDate;
if (!DateTime.TryParse(previousFileVersion, out previousDate))
if (previousFileVersion == null)
return false;
return File.GetLastWriteTimeUtc(ioc.Path) > previousDate;
DateTime previousDate;
if (!DateTime.TryParse(previousFileVersion, CultureInfo.InvariantCulture, DateTimeStyles.None, out previousDate))
return false;
DateTime currentModificationDate = File.GetLastWriteTimeUtc(ioc.Path);
TimeSpan diff = currentModificationDate - previousDate;
return diff > TimeSpan.FromSeconds(1);
//don't use > operator because milliseconds are truncated
return File.GetLastWriteTimeUtc(ioc.Path) - previousDate >= TimeSpan.FromSeconds(1);
}

View File

@ -43,6 +43,7 @@ namespace keepass2android.Io
/// </summary>
/// Note: This function may return false even if the file might have changed. The function
/// should focus on being fast and cheap instead of doing things like hashing or downloading a full file.
/// previousFileVersion may be null to indicate no previous version is known.
/// <returns>Returns true if a change was detected, false otherwise.</returns>
bool CheckForFileChangeFast(IOConnectionInfo ioc , string previousFileVersion);

View File

@ -60,7 +60,7 @@ namespace keepass2android
{
_handler.Post(() =>
{
if (String.IsNullOrEmpty(submessage))
if (!String.IsNullOrEmpty(submessage))
{
_progressDialog.SetMessage(_message + " (" + submessage + ")");
}

View File

@ -29,7 +29,7 @@ namespace keepass2android
ParsingDatabase,
CheckingTargetFileForChanges,
TitleSyncQuestion,
MessageSyncQuestions,
MessageSyncQuestion,
SynchronizingDatabase
}
}

View File

@ -75,8 +75,9 @@ namespace keepass2android
if (fileStorage.CheckForFileChangeFast(ioc, _app.GetDb().LastFileVersion) //first try to use the fast change detection
|| (FileHashChanged(ioc, _app.GetDb().KpDatabase.HashOfFileOnDisk))) //if that fails, hash the file and compare:
{
//ask user...
_app.AskYesNoCancel(UiStringKey.TitleSyncQuestion, UiStringKey.MessageSyncQuestions,
_app.AskYesNoCancel(UiStringKey.TitleSyncQuestion, UiStringKey.MessageSyncQuestion,
//yes = sync
(sender, args) =>
{
@ -85,7 +86,6 @@ namespace keepass2android
//note: when synced, the file might be downloaded once again from the server. Caching the data
//in the hashing function would solve this but increases complexity. I currently assume the files are
//small.
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase));
MergeIn(fileStorage, ioc);
PerformSaveWithoutCheck(fileStorage, ioc);
Finish(true);
@ -108,8 +108,6 @@ namespace keepass2android
},
_ctx
);
}
else
{
@ -125,7 +123,8 @@ namespace keepass2android
bSuccess = false;
}
*/
Finish (false, e.ToString());
Kp2aLog.Log("Error while saving: "+e.ToString());
Finish (false, e.Message);
return;
}
}
@ -138,7 +137,7 @@ namespace keepass2android
try
{
_workerThread = new Thread(runHandler);
_workerThread.Run();
_workerThread.Start();
}
catch (Exception e)
{
@ -156,6 +155,8 @@ namespace keepass2android
private void MergeIn(IFileStorage fileStorage, IOConnectionInfo ioc)
{
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase));
PwDatabase pwImp = new PwDatabase();
PwDatabase pwDatabase = _app.GetDb().KpDatabase;
pwImp.New(new IOConnectionInfo(), pwDatabase.MasterKey);
@ -170,6 +171,7 @@ namespace keepass2android
private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
{
StatusLogger.UpdateSubMessage("");
_app.GetDb().SaveData(_ctx);
_app.GetDb().LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
}

View File

@ -228,6 +228,14 @@ namespace Kp2aUnitTests
Assert.Fail("TODO: Test ");
}
[TestMethod]
public void TestReloadWhenCancelSync()
{
//when a change is detected and the user cancels saving, the app should display the "file was modified - reload?" question.
Assert.Fail("TODO: Test ");
}
[TestMethod]
public void TestSaveAsWhenSyncError()
{

View File

@ -42,6 +42,10 @@ namespace keepass2android
if (TimeoutHelper.CheckShutdown(this, _ioc))
return;
//todo: it seems like OnResume can be called after dismissing a dialog, e.g. the Delete-permanently-Dialog.
//in this case the following check might run in parallel with the check performed during the SaveDb check (triggered after the
//aforementioned dialog is closed) which can cause odd behavior. However, this is a rare case and hard to resolve so this is currently
//accepted. (If the user clicks cancel on the reload-dialog, everything will work.)
App.Kp2a.CheckForOpenFileChanged(this);
}

View File

@ -53,6 +53,7 @@ namespace keepass2android
if (TimeoutHelper.CheckShutdown(this, _ioc))
return;
//todo: see LockCloseActivity.OnResume
App.Kp2a.CheckForOpenFileChanged(this);
}

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,8 @@
<string name="LastInfoVersionCode_key">LastInfoVersion</string>
<string name="UseFileTransactions_key">UseFileTransactions</string>
<string name="CheckForFileChangesOnSave_key">CheckForFileChangesOnSave</string>
<string name="MarketURL">market://details?id=</string>
<string name="SuggestionsURL">https://keepass2android.codeplex.com/workitem/list/basic</string>
<string name="TranslationURL">http://crowdin.net/project/keepass2android</string>

View File

@ -218,6 +218,9 @@
<string name="credentials_dialog_title">Enter server credentials</string>
<string name="UseFileTransactions_title">File transactions</string>
<string name="UseFileTransactions_summary">Use file transactions for writing databases</string>
<string name="CheckForFileChangesOnSave_title">Check for modifications</string>
<string name="CheckForFileChangesOnSave_summary">Check whether the file was modified externally before saving changes.</string>
<string name="ShowCopyToClipboardNotification_title">Clipboard notifications</string>
<string name="ShowCopyToClipboardNotification_summary">Make username and password accessible through the notification bar and clipboard. Beware of password sniffers!</string>
<string name="ShowKp2aKeyboardNotification_title">KP2A keyboard notification</string>
@ -251,9 +254,23 @@
<string name="DecodingDatabase">Decoding database…</string>
<string name="ParsingDatabase">Parsing database…</string>
<string name="CheckingTargetFileForChanges">Checking target file for changes…</string>
<string name="ChangeLog_title">Change log</string>
<string name="ChangeLog_0_8_3"><b>Version 0.8.3</b>\n
<string name="TitleSyncQuestion">Merge changes?</string>
<string name="MessageSyncQuestion">The database file was modified externally. Do you want to load and merge the changes before saving? Select No if you want to overwrite the external changes.</string>
<string name="SynchronizingDatabase">Merging changes…</string>
<string name="ChangeLog_title">Change log</string>
<string name="ChangeLog_0_8_4">
<b>Version 0.8.4</b>\n
* External changes are detected and merged when saving\n
* Improved loading performance\n
* Improved search toolbar with suggestions\n
? New app logo!\n
? Added support for .kdbp format for faster loading/saving\n
? Improved editing of extra strings and hidden display when protected\n
Thanks to Alex Vallat for his code contributions!\n
Thanks to Niki Hüttner (www.close-cut.de) for the new logo!\n
</string>
<string name="ChangeLog_0_8_3"><b>Version 0.8.3</b>\n
* Username/TAN index displayed in entry list (see settings)\n
* Entries can be created if search from browser doesn\'t return results\n
* KP2A keyboard provides possibility to search for credentials for current app\n

View File

@ -148,7 +148,14 @@
android:summary="@string/UseFileTransactions_summary"
android:defaultValue="true"
android:title="@string/UseFileTransactions_title"
android:key="@string/UseFileTransactions_key" />
android:key="@string/UseFileTransactions_key" />
<CheckBoxPreference
android:enabled="true"
android:persistent="true"
android:summary="@string/CheckForFileChangesOnSave_summary"
android:defaultValue="true"
android:title="@string/CheckForFileChangesOnSave_title"
android:key="@string/CheckForFileChangesOnSave_key" />
</PreferenceScreen>
</PreferenceScreen>

View File

@ -103,6 +103,8 @@ namespace keepass2android
return prefs.GetBoolean(ctx.Resources.GetString(Resource.String.keyfile_key), ctx.Resources.GetBoolean(Resource.Boolean.keyfile_default));
case PreferenceKey.UseFileTransactions:
return prefs.GetBoolean(ctx.Resources.GetString(Resource.String.UseFileTransactions_key), true);
case PreferenceKey.CheckForFileChangesOnSave:
return prefs.GetBoolean(ctx.Resources.GetString(Resource.String.CheckForFileChangesOnSave_key), true);
default:
throw new Exception("unexpected key!");
}
@ -163,22 +165,25 @@ namespace keepass2android
EventHandler<DialogClickEventArgs> cancelHandler,
Context ctx)
{
Handler handler = new Handler(Looper.MainLooper);
handler.Post(() =>
{
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.SetTitle(GetResourceString(titleKey));
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.SetTitle(GetResourceString(titleKey));
builder.SetMessage(GetResourceString(messageKey));
builder.SetMessage(GetResourceString(messageKey));
builder.SetPositiveButton(Resource.String.yes, yesHandler);
builder.SetPositiveButton(Resource.String.yes, yesHandler);
builder.SetNegativeButton(Resource.String.no, noHandler);
builder.SetNegativeButton(Resource.String.no, noHandler);
builder.SetNeutralButton(ctx.GetString(Android.Resource.String.Cancel),
cancelHandler);
Dialog dialog = builder.Create();
dialog.Show();
builder.SetNeutralButton(ctx.GetString(Android.Resource.String.Cancel),
cancelHandler);
Dialog dialog = builder.Create();
dialog.Show();
}
);
}
public Handler UiThreadHandler

View File

@ -371,7 +371,9 @@
</AndroidResource>
<AndroidResource Include="Resources\xml\preferences.xml" />
<AndroidResource Include="Resources\xml\searchable.xml" />
<AndroidResource Include="Resources\values\strings.xml" />
<AndroidResource Include="Resources\values\strings.xml">
<SubType>Designer</SubType>
</AndroidResource>
<AndroidResource Include="Resources\menu-v11\entry.xml" />
<AndroidResource Include="Resources\menu-v11\password.xml" />
<AndroidResource Include="Resources\menu-v11\entry_edit.xml" />