moved logic of StorageSelectionActivity to testable base class
added tests for StorageSelectionActivity, fixed bugs if database is read-only, no edit buttons are displayed
This commit is contained in:
		| @@ -41,8 +41,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MasterKeeWinPlugin", "Maste | |||||||
| EndProject | EndProject | ||||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplePlugin", "SamplePlugin\SamplePlugin.csproj", "{4C1BB6F8-D2CD-49C2-9053-21705681356C}" | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplePlugin", "SamplePlugin\SamplePlugin.csproj", "{4C1BB6F8-D2CD-49C2-9053-21705681356C}" | ||||||
| EndProject | EndProject | ||||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeNeo", "..\..\..\..\..\Dropbox\programmieren\KeepassPlugins\AutoTypeNeo\AutoTypeNeo.csproj", "{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}" |  | ||||||
| EndProject |  | ||||||
| Global | Global | ||||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||||
| 		Debug|Any CPU = Debug|Any CPU | 		Debug|Any CPU = Debug|Any CPU | ||||||
| @@ -446,24 +444,6 @@ Global | |||||||
| 		{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU | 		{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU | ||||||
| 		{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU | 		{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU | ||||||
| 		{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU | 		{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Any CPU.Build.0 = Debug|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Win32.ActiveCfg = Debug|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|x64.ActiveCfg = Debug|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Any CPU.ActiveCfg = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Any CPU.Build.0 = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Mixed Platforms.Build.0 = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Win32.ActiveCfg = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|x64.ActiveCfg = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU |  | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU |  | ||||||
| 	EndGlobalSection | 	EndGlobalSection | ||||||
| 	GlobalSection(SolutionProperties) = preSolution | 	GlobalSection(SolutionProperties) = preSolution | ||||||
| 		HideSolutionNode = FALSE | 		HideSolutionNode = FALSE | ||||||
| @@ -474,7 +454,6 @@ Global | |||||||
| 		{9A4C5BAA-1A8A-49B4-BBC3-60D4871FB36C} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} | 		{9A4C5BAA-1A8A-49B4-BBC3-60D4871FB36C} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} | ||||||
| 		{BBF77830-BC7D-4F28-A255-A348B5C6A925} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} | 		{BBF77830-BC7D-4F28-A255-A348B5C6A925} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} | ||||||
| 		{4C1BB6F8-D2CD-49C2-9053-21705681356C} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} | 		{4C1BB6F8-D2CD-49C2-9053-21705681356C} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} | ||||||
| 		{4E5181A6-5FC4-4963-87B3-BAD15E7A765B} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} |  | ||||||
| 	EndGlobalSection | 	EndGlobalSection | ||||||
| 	GlobalSection(MonoDevelopProperties) = preSolution | 	GlobalSection(MonoDevelopProperties) = preSolution | ||||||
| 		StartupItem = keepass2android\keepass2android.csproj | 		StartupItem = keepass2android\keepass2android.csproj | ||||||
|   | |||||||
| @@ -79,8 +79,17 @@ namespace keepass2android | |||||||
| 		Handler UiThreadHandler { get; } | 		Handler UiThreadHandler { get; } | ||||||
|  |  | ||||||
| 		IProgressDialog CreateProgressDialog(Context ctx); | 		IProgressDialog CreateProgressDialog(Context ctx); | ||||||
|  |  | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// returns the file storage for the given ioc. might be a caching file storage | ||||||
|  | 		/// </summary> | ||||||
| 		IFileStorage GetFileStorage(IOConnectionInfo iocInfo); | 		IFileStorage GetFileStorage(IOConnectionInfo iocInfo); | ||||||
|  |  | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// returns the file storage for the given ioc. if allowCache=false, no cached file storage is returned | ||||||
|  | 		/// </summary> | ||||||
|  | 		IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache); | ||||||
|  |  | ||||||
| 		void TriggerReload(Context context); | 		void TriggerReload(Context context); | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| @@ -90,5 +99,7 @@ namespace keepass2android | |||||||
| 		//bool OnServerCertificateError(int certificateProblem); | 		//bool OnServerCertificateError(int certificateProblem); | ||||||
|  |  | ||||||
| 		RemoteCertificateValidationCallback CertificateValidationCallback { get; } | 		RemoteCertificateValidationCallback CertificateValidationCallback { get; } | ||||||
|  |  | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -80,6 +80,7 @@ | |||||||
|     <Compile Include="Io\SkyDriveFileStorage.cs" /> |     <Compile Include="Io\SkyDriveFileStorage.cs" /> | ||||||
|     <Compile Include="IProgressDialog.cs" /> |     <Compile Include="IProgressDialog.cs" /> | ||||||
|     <Compile Include="PreferenceKey.cs" /> |     <Compile Include="PreferenceKey.cs" /> | ||||||
|  |     <Compile Include="SelectStorageLocationActivityBase.cs" /> | ||||||
|     <Compile Include="UiStringKey.cs" /> |     <Compile Include="UiStringKey.cs" /> | ||||||
|     <Compile Include="database\Database.cs" /> |     <Compile Include="database\Database.cs" /> | ||||||
|     <Compile Include="database\edit\ActionOnFinish.cs" /> |     <Compile Include="database\edit\ActionOnFinish.cs" /> | ||||||
|   | |||||||
							
								
								
									
										318
									
								
								src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,318 @@ | |||||||
