Implemented UI for sync
Fixed bugs
This commit is contained in:
		| @@ -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); | ||||
| 		} | ||||
|  | ||||
| 		 | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -60,7 +60,7 @@ namespace keepass2android | ||||
| 			{ | ||||
| 				_handler.Post(() =>  | ||||
| 				{  | ||||
| 					if (String.IsNullOrEmpty(submessage)) | ||||
| 					if (!String.IsNullOrEmpty(submessage)) | ||||
| 					{ | ||||
| 						_progressDialog.SetMessage(_message + " (" + submessage + ")"); | ||||
| 					} | ||||
|   | ||||
| @@ -29,7 +29,7 @@ namespace keepass2android | ||||
| 		ParsingDatabase, | ||||
| 		CheckingTargetFileForChanges, | ||||
| 		TitleSyncQuestion, | ||||
| 		MessageSyncQuestions, | ||||
| 		MessageSyncQuestion, | ||||
| 		SynchronizingDatabase | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
| 		} | ||||
|   | ||||
| @@ -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() | ||||
| 		{ | ||||
|   | ||||
| @@ -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); | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -53,6 +53,7 @@ namespace keepass2android | ||||
| 			if (TimeoutHelper.CheckShutdown(this, _ioc)) | ||||
| 				return; | ||||
| 			 | ||||
| 			//todo: see LockCloseActivity.OnResume | ||||
| 			App.Kp2a.CheckForOpenFileChanged(this); | ||||
| 		} | ||||
|  | ||||
|   | ||||
							
								
								
									
										1067
									
								
								src/keepass2android/Resources/Resource.designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1067
									
								
								src/keepass2android/Resources/Resource.designer.cs
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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> | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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  | ||||
|   | ||||
| @@ -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" /> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Philipp Crocoll
					Philipp Crocoll