|  | using System; | ||||||
|  | using Android.App; | ||||||
|  | using Android.Content; | ||||||
|  | using Android.Widget; | ||||||
|  | using Java.Net; | ||||||
|  | using KeePassLib.Serialization; | ||||||
|  | using keepass2android.Io; | ||||||
|  |  | ||||||
|  | namespace keepass2android | ||||||
|  | { | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// base class for SelectStorageLocationActivity containing testable (non-UI) code | ||||||
|  | 	/// </summary> | ||||||
|  | 	public abstract class SelectStorageLocationActivityBase: Activity | ||||||
|  | 	{ | ||||||
|  | 		public enum WritableRequirements | ||||||
|  | 		{ | ||||||
|  | 			ReadOnly = 0, | ||||||
|  | 			WriteDesired = 1, | ||||||
|  | 			WriteDemanded = 2 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected const int RequestCodeFileStorageSelectionForPrimarySelect = 983713; | ||||||
|  | 		private const int RequestCodeFileStorageSelectionForCopyToWritableLocation = 983714; | ||||||
|  | 		private const int RequestCodeFileFileBrowseForWritableLocation = 983715; | ||||||
|  | 		private const int RequestCodeFileBrowseForOpen = 983716; | ||||||
|  | 		 | ||||||
|  |  | ||||||
|  | 		protected IOConnectionInfo _selectedIoc; | ||||||
|  | 		private IKp2aApp _app; | ||||||
|  |  | ||||||
|  | 		public SelectStorageLocationActivityBase(IKp2aApp app) | ||||||
|  | 		{ | ||||||
|  | 			_app = app; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) | ||||||
|  | 		{ | ||||||
|  | 			base.OnActivityResult(requestCode, resultCode, data); | ||||||
|  | 			if ((requestCode == RequestCodeFileStorageSelectionForPrimarySelect) || ((requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation))) | ||||||
|  | 			{ | ||||||
|  | 				int browseRequestCode = RequestCodeFileBrowseForOpen; | ||||||
|  | 				if (requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation) | ||||||
|  | 				{ | ||||||
|  | 					browseRequestCode = RequestCodeFileFileBrowseForWritableLocation; | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if (resultCode == ExitFileStorageSelectionOk) | ||||||
|  | 				{ | ||||||
|  |  | ||||||
|  | 					string protocolId = data.GetStringExtra("protocolId"); | ||||||
|  |  | ||||||
|  | 					if (protocolId == "androidget") | ||||||
|  | 					{ | ||||||
|  | 						ShowAndroidBrowseDialog(RequestCodeFileBrowseForOpen, false); | ||||||
|  | 					} | ||||||
|  | 					else | ||||||
|  | 					{ | ||||||
|  | 						bool isForSave = (requestCode == RequestCodeFileStorageSelectionForPrimarySelect) ? | ||||||
|  | 							                 IsStorageSelectionForSave : true; | ||||||
|  |  | ||||||
|  | 						 | ||||||
|  | 						StartSelectFile(isForSave, browseRequestCode, protocolId); | ||||||
|  | 						 | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					ReturnCancel(); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if ((requestCode == RequestCodeFileBrowseForOpen) || (requestCode == RequestCodeFileFileBrowseForWritableLocation)) | ||||||
|  | 			{ | ||||||
|  | 				if (resultCode == (Result)FileStorageResults.FileChooserPrepared) | ||||||
|  | 				{ | ||||||
|  | 					IOConnectionInfo ioc = new IOConnectionInfo(); | ||||||
|  | 					SetIoConnectionFromIntent(ioc, data); | ||||||
|  | 					bool isForSave = (requestCode == RequestCodeFileFileBrowseForWritableLocation) ? | ||||||
|  | 						                 true : IsStorageSelectionForSave; | ||||||
|  |  | ||||||
|  | 					StartFileChooser(ioc.Path, requestCode, isForSave); | ||||||
|  |  | ||||||
|  | 					return; | ||||||
|  | 				} | ||||||
|  | 				if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE"))) | ||||||
|  | 				{ | ||||||
|  | 					ShowToast(data.GetStringExtra("EXTRA_ERROR_MESSAGE")); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if (resultCode == Result.Ok) | ||||||
|  | 				{ | ||||||
|  | 					string filename = IntentToFilename(data); | ||||||
|  | 					if (filename != null) | ||||||
|  | 					{ | ||||||
|  | 						if (filename.StartsWith("file://")) | ||||||
|  | 						{ | ||||||
|  | 							filename = filename.Substring(7); | ||||||
|  | 							filename = URLDecoder.Decode(filename); | ||||||
|  | 						} | ||||||
|  |  | ||||||
|  | 						IOConnectionInfo ioc = new IOConnectionInfo | ||||||
|  | 							{ | ||||||
|  | 								Path = filename | ||||||
|  | 							}; | ||||||
|  |  | ||||||
|  | 						IocSelected(ioc, requestCode); | ||||||
|  | 					} | ||||||
|  | 					else | ||||||
|  | 					{ | ||||||
|  | 						if (data.Data.Scheme == "content") | ||||||
|  | 						{ | ||||||
|  | 							IocSelected(IOConnectionInfo.FromPath(data.DataString), requestCode); | ||||||
|  |  | ||||||
|  | 						} | ||||||
|  | 						else | ||||||
|  | 						{ | ||||||
|  | 							ShowInvalidSchemeMessage(data.DataString); | ||||||
|  | 							ReturnCancel(); | ||||||
|  | 						} | ||||||
|  |  | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					ReturnCancel(); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected abstract void ShowToast(string text); | ||||||
|  |  | ||||||
|  | 		protected abstract void ShowInvalidSchemeMessage(string dataString); | ||||||
|  |  | ||||||
|  | 		protected abstract string IntentToFilename(Intent data); | ||||||
|  |  | ||||||
|  | 		protected abstract void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data); | ||||||
|  |  | ||||||
|  | 		protected abstract Result ExitFileStorageSelectionOk { get; } | ||||||
|  |  | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Starts the appropriate file selection process (either manual file select or prepare filechooser + filechooser) | ||||||
|  | 		/// </summary> | ||||||
|  | 		/// <param name="isForSave"></param> | ||||||
|  | 		/// <param name="browseRequestCode"></param> | ||||||
|  | 		/// <param name="protocolId"></param> | ||||||
|  | 		protected abstract void StartSelectFile(bool isForSave, int browseRequestCode, string protocolId); | ||||||
|  |  | ||||||
|  | 		protected abstract void ShowAndroidBrowseDialog(int requestCode, bool isForSave); | ||||||
|  |  | ||||||
|  | 		protected abstract bool IsStorageSelectionForSave { get; } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		private void IocSelected(IOConnectionInfo ioc, int requestCode) | ||||||
|  | 		{ | ||||||
|  | 			if (requestCode == RequestCodeFileFileBrowseForWritableLocation) | ||||||
|  | 			{ | ||||||
|  | 				IocForCopySelected(ioc); | ||||||
|  | 			} | ||||||
|  | 			else if (requestCode == RequestCodeFileBrowseForOpen) | ||||||
|  | 			{ | ||||||
|  | 				PrimaryIocSelected(ioc); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | #if DEBUG | ||||||
|  | 				throw new Exception("invalid request code!"); | ||||||
|  | #endif | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		private void IocForCopySelected(IOConnectionInfo targetIoc) | ||||||
|  | 		{ | ||||||
|  | 			PerformCopy(() => | ||||||
|  | 				{ | ||||||
|  | 					IOConnectionInfo sourceIoc = _selectedIoc; | ||||||
|  |  | ||||||
|  | 					try | ||||||
|  | 					{ | ||||||
|  | 						CopyFile(targetIoc, sourceIoc); | ||||||
|  | 					} | ||||||
|  | 					catch (Exception e) | ||||||
|  | 					{ | ||||||
|  | 						return () => | ||||||
|  | 							{ | ||||||
|  | 								ShowToast(_app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message); | ||||||
|  | 								ReturnCancel(); | ||||||
|  | 							}; | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 					return () => { ReturnOk(targetIoc); }; | ||||||
|  | 				}); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected abstract void PerformCopy(Func<Action> copyAndReturnPostExecute); | ||||||
|  |  | ||||||
|  | 		private void MoveToWritableLocation(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			_selectedIoc = ioc; | ||||||
|  |  | ||||||
|  | 			StartFileStorageSelection(RequestCodeFileStorageSelectionForCopyToWritableLocation, false, false); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected abstract void StartFileStorageSelection(int requestCode, | ||||||
|  | 		                                                  bool allowThirdPartyGet, bool allowThirdPartySend); | ||||||
|  |  | ||||||
|  | 		protected bool OnReceivedSftpData(string filename, int requestCode, bool isForSave) | ||||||
|  | 		{ | ||||||
|  | 			IOConnectionInfo ioc = new IOConnectionInfo { Path = filename }; | ||||||
|  | #if !EXCLUDE_FILECHOOSER | ||||||
|  | 			StartFileChooser(ioc.Path, requestCode, isForSave); | ||||||
|  | #else | ||||||
|  | 			IocSelected(ioc, requestCode); | ||||||
|  | #endif | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected abstract void StartFileChooser(string path, int requestCode, bool isForSave); | ||||||
|  |  | ||||||
|  | 		protected bool OnOpenButton(String fileName, int requestCode) | ||||||
|  | 		{ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			IOConnectionInfo ioc = new IOConnectionInfo | ||||||
|  | 				{ | ||||||
|  | 					Path = fileName | ||||||
|  | 				}; | ||||||
|  |  | ||||||
|  | 			IocSelected(ioc, requestCode); | ||||||
|  |  | ||||||
|  | 			return true; | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		protected virtual void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc) | ||||||
|  | 		{ | ||||||
|  | 			IFileStorage sourceStorage = _app.GetFileStorage(sourceIoc, false); //don't cache source. file won't be used ever again | ||||||
|  | 			IFileStorage targetStorage = _app.GetFileStorage(targetIoc); | ||||||
|  |  | ||||||
|  | 			using ( | ||||||
|  | 				var writeTransaction = targetStorage.OpenWriteTransaction(targetIoc, | ||||||
|  | 				                                                          _app.GetBooleanPreference( | ||||||
|  | 					                                                          PreferenceKey.UseFileTransactions))) | ||||||
|  | 			{ | ||||||
|  | 				using (var writeStream = writeTransaction.OpenFile()) | ||||||
|  | 				{ | ||||||
|  | 					sourceStorage.OpenFileForRead(sourceIoc).CopyTo(writeStream); | ||||||
|  | 				} | ||||||
|  | 				writeTransaction.CommitWrite(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		private void PrimaryIocSelected(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			var filestorage = _app.GetFileStorage(ioc, false); | ||||||
|  | 			if (!filestorage.IsPermanentLocation(ioc)) | ||||||
|  | 			{ | ||||||
|  | 				 | ||||||
|  | 				string message = _app.GetResourceString(UiStringKey.FileIsTemporarilyAvailable) + " " + _app.GetResourceString(UiStringKey.CopyFileRequired) + " " + _app.GetResourceString(UiStringKey.ClickOkToSelectLocation); | ||||||
|  | 				EventHandler<DialogClickEventArgs> onOk = (sender, args) => { MoveToWritableLocation(ioc); }; | ||||||
|  | 				EventHandler<DialogClickEventArgs> onCancel = (sender, args) => { ReturnCancel(); }; | ||||||
|  | 				ShowAlertDialog(message, onOk, onCancel); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  |  | ||||||
|  | 			if ((RequestedWritableRequirements != WritableRequirements.ReadOnly) && (filestorage.IsReadOnly(ioc))) | ||||||
|  | 			{ | ||||||
|  | 				string readOnlyExplanation = _app.GetResourceString(UiStringKey.FileIsReadOnly); | ||||||
|  | 				BuiltInFileStorage builtInFileStorage = filestorage as BuiltInFileStorage; | ||||||
|  | 				if (builtInFileStorage != null) | ||||||
|  | 				{ | ||||||
|  | 					if (builtInFileStorage.IsReadOnlyBecauseKitkatRestrictions(ioc)) | ||||||
|  | 						readOnlyExplanation = _app.GetResourceString(UiStringKey.FileIsReadOnlyOnKitkat); | ||||||
|  | 				} | ||||||
|  | 				EventHandler<DialogClickEventArgs> onOk = (sender, args) => { MoveToWritableLocation(ioc); }; | ||||||
|  | 				EventHandler<DialogClickEventArgs> onCancel = (sender, args) => | ||||||
|  | 					{ | ||||||
|  | 						if (RequestedWritableRequirements == WritableRequirements.WriteDemanded) | ||||||
|  | 							ReturnCancel(); | ||||||
|  | 						else | ||||||
|  | 							ReturnOk(ioc); | ||||||
|  | 					}; | ||||||
|  | 				ShowAlertDialog(readOnlyExplanation + " " | ||||||
|  | 								+ (RequestedWritableRequirements == WritableRequirements.WriteDemanded ? | ||||||
|  | 									   _app.GetResourceString(UiStringKey.CopyFileRequired) | ||||||
|  | 									   : _app.GetResourceString(UiStringKey.CopyFileRequiredForEditing)) | ||||||
|  | 								+ " " | ||||||
|  | 								+ _app.GetResourceString(UiStringKey.ClickOkToSelectLocation), onOk, onCancel); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 			ReturnOk(ioc); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected abstract void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel); | ||||||
|  |  | ||||||
|  | 		protected abstract WritableRequirements RequestedWritableRequirements { get; } | ||||||
|  |  | ||||||
|  | 		protected abstract void ReturnOk(IOConnectionInfo ioc); | ||||||
|  |  | ||||||
|  | 		protected abstract void ReturnCancel(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -48,6 +48,12 @@ namespace keepass2android | |||||||
| 		SynchronizingOtpAuxFile, | 		SynchronizingOtpAuxFile, | ||||||
| 		SavingOtpAuxFile, | 		SavingOtpAuxFile, | ||||||
| 		CertificateFailure, | 		CertificateFailure, | ||||||
| 		exporting_database | 		exporting_database, | ||||||
|  | 		FileIsTemporarilyAvailable, | ||||||
|  | 		CopyFileRequired, | ||||||
|  | 		ClickOkToSelectLocation, | ||||||
|  | 		FileIsReadOnly, | ||||||
|  | 		FileIsReadOnlyOnKitkat, | ||||||
|  | 		CopyFileRequiredForEditing | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -119,7 +119,7 @@ namespace keepass2android | |||||||
| 			KpDatabase = pwDatabase; | 			KpDatabase = pwDatabase; | ||||||
| 			SearchHelper = new SearchDbHelper(app); | 			SearchHelper = new SearchDbHelper(app); | ||||||
|  |  | ||||||
| 			CanWrite = databaseLoader.CanWrite; | 			CanWrite = databaseLoader.CanWrite && !fileStorage.IsReadOnly(iocInfo); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
|   | |||||||
| @@ -84,6 +84,7 @@ | |||||||
|     <Compile Include="TestCachingFileStorage.cs" /> |     <Compile Include="TestCachingFileStorage.cs" /> | ||||||
|     <Compile Include="TestSaveDb.cs" /> |     <Compile Include="TestSaveDb.cs" /> | ||||||
|     <Compile Include="TestSaveDbCached.cs" /> |     <Compile Include="TestSaveDbCached.cs" /> | ||||||
|  |     <Compile Include="TestSelectStorageLocation.cs" /> | ||||||
|     <Compile Include="TestSynchronizeCachedDatabase.cs" /> |     <Compile Include="TestSynchronizeCachedDatabase.cs" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ namespace Kp2aUnitTests | |||||||
| 			//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileByDirectCall")); | 			//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileByDirectCall")); | ||||||
| 			//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileOnly")); | 			//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileOnly")); | ||||||
|  |  | ||||||
|  | 			 | ||||||
| 			runner.AddTests(new List<Type> { typeof(TestBuiltInFileStorage) }); | 			runner.AddTests(new List<Type> { typeof(TestSelectStorageLocation) }); | ||||||
| 			//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)}); | 			//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)}); | ||||||
| 			//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure")); | 			//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure")); | ||||||
| 			//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure")); | 			//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure")); | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								src/Kp2aUnitTests/TestAndroidContentFileStorage.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/Kp2aUnitTests/TestAndroidContentFileStorage.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using Android.App; | ||||||
|  | using Android.Content; | ||||||
|  | using Android.OS; | ||||||
|  | using Java.IO; | ||||||
|  | using KeePassLib; | ||||||
|  | using KeePassLib.Interfaces; | ||||||
|  | using KeePassLib.Keys; | ||||||
|  | using KeePassLib.Serialization; | ||||||
|  | using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||||
|  | using keepass2android; | ||||||
|  | using keepass2android.Io; | ||||||
|  |  | ||||||
|  | namespace Kp2aUnitTests | ||||||
|  | { | ||||||
|  | 	[TestClass] | ||||||
|  | 	internal class TestBuiltInFileStorage | ||||||
|  | 	{ | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void ReadOnlyKitKat() | ||||||
|  | 		{ | ||||||
|  | 			var storage = new BuiltInFileStorage(new TestKp2aApp()); | ||||||
|  | 			var extFile = "/storage/sdcard1/file.txt"; | ||||||
|  | 			Assert.IsTrue(storage.IsReadOnly(IOConnectionInfo.FromPath(extFile))); | ||||||
|  | 			Assert.IsTrue(storage.IsReadOnly(IOConnectionInfo.FromPath(extFile))); | ||||||
|  |  | ||||||
|  | 			Assert.IsFalse(storage.IsReadOnly(IOConnectionInfo.FromPath(Application.Context.GetExternalFilesDir(null).AbsolutePath+ "/file.txt"))); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -134,11 +134,18 @@ namespace Kp2aUnitTests | |||||||
| 			return new ProgressDialogStub(); | 			return new ProgressDialogStub(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		public IFileStorage GetFileStorage(IOConnectionInfo iocInfo) | 		public virtual IFileStorage GetFileStorage(IOConnectionInfo iocInfo) | ||||||
| 		{ | 		{ | ||||||
| 			return FileStorage; | 			return FileStorage; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		public virtual IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache) | ||||||
|  | 		{ | ||||||
|  | 			if (FileStorage is CachingFileStorage) | ||||||
|  | 				throw new Exception("bad test class"); | ||||||
|  | 			return FileStorage; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		 | 		 | ||||||
| 		public bool TriggerReloadCalled; | 		public bool TriggerReloadCalled; | ||||||
| 		private TestFileStorage _testFileStorage; | 		private TestFileStorage _testFileStorage; | ||||||
|   | |||||||
							
								
								
									
										790
									
								
								src/Kp2aUnitTests/TestSelectStorageLocation.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										790
									
								
								src/Kp2aUnitTests/TestSelectStorageLocation.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,790 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
|  | using Android.App; | ||||||
|  | using Android.Content; | ||||||
|  | using Android.OS; | ||||||
|  | using Android.Runtime; | ||||||
|  | using Android.Views; | ||||||
|  | using Android.Widget; | ||||||
|  | using KeePassLib.Serialization; | ||||||
|  | using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||||
|  | using keepass2android; | ||||||
|  | using keepass2android.Io; | ||||||
|  |  | ||||||
|  | namespace Kp2aUnitTests | ||||||
|  | { | ||||||
|  |  | ||||||
|  | 	class TemporaryFileStorage: IFileStorage | ||||||
|  | 	{ | ||||||
|  | 		public IEnumerable<string> SupportedProtocols | ||||||
|  | 		{ | ||||||
|  | 			get {  | ||||||
|  | 				yield return "content"; | ||||||
|  | 				yield return "readonly"; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void Delete(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion) | ||||||
|  | 		{ | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public string GetCurrentFileVersionFast(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			return null; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public Stream OpenFileForRead(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public bool RequiresCredentials(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void CreateDirectory(IOConnectionInfo ioc, string newDirName) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public FileDescription GetFileDescription(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public bool RequiresSetup(IOConnectionInfo ioConnection) | ||||||
|  | 		{ | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public string IocToPath(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			return ioc.Path; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode, | ||||||
|  | 		                             bool alwaysReturnSuccess) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void OnResume(IFileStorageSetupActivity activity) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void OnStart(IFileStorageSetupActivity activity) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public string GetDisplayName(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public string CreateFilePath(string parent, string newFilename) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public IOConnectionInfo GetParentPath(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename) | ||||||
|  | 		{ | ||||||
|  | 			throw new NotImplementedException(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public bool IsPermanentLocation(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			return ioc.Path.StartsWith("content") == false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public bool IsReadOnly(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	class TestKp2aAppForSelectStorageLocation: TestKp2aApp | ||||||
|  | 	{ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		public override IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache) | ||||||
|  | 		{ | ||||||
|  | 			if ((iocInfo.Path.StartsWith("content://")) || (iocInfo.Path.StartsWith("readonly://"))) | ||||||
|  | 			{ | ||||||
|  | 				return new TemporaryFileStorage(); | ||||||
|  | 			} | ||||||
|  | 			return base.GetFileStorage(iocInfo); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sealed class TestControllableSelectStorageLocationActivity: SelectStorageLocationActivityBase | ||||||
|  | 	{ | ||||||
|  |  | ||||||
|  | 		public List<string> toasts = new List<string>(); | ||||||
|  | 		public WritableRequirements requestedWritableRequirements; | ||||||
|  | 		public bool? _result; | ||||||
|  | 		public IOConnectionInfo _resultIoc; | ||||||
|  | 		public object _userAction; | ||||||
|  | 		private IKp2aApp _app; | ||||||
|  |  | ||||||
|  | 		public TestControllableSelectStorageLocationActivity(IKp2aApp app) : base(app) | ||||||
|  | 		{ | ||||||
|  | 			_app = app; | ||||||
|  | 			StartFileStorageSelection(RequestCodeFileStorageSelectionForPrimarySelect, true, false); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void ShowToast(string text) | ||||||
|  | 		{ | ||||||
|  | 			toasts.Add(text); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc) | ||||||
|  | 		{ | ||||||
|  | 			if (CopyFileShouldFail) | ||||||
|  | 			{ | ||||||
|  | 				throw new Exception("CopyFile failed in test."); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public bool CopyFileShouldFail { get; set; } | ||||||
|  |  | ||||||
|  | 		protected override void ShowInvalidSchemeMessage(string dataString) | ||||||
|  | 		{ | ||||||
|  | 			toasts.Add("invalid scheme: " + dataString); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override string IntentToFilename(Intent data) | ||||||
|  | 		{ | ||||||
|  | 			return data.GetStringExtra("path"); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data) | ||||||
|  | 		{ | ||||||
|  | 			ioc.Path = data.GetStringExtra("path"); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override Result ExitFileStorageSelectionOk | ||||||
|  | 		{ | ||||||
|  | 			get { return Result.FirstUser + 825; } | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void StartSelectFile(bool isForSave, int browseRequestCode, string protocolId) | ||||||
|  | 		{ | ||||||
|  | 			_userAction = new SelectFileAction(isForSave, browseRequestCode, protocolId, this); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public void HandleActivityResult(int requestCode, Result resultCode, Intent data) | ||||||
|  | 		{ | ||||||
|  | 			OnActivityResult(requestCode, resultCode, data); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		internal class SelectFileAction | ||||||
|  | 		{ | ||||||
|  | 			private readonly bool _isForSave; | ||||||
|  | 			private readonly int _browseRequestCode; | ||||||
|  | 			private readonly string _protocolId; | ||||||
|  | 			private readonly TestControllableSelectStorageLocationActivity _testControllableSelectStorageLocationActivity; | ||||||
|  |  | ||||||
|  | 			public SelectFileAction(bool isForSave, int browseRequestCode, string protocolId, TestControllableSelectStorageLocationActivity testControllableSelectStorageLocationActivity) | ||||||
|  | 			{ | ||||||
|  | 				_isForSave = isForSave; | ||||||
|  | 				_browseRequestCode = browseRequestCode; | ||||||
|  | 				_protocolId = protocolId; | ||||||
|  | 				_testControllableSelectStorageLocationActivity = testControllableSelectStorageLocationActivity; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public bool IsForSave { | ||||||
|  | 				get { return _isForSave; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public int BrowseRequestCode | ||||||
|  | 			{ | ||||||
|  | 				get { return _browseRequestCode; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public string ProtocolId | ||||||
|  | 			{ | ||||||
|  | 				get { return _protocolId; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			public void PerformManualFileSelect(string path) | ||||||
|  | 			{ | ||||||
|  | 				_testControllableSelectStorageLocationActivity.PressOpenButton(path, _browseRequestCode); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void Cancel() | ||||||
|  | 			{ | ||||||
|  | 				_testControllableSelectStorageLocationActivity.ReturnCancel(); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void PrepareFileChooser(string protocolId) | ||||||
|  | 			{ | ||||||
|  | 				Intent data = new Intent(); | ||||||
|  | 				data.PutExtra("path", protocolId+"://"); | ||||||
|  | 				_testControllableSelectStorageLocationActivity.HandleActivityResult(_browseRequestCode, (Result) FileStorageResults.FileChooserPrepared, data); | ||||||
|  | 				 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		private void PressOpenButton(string path, int browseRequestCode) | ||||||
|  | 		{ | ||||||
|  | 			OnOpenButton(path, browseRequestCode); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		internal class FileStorageSelectionAction | ||||||
|  | 		{ | ||||||
|  | 			private readonly int _requestCode; | ||||||
|  | 			private readonly bool _allowThirdPartyGet; | ||||||
|  | 			private readonly bool _allowThirdPartySend; | ||||||
|  | 			private readonly TestControllableSelectStorageLocationActivity _testControllableSelectStorageLocationActivity; | ||||||
|  |  | ||||||
|  | 			public FileStorageSelectionAction(int requestCode, bool allowThirdPartyGet, bool allowThirdPartySend, TestControllableSelectStorageLocationActivity testControllableSelectStorageLocationActivity) | ||||||
|  | 			{ | ||||||
|  | 				_requestCode = requestCode; | ||||||
|  | 				_allowThirdPartyGet = allowThirdPartyGet; | ||||||
|  | 				_allowThirdPartySend = allowThirdPartySend; | ||||||
|  | 				_testControllableSelectStorageLocationActivity = testControllableSelectStorageLocationActivity; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public int RequestCode | ||||||
|  | 			{ | ||||||
|  | 				get { return _requestCode; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public bool AllowThirdPartyGet | ||||||
|  | 			{ | ||||||
|  | 				get { return _allowThirdPartyGet; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public bool AllowThirdPartySend | ||||||
|  | 			{ | ||||||
|  | 				get { return _allowThirdPartySend; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void ReturnProtocol(string protocolId) | ||||||
|  | 			{ | ||||||
|  | 				Intent intent = new Intent(); | ||||||
|  | 				intent.PutExtra("protocolId", protocolId); | ||||||
|  | 				_testControllableSelectStorageLocationActivity.HandleActivityResult(_requestCode, Result.FirstUser + 825 /*fs select ok*/, intent); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void Cancel() | ||||||
|  | 			{ | ||||||
|  | 				_testControllableSelectStorageLocationActivity.HandleActivityResult(_requestCode, Result.Canceled, null); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void ShowAndroidBrowseDialog(int requestCode, bool isForSave) | ||||||
|  | 		{ | ||||||
|  | 			_userAction = new AndroidBrowseDialogAction(requestCode, isForSave, this); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		internal class AndroidBrowseDialogAction | ||||||
|  | 		{ | ||||||
|  | 			private readonly int _requestCode; | ||||||
|  | 			private readonly bool _isForSave; | ||||||
|  | 			private readonly TestControllableSelectStorageLocationActivity _activity; | ||||||
|  |  | ||||||
|  | 			public AndroidBrowseDialogAction(int requestCode, bool isForSave, TestControllableSelectStorageLocationActivity activity) | ||||||
|  | 			{ | ||||||
|  | 				_requestCode = requestCode; | ||||||
|  | 				_isForSave = isForSave; | ||||||
|  | 				_activity = activity; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public int RequestCode | ||||||
|  | 			{ | ||||||
|  | 				get { return _requestCode; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void ReturnSelectedFile(string selectedUri) | ||||||
|  | 			{ | ||||||
|  | 				Intent data = new Intent(); | ||||||
|  | 				data.PutExtra("path", selectedUri); | ||||||
|  | 				_activity.HandleActivityResult(_requestCode, Result.Ok, data); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void Cancel() | ||||||
|  | 			{ | ||||||
|  | 				_activity.HandleActivityResult(_requestCode, Result.Canceled, null); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override bool IsStorageSelectionForSave { get { return SelectLocationForSave; } } | ||||||
|  |  | ||||||
|  | 		private bool SelectLocationForSave { get; set; } | ||||||
|  |  | ||||||
|  | 		protected override void PerformCopy(Func<Action> copyAndReturnPostExecute) | ||||||
|  | 		{ | ||||||
|  | 			Action postExec = copyAndReturnPostExecute(); | ||||||
|  | 			postExec(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void StartFileStorageSelection(int requestCode, bool allowThirdPartyGet, bool allowThirdPartySend) | ||||||
|  | 		{ | ||||||
|  | 			_userAction = new FileStorageSelectionAction(requestCode, allowThirdPartyGet, allowThirdPartySend, this); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void StartFileChooser(string path, int requestCode, bool isForSave) | ||||||
|  | 		{ | ||||||
|  | 			_userAction = new FileChooserAction(path, requestCode, isForSave, this); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		internal class FileChooserAction | ||||||
|  | 		{ | ||||||
|  | 			private readonly string _path; | ||||||
|  | 			private readonly int _requestCode; | ||||||
|  | 			private readonly bool _isForSave; | ||||||
|  | 			private readonly TestControllableSelectStorageLocationActivity _activity; | ||||||
|  |  | ||||||
|  | 			public FileChooserAction(string path, int requestCode, bool isForSave, TestControllableSelectStorageLocationActivity activity) | ||||||
|  | 			{ | ||||||
|  | 				_path = path; | ||||||
|  | 				_requestCode = requestCode; | ||||||
|  | 				_isForSave = isForSave; | ||||||
|  | 				_activity = activity; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public string Path | ||||||
|  | 			{ | ||||||
|  | 				get { return _path; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public int RequestCode | ||||||
|  | 			{ | ||||||
|  | 				get { return _requestCode; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public bool IsForSave | ||||||
|  | 			{ | ||||||
|  | 				get { return _isForSave; } | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void ReturnChosenFile(string path) | ||||||
|  | 			{ | ||||||
|  | 				Intent data = new Intent(); | ||||||
|  | 				data.PutExtra("path", path); | ||||||
|  | 				_activity.HandleActivityResult(_requestCode, Result.Ok, data); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void Cancel() | ||||||
|  | 			{ | ||||||
|  | 				_activity.HandleActivityResult(_requestCode, Result.Canceled, null); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel) | ||||||
|  | 		{ | ||||||
|  | 			_userAction = new ShowAlertDialogAction(message, onOk, onCancel); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		internal class ShowAlertDialogAction | ||||||
|  | 		{ | ||||||
|  | 			public string Message { get; set; } | ||||||
|  | 			public EventHandler<DialogClickEventArgs> OnOk { get; set; } | ||||||
|  | 			public EventHandler<DialogClickEventArgs> OnCancel { get; set; } | ||||||
|  |  | ||||||
|  | 			public ShowAlertDialogAction(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel) | ||||||
|  | 			{ | ||||||
|  | 				Message = message; | ||||||
|  | 				OnOk = onOk; | ||||||
|  | 				OnCancel = onCancel; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void Cancel() | ||||||
|  | 			{ | ||||||
|  | 				OnCancel(this, null); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			public void Ok() | ||||||
|  | 			{ | ||||||
|  | 				OnOk(this, null); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override WritableRequirements RequestedWritableRequirements | ||||||
|  | 		{ | ||||||
|  | 			get { return requestedWritableRequirements; } | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public IKp2aApp App | ||||||
|  | 		{ | ||||||
|  | 			get { return _app; } | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void ReturnOk(IOConnectionInfo ioc) | ||||||
|  | 		{ | ||||||
|  | 			_result = true; | ||||||
|  | 			_resultIoc = ioc; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void ReturnCancel() | ||||||
|  | 		{ | ||||||
|  | 			_result = false; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	[TestClass] | ||||||
|  | 	class TestSelectStorageLocation | ||||||
|  | 	{ | ||||||
|  | 		[TestInitialize] | ||||||
|  | 		public void Init() | ||||||
|  | 		{ | ||||||
|  | 			try | ||||||
|  | 			{ | ||||||
|  | 				Looper.Prepare(); | ||||||
|  | 			} | ||||||
|  | 			catch (Exception) | ||||||
|  | 			{ | ||||||
|  | 				 | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCancelFileStorageSelection() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action.Cancel(); | ||||||
|  | 			Assert.IsFalse((bool) testee._result); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestSimpleManualSelect() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction; | ||||||
|  | 			action.ReturnProtocol("ftp"); | ||||||
|  |  | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction; | ||||||
|  | 			string path = "ftp://crocoll.net/test.kdbx"; | ||||||
|  | 			action2.PerformManualFileSelect(path); | ||||||
|  |  | ||||||
|  | 			Assert.IsTrue((bool) testee._result); | ||||||
|  | 			Assert.AreEqual(testee._resultIoc.Path, path); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCancelManualSelect() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action.ReturnProtocol("ftp"); | ||||||
|  |  | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction; | ||||||
|  | 			action2.Cancel(); | ||||||
|  |  | ||||||
|  | 			Assert.IsFalse((bool)testee._result); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCancelAndroidBrowseDialog() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action.ReturnProtocol("androidget"); | ||||||
|  |  | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction; | ||||||
|  | 			action2.Cancel(); | ||||||
|  |  | ||||||
|  | 			Assert.IsFalse((bool)testee._result); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCancelCopyTemporaryLocation() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action.ReturnProtocol("androidget"); | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction; | ||||||
|  | 			action2.ReturnSelectedFile("content://abc.kdbx"); | ||||||
|  |  | ||||||
|  | 			var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable))); | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  | 			action3.Cancel(); | ||||||
|  |  | ||||||
|  | 			Assert.IsFalse((bool)testee._result); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCopyTemporaryLocation() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action.ReturnProtocol("androidget"); | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction; | ||||||
|  | 			action2.ReturnSelectedFile("content://abc.kdbx"); | ||||||
|  |  | ||||||
|  | 			var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable))); | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  | 			action3.Ok(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action4.ReturnProtocol("ftp"); | ||||||
|  |  | ||||||
|  | 			Assert.IsNull(testee._result); | ||||||
|  |  | ||||||
|  | 			var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action5.IsForSave); | ||||||
|  | 			string path = "ftp://crocoll.net/testtarget.kdbx"; | ||||||
|  | 			action5.PerformManualFileSelect(path); | ||||||
|  |  | ||||||
|  | 			Assert.IsTrue((bool)testee._result); | ||||||
|  | 			Assert.AreEqual(path, testee._resultIoc.Path); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCopyTemporaryLocationWithFileBrowser() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action.ReturnProtocol("androidget"); | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction; | ||||||
|  | 			action2.ReturnSelectedFile("content://abc.kdbx"); | ||||||
|  |  | ||||||
|  | 			var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable))); | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  | 			action3.Ok(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action4.ReturnProtocol("file"); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action5.IsForSave); | ||||||
|  | 			 | ||||||
|  | 			action5.PrepareFileChooser("file"); | ||||||
|  |  | ||||||
|  | 			Assert.IsNull(testee._result); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			var action6 = (TestControllableSelectStorageLocationActivity.FileChooserAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action5.IsForSave); | ||||||
|  | 			string path = "file:///mnt/sdcard/testtarget.kdbx"; | ||||||
|  | 			 | ||||||
|  | 			action6.ReturnChosenFile(path); | ||||||
|  |  | ||||||
|  | 			string expectedpath = "/mnt/sdcard/testtarget.kdbx"; | ||||||
|  | 			Assert.IsTrue((bool)testee._result); | ||||||
|  | 			Assert.AreEqual(expectedpath, testee._resultIoc.Path); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCopyTemporaryLocationWithCancelFileBrowser() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action.ReturnProtocol("androidget"); | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction; | ||||||
|  | 			action2.ReturnSelectedFile("content://abc.kdbx"); | ||||||
|  |  | ||||||
|  | 			var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable))); | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  | 			action3.Ok(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action4.ReturnProtocol("file"); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action5.IsForSave); | ||||||
|  |  | ||||||
|  | 			action5.PrepareFileChooser("file"); | ||||||
|  |  | ||||||
|  | 			Assert.IsNull(testee._result); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			var action6 = (TestControllableSelectStorageLocationActivity.FileChooserAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action5.IsForSave); | ||||||
|  | 			string path = "file:///mnt/sdcard/testtarget.kdbx"; | ||||||
|  |  | ||||||
|  | 			action6.Cancel(); | ||||||
|  |  | ||||||
|  | 			Assert.IsFalse((bool)testee._result); | ||||||
|  | 			 | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCancelCopyReadOnlyLocation() | ||||||
|  | 		{ | ||||||
|  | 			SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.WriteDesired; | ||||||
|  | 			string path; | ||||||
|  | 			var testee = PrepareTesteeForCancelCopyReadOnly(requestedWritableRequirements, out path); | ||||||
|  |  | ||||||
|  | 			Assert.IsTrue((bool)testee._result); | ||||||
|  | 			Assert.AreEqual(path, testee._resultIoc.Path); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCancelCopyReadOnlyLocationWriteRequired() | ||||||
|  | 		{ | ||||||
|  | 			SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.WriteDemanded; | ||||||
|  | 			string path; | ||||||
|  | 			var testee = PrepareTesteeForCancelCopyReadOnly(requestedWritableRequirements, out path); | ||||||
|  |  | ||||||
|  | 			Assert.IsFalse((bool)testee._result); | ||||||
|  | 			 | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		private static TestControllableSelectStorageLocationActivity PrepareTesteeForCancelCopyReadOnly( | ||||||
|  | 			SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements, out string path) | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  |  | ||||||
|  | 			testee.requestedWritableRequirements = requestedWritableRequirements; | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction; | ||||||
|  | 			action.ReturnProtocol("androidget"); | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction) testee._userAction; | ||||||
|  | 			path = "readonly://abc.kdbx"; | ||||||
|  | 			action2.ReturnSelectedFile(path); | ||||||
|  |  | ||||||
|  | 			var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction) testee._userAction; | ||||||
|  | 			Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsReadOnly))); | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  | 			action3.Cancel(); | ||||||
|  | 			return testee; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestOpenReadOnly() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  |  | ||||||
|  | 			testee.requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.ReadOnly; | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction; | ||||||
|  | 			action.ReturnProtocol("androidget"); | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction) testee._userAction; | ||||||
|  | 			var path = "readonly://abc.kdbx"; | ||||||
|  | 			action2.ReturnSelectedFile(path); | ||||||
|  |  | ||||||
|  | 			Assert.IsTrue((bool)testee._result); | ||||||
|  | 			Assert.AreEqual(path, testee._resultIoc.Path); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		[TestMethod] | ||||||
|  | 		public void TestCopyTemporaryLocationFails() | ||||||
|  | 		{ | ||||||
|  | 			var testee = CreateTestee(); | ||||||
|  | 			var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action.ReturnProtocol("androidget"); | ||||||
|  |  | ||||||
|  | 			var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction; | ||||||
|  | 			action2.ReturnSelectedFile("content://abc.kdbx"); | ||||||
|  |  | ||||||
|  | 			var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable))); | ||||||
|  | 			Assert.IsNull(testee._result); //no result yet | ||||||
|  | 			action3.Ok(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; | ||||||
|  | 			action4.ReturnProtocol("ftp"); | ||||||
|  |  | ||||||
|  | 			Assert.IsNull(testee._result); | ||||||
|  |  | ||||||
|  | 			var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction; | ||||||
|  | 			Assert.IsTrue(action5.IsForSave); | ||||||
|  | 			string path = "ftp://crocoll.net/testtarget.kdbx"; | ||||||
|  |  | ||||||
|  | 			testee.CopyFileShouldFail = true; | ||||||
|  |  | ||||||
|  | 			action5.PerformManualFileSelect(path); | ||||||
|  |  | ||||||
|  | 			Assert.IsFalse((bool)testee._result); | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		private static TestControllableSelectStorageLocationActivity CreateTestee() | ||||||
|  | 		{ | ||||||
|  | 			return new TestControllableSelectStorageLocationActivity(new TestKp2aAppForSelectStorageLocation()); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,42 +1,26 @@ | |||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Text; |  | ||||||
|  |  | ||||||
| using Android.App; | using Android.App; | ||||||
| using Android.Content; | using Android.Content; | ||||||
| using Android.OS; | using Android.OS; | ||||||
| using Android.Runtime; |  | ||||||
| using Android.Views; |  | ||||||
| using Android.Widget; | using Android.Widget; | ||||||
| using Group.Pals.Android.Lib.UI.Filechooser.Utils.UI; |  | ||||||
| using KeePassLib.Serialization; | using KeePassLib.Serialization; | ||||||
|  | using KeePassLib.Utility; | ||||||
| using keepass2android.Io; | using keepass2android.Io; | ||||||
| using Environment = Android.OS.Environment; | using keepass2android.Utils; | ||||||
|  |  | ||||||
| namespace keepass2android | namespace keepass2android | ||||||
| { | { | ||||||
| 	[Activity(Label = "")] | 	[Activity(Label = "")] | ||||||
| 	public class SelectStorageLocationActivity : Activity, IDialogInterfaceOnDismissListener | 	public class SelectStorageLocationActivity : SelectStorageLocationActivityBase, IDialogInterfaceOnDismissListener | ||||||
| 	{ | 	{ | ||||||
| 		private ActivityDesign _design; | 		private ActivityDesign _design; | ||||||
| 		private bool _isRecreated; | 		 | ||||||
| 		private IOConnectionInfo _selectedIoc; |  | ||||||
| 		private const string BundleKeySelectedIoc = "BundleKeySelectedIoc"; | 		private const string BundleKeySelectedIoc = "BundleKeySelectedIoc"; | ||||||
| 		private const int RequestCodeFileStorageSelectionForPrimarySelect = 983713; | 		 | ||||||
| 		private const int RequestCodeFileStorageSelectionForCopyToWritableLocation = 983714; | 		 | ||||||
| 		private const int RequestCodeFileFileBrowseForWritableLocation = 983715; |  | ||||||
|  |  | ||||||
| 		public enum WritableRequirements |  | ||||||
| 		{ |  | ||||||
| 			ReadOnly = 0, |  | ||||||
| 			WriteDesired = 1, |  | ||||||
| 			WriteDemanded = 2 |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		public const string ExtraKeyWritableRequirements = "EXTRA_KEY_WRITABLE_REQUIREMENTS"; | 		public const string ExtraKeyWritableRequirements = "EXTRA_KEY_WRITABLE_REQUIREMENTS"; | ||||||
|  |  | ||||||
| 		public SelectStorageLocationActivity() | 		public SelectStorageLocationActivity() : base(App.Kp2a) | ||||||
| 		{ | 		{ | ||||||
| 			_design = new ActivityDesign(this); | 			_design = new ActivityDesign(this); | ||||||
| 		} | 		} | ||||||
| @@ -58,6 +42,8 @@ namespace keepass2android | |||||||
|  |  | ||||||
| 			bool allowThirdPartyGet = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, false); | 			bool allowThirdPartyGet = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, false); | ||||||
| 			bool allowThirdPartySend = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false); | 			bool allowThirdPartySend = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false); | ||||||
|  |  | ||||||
|  | 			bool isRecreated = false; | ||||||
| 			if (bundle == null) | 			if (bundle == null) | ||||||
| 				State = new Bundle(); | 				State = new Bundle(); | ||||||
| 			else | 			else | ||||||
| @@ -66,15 +52,14 @@ namespace keepass2android | |||||||
| 				var selectedIocString = bundle.GetString(BundleKeySelectedIoc, null); | 				var selectedIocString = bundle.GetString(BundleKeySelectedIoc, null); | ||||||
| 				if (selectedIocString != null) | 				if (selectedIocString != null) | ||||||
| 					_selectedIoc = IOConnectionInfo.UnserializeFromString(selectedIocString); | 					_selectedIoc = IOConnectionInfo.UnserializeFromString(selectedIocString); | ||||||
| 				_isRecreated = true; | 				isRecreated = true; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if (!_isRecreated) | 			//todo: handle orientation change while dialog is shown | ||||||
|  |  | ||||||
|  | 			if (!isRecreated) | ||||||
| 			{ | 			{ | ||||||
| 				Intent intent = new Intent(this, typeof(FileStorageSelectionActivity)); | 				StartFileStorageSelection(RequestCodeFileStorageSelectionForPrimarySelect, allowThirdPartyGet, allowThirdPartySend); | ||||||
| 				intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, allowThirdPartyGet); |  | ||||||
| 				intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, allowThirdPartySend); |  | ||||||
| 				StartActivityForResult(intent, RequestCodeFileStorageSelectionForPrimarySelect);	 |  | ||||||
| 			} | 			} | ||||||
| 				 | 				 | ||||||
|  |  | ||||||
| @@ -82,7 +67,58 @@ namespace keepass2android | |||||||
|  |  | ||||||
| 		protected Bundle State { get; set; } | 		protected Bundle State { get; set; } | ||||||
|  |  | ||||||
| 		protected bool IsStorageSelectionForSave  | 		protected override void ShowToast(string text) | ||||||
|  | 		{ | ||||||
|  | 			Toast.MakeText(this, text, ToastLength.Long).Show(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void ShowInvalidSchemeMessage(string dataString) | ||||||
|  | 		{ | ||||||
|  | 			Toast.MakeText(this, Resources.GetString(Resource.String.unknown_uri_scheme, new Java.Lang.Object[] { dataString }), | ||||||
|  | 										   ToastLength.Long).Show(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override string IntentToFilename(Intent data) | ||||||
|  | 		{ | ||||||
|  | 			return Util.IntentToFilename(data, this); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data) | ||||||
|  | 		{ | ||||||
|  | 			PasswordActivity.SetIoConnectionFromIntent(ioc, data); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override Result ExitFileStorageSelectionOk | ||||||
|  | 		{ | ||||||
|  | 			get { return KeePass.ExitFileStorageSelectionOk; } | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void StartSelectFile( bool isForSave, int browseRequestCode, string protocolId) | ||||||
|  | 		{ | ||||||
|  | 			var startManualFileSelect = new Action<string>(defaultPath => | ||||||
|  | 							{ | ||||||
|  | 								if (defaultPath.StartsWith("sftp://")) | ||||||
|  | 									Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel); | ||||||
|  | 								else | ||||||
|  | 									//todo oncreate nur wenn for save? | ||||||
|  | 									Util.ShowFilenameDialog(this, filename => OnOpenButton(filename, browseRequestCode), | ||||||
|  | 									                        filename => OnOpenButton(filename, browseRequestCode), | ||||||
|  | 									                        ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url), | ||||||
|  | 									                        browseRequestCode); | ||||||
|  | 							}); | ||||||
|  | 			; | ||||||
|  | 			App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this, | ||||||
|  | 																												  OnActivityResult, | ||||||
|  | 																												  startManualFileSelect | ||||||
|  | 																				), isForSave, browseRequestCode, protocolId); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override void ShowAndroidBrowseDialog(int requestCode, bool isForSave) | ||||||
|  | 		{ | ||||||
|  | 			Util.ShowBrowseDialog(this, requestCode, isForSave); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override bool IsStorageSelectionForSave  | ||||||
| 		{  | 		{  | ||||||
| 			get { return Intent.GetBooleanExtra(FileStorageSetupDefs.ExtraIsForSave, false); } | 			get { return Intent.GetBooleanExtra(FileStorageSetupDefs.ExtraIsForSave, false); } | ||||||
| 		} | 		} | ||||||
| @@ -103,241 +139,14 @@ namespace keepass2android | |||||||
| 			_design.ReapplyTheme(); | 			_design.ReapplyTheme(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) |  | ||||||
| 		{ |  | ||||||
| 			base.OnActivityResult(requestCode, resultCode, data); |  | ||||||
| 			if ((requestCode == RequestCodeFileStorageSelectionForPrimarySelect) || ((requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation))) |  | ||||||
| 			{ |  | ||||||
| 				int browseRequestCode = Intents.RequestCodeFileBrowseForOpen; |  | ||||||
| 				if (requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation) |  | ||||||
| 				{ |  | ||||||
| 					browseRequestCode = RequestCodeFileFileBrowseForWritableLocation; |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				if (resultCode == KeePass.ExitFileStorageSelectionOk) | 		protected override void ReturnCancel() | ||||||
| 				{ |  | ||||||
|  |  | ||||||
| 					string protocolId = data.GetStringExtra("protocolId"); |  | ||||||
|  |  | ||||||
| 					if (protocolId == "androidget") |  | ||||||
| 					{ |  | ||||||
| 						Util.ShowBrowseDialog(this, Intents.RequestCodeFileBrowseForOpen, false); |  | ||||||
| 					} |  | ||||||
| 					else |  | ||||||
| 					{ |  | ||||||
| 						bool isForSave = (requestCode == RequestCodeFileStorageSelectionForPrimarySelect) ? |  | ||||||
| 							IsStorageSelectionForSave : true; |  | ||||||
| 						 |  | ||||||
| 						App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this, |  | ||||||
| 							OnActivityResult, |  | ||||||
| 							defaultPath => |  | ||||||
| 								{ |  | ||||||
| 								if (defaultPath.StartsWith("sftp://")) |  | ||||||
| 									Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel); |  | ||||||
| 								else |  | ||||||
| 									//todo oncreate nur wenn for save? |  | ||||||
| 									Util.ShowFilenameDialog(this, filename => OnOpenButton(filename, browseRequestCode), |  | ||||||
| 												filename => OnOpenButton(filename, browseRequestCode),  |  | ||||||
| 												ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url), |  | ||||||
| 													browseRequestCode); |  | ||||||
| 							} |  | ||||||
| 							), isForSave, browseRequestCode, protocolId); |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					ReturnCancel(); |  | ||||||
| 				} |  | ||||||
| 	 |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if ((requestCode == Intents.RequestCodeFileBrowseForOpen) || (requestCode == RequestCodeFileFileBrowseForWritableLocation)) |  | ||||||
| 			{ |  | ||||||
| 				if (resultCode == (Result)FileStorageResults.FileChooserPrepared) |  | ||||||
| 				{ |  | ||||||
| 					IOConnectionInfo ioc = new IOConnectionInfo(); |  | ||||||
| 					PasswordActivity.SetIoConnectionFromIntent(ioc, data); |  | ||||||
| #if !EXCLUDE_FILECHOOSER |  | ||||||
| 					bool isForSave = (requestCode == RequestCodeFileFileBrowseForWritableLocation) ? |  | ||||||
| 						true : IsStorageSelectionForSave ; |  | ||||||
| 						 |  | ||||||
| 					StartFileChooser(ioc.Path, requestCode, isForSave); |  | ||||||
| #else |  | ||||||
| 						IocSelected(new IOConnectionInfo { Path = "/mnt/sdcard/keepass/yubi.kdbx" }, requestCode); |  | ||||||
| #endif |  | ||||||
| 					return; |  | ||||||
| 				} |  | ||||||
| 				if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE"))) |  | ||||||
| 				{ |  | ||||||
| 					Toast.MakeText(this, data.GetStringExtra("EXTRA_ERROR_MESSAGE"), ToastLength.Long).Show(); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				if (resultCode == Result.Ok) |  | ||||||
| 				{ |  | ||||||
| 					string filename = Util.IntentToFilename(data, this); |  | ||||||
| 					if (filename != null) |  | ||||||
| 					{ |  | ||||||
| 						if (filename.StartsWith("file://")) |  | ||||||
| 						{ |  | ||||||
| 							filename = filename.Substring(7); |  | ||||||
| 							filename = Java.Net.URLDecoder.Decode(filename); |  | ||||||
| 						} |  | ||||||
|  |  | ||||||
| 						IOConnectionInfo ioc = new IOConnectionInfo |  | ||||||
| 							{ |  | ||||||
| 								Path = filename |  | ||||||
| 							}; |  | ||||||
|  |  | ||||||
| 						IocSelected(ioc, requestCode); |  | ||||||
| 					} |  | ||||||
| 					else |  | ||||||
| 					{ |  | ||||||
| 						if (data.Data.Scheme == "content") |  | ||||||
| 						{ |  | ||||||
| 							IocSelected(IOConnectionInfo.FromPath(data.DataString), requestCode); |  | ||||||
| 	 |  | ||||||
| 						} |  | ||||||
| 						else |  | ||||||
| 						{ |  | ||||||
| 							Toast.MakeText(this, Resources.GetString(Resource.String.unknown_uri_scheme, new Java.Lang.Object[] {data.DataString}), |  | ||||||
| 							               ToastLength.Long).Show(); |  | ||||||
| 							ReturnCancel(); |  | ||||||
| 						} |  | ||||||
| 						 |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					ReturnCancel();	 |  | ||||||
| 				} |  | ||||||
| 				 |  | ||||||
| 				 |  | ||||||
| 			} |  | ||||||
| 				 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 			 |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private void ReturnCancel() |  | ||||||
| 		{ | 		{ | ||||||
| 			SetResult(Result.Canceled); | 			SetResult(Result.Canceled); | ||||||
| 			Finish(); | 			Finish(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private void IocSelected(IOConnectionInfo ioc, int requestCode) | 		protected override void ReturnOk(IOConnectionInfo ioc) | ||||||
| 		{ |  | ||||||
| 			if (requestCode == RequestCodeFileFileBrowseForWritableLocation) |  | ||||||
| 			{ |  | ||||||
| 				IocForCopySelected(ioc); |  | ||||||
| 			} |  | ||||||
| 			else if (requestCode == Intents.RequestCodeFileBrowseForOpen) |  | ||||||
| 			{ |  | ||||||
| 				PrimaryIocSelected(ioc); |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| #if DEBUG |  | ||||||
| 				throw new Exception("invalid request code!"); |  | ||||||
| #endif |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			 |  | ||||||
|  |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private void IocForCopySelected(IOConnectionInfo targetIoc) |  | ||||||
| 		{ |  | ||||||
| 			new keepass2android.Utils.SimpleLoadingDialog(this, GetString(Resource.String.CopyingFile), false, |  | ||||||
| 			() => |  | ||||||
| 				{ |  | ||||||
| 					IOConnectionInfo sourceIoc = _selectedIoc; |  | ||||||
|  |  | ||||||
| 					try |  | ||||||
| 					{ |  | ||||||
| 						CopyFile(targetIoc, sourceIoc); |  | ||||||
| 					} |  | ||||||
| 					catch (Exception e) |  | ||||||
| 					{ |  | ||||||
| 						return () => |  | ||||||
| 							{ |  | ||||||
| 								Toast.MakeText(this, App.Kp2a.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message, ToastLength.Long).Show(); |  | ||||||
| 								ReturnCancel(); |  | ||||||
| 							}; |  | ||||||
| 					} |  | ||||||
| 					 |  | ||||||
|  |  | ||||||
| 					return () => {ReturnOk(targetIoc); }; |  | ||||||
| 				} |  | ||||||
| 			).Execute(new Object[] {}); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private static void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc) |  | ||||||
| 		{ |  | ||||||
| 			IFileStorage sourceStorage = App.Kp2a.GetFileStorage(sourceIoc); |  | ||||||
| 			IFileStorage targetStorage = App.Kp2a.GetFileStorage(targetIoc); |  | ||||||
|  |  | ||||||
| 			using ( |  | ||||||
| 				var writeTransaction = targetStorage.OpenWriteTransaction(targetIoc, |  | ||||||
| 				                                                          App.Kp2a.GetBooleanPreference( |  | ||||||
| 					                                                          PreferenceKey.UseFileTransactions))) |  | ||||||
| 			{ |  | ||||||
| 				using (var writeStream = writeTransaction.OpenFile()) |  | ||||||
| 				{ |  | ||||||
| 					sourceStorage.OpenFileForRead(sourceIoc).CopyTo(writeStream); |  | ||||||
| 				} |  | ||||||
| 				writeTransaction.CommitWrite(); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private void PrimaryIocSelected(IOConnectionInfo ioc) |  | ||||||
| 		{ |  | ||||||
| 			if (!App.Kp2a.GetFileStorage(ioc).IsPermanentLocation(ioc)) |  | ||||||
| 			{ |  | ||||||
| 				new AlertDialog.Builder(this) |  | ||||||
| 					.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => { MoveToWritableLocation(ioc); }) |  | ||||||
| 					.SetMessage(Resources.GetString(Resource.String.FileIsTemporarilyAvailable) + " " |  | ||||||
| 							+ Resources.GetString(Resource.String.CopyFileRequired) + " " |  | ||||||
| 							+ Resources.GetString(Resource.String.ClickOkToSelectLocation)) |  | ||||||
| 					.SetCancelable(false) |  | ||||||
| 					.SetNegativeButton(Android.Resource.String.Cancel, (sender, args) => { ReturnCancel(); }) |  | ||||||
| 					//.SetOnDismissListener(this) |  | ||||||
| 					.Create() |  | ||||||
| 					.Show(); |  | ||||||
| 				return; |  | ||||||
| 			} |  | ||||||
| 			var filestorage = App.Kp2a.GetFileStorage(ioc); |  | ||||||
|  |  | ||||||
| 			if ((RequestedWritableRequirements != WritableRequirements.ReadOnly) && (filestorage.IsReadOnly(ioc))) |  | ||||||
| 			{ |  | ||||||
| 				string readOnlyExplanation = Resources.GetString(Resource.String.FileIsReadOnly); |  | ||||||
| 				BuiltInFileStorage builtInFileStorage = filestorage as BuiltInFileStorage; |  | ||||||
| 				if (builtInFileStorage != null) |  | ||||||
| 				{ |  | ||||||
| 					if (builtInFileStorage.IsReadOnlyBecauseKitkatRestrictions(ioc)) |  | ||||||
| 						readOnlyExplanation = Resources.GetString(Resource.String.FileIsReadOnlyOnKitkat); |  | ||||||
| 				} |  | ||||||
| 				new AlertDialog.Builder(this) |  | ||||||
| 						.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => { MoveToWritableLocation(ioc); }) |  | ||||||
| 						.SetCancelable(false) |  | ||||||
| 						.SetNegativeButton(Android.Resource.String.Cancel, (sender, args) => { ReturnCancel(); }) |  | ||||||
| 					//.SetOnDismissListener(this) |  | ||||||
| 						.SetMessage(readOnlyExplanation + " " |  | ||||||
| 							+ (RequestedWritableRequirements == WritableRequirements.WriteDemanded ? |  | ||||||
| 								Resources.GetString(Resource.String.CopyFileRequired) |  | ||||||
| 								: Resources.GetString(Resource.String.CopyFileRequiredForEditing)) |  | ||||||
| 							+ " " |  | ||||||
| 							+ Resources.GetString(Resource.String.ClickOkToSelectLocation)) |  | ||||||
| 						.Create() |  | ||||||
| 						.Show(); |  | ||||||
| 				return; |  | ||||||
| 			} |  | ||||||
| 			ReturnOk(ioc); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private void ReturnOk(IOConnectionInfo ioc) |  | ||||||
| 		{ | 		{ | ||||||
| 			Intent intent = new Intent(); | 			Intent intent = new Intent(); | ||||||
| 			PasswordActivity.PutIoConnectionToIntent(ioc, intent); | 			PasswordActivity.PutIoConnectionToIntent(ioc, intent); | ||||||
| @@ -345,75 +154,80 @@ namespace keepass2android | |||||||
| 			Finish(); | 			Finish(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private WritableRequirements RequestedWritableRequirements | 		protected override void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel) | ||||||
|  | 		{ | ||||||
|  | 			new AlertDialog.Builder(this) | ||||||
|  | 					.SetPositiveButton(Android.Resource.String.Ok, onOk) | ||||||
|  | 					.SetMessage(message) | ||||||
|  | 					.SetCancelable(false) | ||||||
|  | 					.SetNegativeButton(Android.Resource.String.Cancel, onCancel) | ||||||
|  | 					.Create() | ||||||
|  | 					.Show(); | ||||||
|  | 				 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected override WritableRequirements RequestedWritableRequirements | ||||||
| 		{ | 		{ | ||||||
| 			get { return (WritableRequirements) Intent.GetIntExtra(ExtraKeyWritableRequirements, (int)WritableRequirements.ReadOnly); } | 			get { return (WritableRequirements) Intent.GetIntExtra(ExtraKeyWritableRequirements, (int)WritableRequirements.ReadOnly); } | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private void MoveToWritableLocation(IOConnectionInfo ioc) | 		 | ||||||
| 		{ |  | ||||||
| 			_selectedIoc = ioc; |  | ||||||
|  |  | ||||||
| 			Intent intent = new Intent(this, typeof(FileStorageSelectionActivity)); | #if !EXCLUDE_FILECHOOSER | ||||||
| 			intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, false); | 		protected override void PerformCopy(Func<Action> copyAndReturnPostExecute) | ||||||
| 			intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false); | 		{ | ||||||
| 			 | 			 | ||||||
| 			StartActivityForResult(intent, RequestCodeFileStorageSelectionForCopyToWritableLocation);	 | 			new SimpleLoadingDialog(this, GetString(Resource.String.CopyingFile), false, | ||||||
|  | 			                      copyAndReturnPostExecute   | ||||||
|  | 				).Execute(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private bool OnReceivedSftpData(string filename, int requestCode, bool isForSave) | 		protected override void StartFileStorageSelection(int requestCode, bool allowThirdPartyGet, | ||||||
|  | 		                                                  bool allowThirdPartySend) | ||||||
| 		{ | 		{ | ||||||
| 			IOConnectionInfo ioc = new IOConnectionInfo { Path = filename }; | 			Intent intent = new Intent(this, typeof(FileStorageSelectionActivity)); | ||||||
| #if !EXCLUDE_FILECHOOSER | 			intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, allowThirdPartyGet); | ||||||
| 			StartFileChooser(ioc.Path, requestCode, isForSave); | 			intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, allowThirdPartySend); | ||||||
| #else |  | ||||||
| 			IocSelected(ioc, requestCode); | 			StartActivityForResult(intent, requestCode); | ||||||
| #endif |  | ||||||
| 			return true; |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| #if !EXCLUDE_FILECHOOSER | 		protected override void StartFileChooser(string defaultPath, int requestCode, bool forSave) | ||||||
| 		private void StartFileChooser(string defaultPath, int requestCode, bool forSave) |  | ||||||
| 		{ | 		{ | ||||||
| 			Kp2aLog.Log("FSA: defaultPath="+defaultPath); | #if !EXCLUDE_FILECHOOSER | ||||||
|  | 			Kp2aLog.Log("FSA: defaultPath=" + defaultPath); | ||||||
| 			string fileProviderAuthority = FileChooserFileProvider.TheAuthority; | 			string fileProviderAuthority = FileChooserFileProvider.TheAuthority; | ||||||
| 			if (defaultPath.StartsWith("file://")) | 			if (defaultPath.StartsWith("file://")) | ||||||
| 			{ | 			{ | ||||||
| 				fileProviderAuthority = PackageName+".android-filechooser.localfile"; | 				fileProviderAuthority = PackageName + ".android-filechooser.localfile"; | ||||||
| 			} | 			} | ||||||
| 			Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority, | 			Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority, | ||||||
| 			                                                                                            defaultPath); | 																										defaultPath); | ||||||
|  |  | ||||||
|  |  | ||||||
| 			if (forSave) | 			if (forSave) | ||||||
| 			{ | 			{ | ||||||
| 				i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true); | 				i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true); | ||||||
| 				i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.default_file_ext", "kdbx"); | 				var ext = UrlUtil.GetExtension(defaultPath); | ||||||
|  | 				if ((ext != String.Empty) && (ext.Contains("?")==false)) | ||||||
|  | 					i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.default_file_ext", ext); | ||||||
| 			} | 			} | ||||||
| 			StartActivityForResult(i, requestCode); | 			StartActivityForResult(i, requestCode); | ||||||
|  |  | ||||||
|  | #else | ||||||
|  | 						IocSelected(new IOConnectionInfo { Path = "/mnt/sdcard/keepass/yubi.kdbx" }, requestCode); | ||||||
|  | #endif | ||||||
|  | 		 | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
| 		private bool OnOpenButton(String fileName, int requestCode) |  | ||||||
| 		{ |  | ||||||
| 			 |  | ||||||
|  |  | ||||||
| 			IOConnectionInfo ioc = new IOConnectionInfo |  | ||||||
| 			{ |  | ||||||
| 				Path = fileName |  | ||||||
| 			}; |  | ||||||
|  |  | ||||||
| 			IocSelected(ioc, requestCode); |  | ||||||
|  |  | ||||||
| 			return true; |  | ||||||
|  |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
|  | 		 | ||||||
| 		public void OnDismiss(IDialogInterface dialog) | 		public void OnDismiss(IDialogInterface dialog) | ||||||
| 		{ | 		{ | ||||||
| //			ReturnCancel(); | //			ReturnCancel(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -328,7 +328,7 @@ namespace keepass2android | |||||||
| 				{ | 				{ | ||||||
| 					dialog.Dismiss(); | 					dialog.Dismiss(); | ||||||
| 					if (onCancel != null) | 					if (onCancel != null) | ||||||
| 						onCancel(); | 					onCancel(); | ||||||
| 				}; | 				}; | ||||||
|  |  | ||||||
| 			 | 			 | ||||||
| @@ -345,7 +345,7 @@ namespace keepass2android | |||||||
| 				{ | 				{ | ||||||
| 					string filename = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text; | 					string filename = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text; | ||||||
|  |  | ||||||
| 					ShowBrowseDialog(activity, requestCodeBrowse, onCreate != null); | 					Util.ShowBrowseDialog(activity, requestCodeBrowse, onCreate != null); | ||||||
|  |  | ||||||
| 				}; | 				}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -392,6 +392,10 @@ namespace keepass2android | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		public IFileStorage GetFileStorage(IOConnectionInfo iocInfo) | 		public IFileStorage GetFileStorage(IOConnectionInfo iocInfo) | ||||||
|  | 		{ | ||||||
|  | 			return GetFileStorage(iocInfo, true); | ||||||
|  | 		} | ||||||
|  | 		public IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache) | ||||||
| 		{ | 		{ | ||||||
| 			if (iocInfo.IsLocalFile()) | 			if (iocInfo.IsLocalFile()) | ||||||
| 				return new BuiltInFileStorage(this); | 				return new BuiltInFileStorage(this); | ||||||
| @@ -399,9 +403,8 @@ namespace keepass2android | |||||||
| 			{ | 			{ | ||||||
| 				IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo); | 				IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo); | ||||||
|  |  | ||||||
| 				if (DatabaseCacheEnabled) | 				if (DatabaseCacheEnabled && allowCache) | ||||||
| 				{ | 				{ | ||||||
| 					//TODO |  | ||||||
| 					return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);	 | 					return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);	 | ||||||
| 				} | 				} | ||||||
| 				else | 				else | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Philipp Crocoll
					Philipp Crocoll