Merge branch 'AlexVallat/Keepass2AndroidPerfOpt'
Conflicts: src/KeePassLib2Android/Serialization/IOConnection.cs src/Kp2aBusinessLogic/IKp2aApp.cs src/Kp2aBusinessLogic/database/Database.cs src/keepass2android/Resources/Resource.designer.cs src/keepass2android/app/App.cs src/keepass2android/fileselect/FileSelectActivity.cs
							
								
								
									
										25
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,3 +1,28 @@ | ||||
| PCtest | ||||
|  | ||||
| *.suo | ||||
| *.userprefs | ||||
| *.user | ||||
|  | ||||
| *.designer.cs | ||||
|  | ||||
| src/java/kp2akeytransform/kp2akeytransform.zip | ||||
|  | ||||
| src/Kp2aKeyboardBinding/bin | ||||
| src/Kp2aKeyboardBinding/obj | ||||
|  | ||||
| src/kp2akeytransform/bin | ||||
| src/kp2akeytransform/obj | ||||
|  | ||||
| src/Kp2aBusinessLogic/bin | ||||
| src/Kp2aBusinessLogic/obj | ||||
|  | ||||
| src/Kp2aUnitTests/bin | ||||
| src/Kp2aUnitTests/obj | ||||
|  | ||||
| src/monodroid-unittesting/MonoDroidUnitTesting/bin | ||||
| src/monodroid-unittesting/MonoDroidUnitTesting/obj | ||||
|  | ||||
| /src/keepass2android/todos.cs | ||||
| /src/keepass2android/obj | ||||
| /src/keepass2android/bin | ||||
|   | ||||
| @@ -26,16 +26,29 @@ namespace keepass2android | ||||
| 	{ | ||||
| 		private static bool? _logToFile; | ||||
|  | ||||
| 		private static object _fileLocker = new object(); | ||||
|  | ||||
| 		public static void Log(string message) | ||||
| 		{ | ||||
| 			Android.Util.Log.Debug("KP2A", message); | ||||
| 			if (LogToFile) | ||||
| 			{ | ||||
| 				using (var streamWriter = File.AppendText(LogFilename)) | ||||
| 				lock (_fileLocker) | ||||
| 				{ | ||||
| 					string stringToLog = DateTime.Now+":"+DateTime.Now.Millisecond+ " -- " + message; | ||||
| 					streamWriter.WriteLine(stringToLog); | ||||
| 					try | ||||
| 					{ | ||||
| 						using (var streamWriter = File.AppendText(LogFilename)) | ||||
| 						{ | ||||
| 							string stringToLog = DateTime.Now + ":" + DateTime.Now.Millisecond + " -- " + message; | ||||
| 							streamWriter.WriteLine(stringToLog); | ||||
| 						} | ||||
| 					} | ||||
| 					catch (Exception e) | ||||
| 					{ | ||||
| 						Android.Util.Log.Debug("KP2A", "Couldn't write to log file. " + e); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|   | ||||
| @@ -162,17 +162,6 @@ namespace KeePassLib.Serialization | ||||
| 			finally { CommonCleanUpRead(sSource, hashedStream); } | ||||
| 		} | ||||
|  | ||||
| 		public static void CopyStream(Stream input, Stream output) | ||||
| 		{ | ||||
| 			byte[] buffer = new byte[4096]; | ||||
| 			int read; | ||||
| 			while ((read = input.Read(buffer, 0, buffer.Length)) > 0) | ||||
| 			{ | ||||
| 				output.Write(buffer, 0, read); | ||||
| 			} | ||||
| 			output.Seek(0, SeekOrigin.Begin); | ||||
| 		} | ||||
|  | ||||
| 		private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream) | ||||
| 		{ | ||||
| 			hashedStream.Close(); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| using System; | ||||
| using Android.App; | ||||
| using System.IO; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using KeePassLib.Serialization; | ||||
| @@ -13,11 +14,15 @@ namespace keepass2android | ||||
| 	/// This also contains methods which are UI specific and should be replacable for testing. | ||||
|     public interface IKp2aApp | ||||
|     { | ||||
| 		/// <summary> | ||||
| 		/// Locks the currently open database, quicklocking if available (unless false is passed for allowQuickUnlock) | ||||
| 		/// </summary> | ||||
| 		void LockDatabase(bool allowQuickUnlock = true); | ||||
|  | ||||
| 		/// <summary> | ||||
| 		/// Set the flag that the database needs to be locked. | ||||
| 		/// Loads the specified data as the currently open database, as unlocked. | ||||
| 		/// </summary> | ||||
|         void SetShutdown(); | ||||
| 		void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string s, string keyFile, ProgressDialogStatusLogger statusLogger); | ||||
|  | ||||
| 		/// <summary> | ||||
| 		/// Returns the current database | ||||
|   | ||||
| @@ -72,25 +72,6 @@ namespace keepass2android | ||||
| 			set { _loaded = value; } | ||||
| 		} | ||||
|  | ||||
| 		public bool Open | ||||
| 		{ | ||||
| 			get { return Loaded && (!Locked); } | ||||
| 		} | ||||
|  | ||||
| 		bool _locked; | ||||
| 		public bool Locked | ||||
| 		{ | ||||
| 			get | ||||
| 			{ | ||||
| 				return _locked; | ||||
| 			} | ||||
| 			set | ||||
| 			{ | ||||
| 				Kp2aLog.Log("Locked=" + _locked); | ||||
| 				_locked = value; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		public bool DidOpenFileChange() | ||||
| 		{ | ||||
| 			if (Loaded == false) | ||||
| @@ -102,7 +83,10 @@ namespace keepass2android | ||||
| 		} | ||||
|  | ||||
| 		 | ||||
| 		public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, String password, String keyfile, ProgressDialogStatusLogger status) | ||||
| 		/// <summary> | ||||
| 		/// Do not call this method directly. Call App.Kp2a.LoadDatabase instead. | ||||
| 		/// </summary> | ||||
| 		public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, String password, String keyfile, ProgressDialogStatusLogger status) | ||||
| 		{ | ||||
| 			PwDatabase pwDatabase = new PwDatabase(); | ||||
|  | ||||
| @@ -121,11 +105,12 @@ namespace keepass2android | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			IFileStorage fileStorage = _app.GetFileStorage(iocInfo); | ||||
| 			var filename = fileStorage.GetFilenameWithoutPathAndExt(iocInfo); | ||||
| 			try | ||||
| 			{ | ||||
| 				IFileStorage fileStorage = _app.GetFileStorage(iocInfo); | ||||
| 				var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo); | ||||
| 				pwDatabase.Open(fileStorage.OpenFileForRead(iocInfo), fileStorage.GetFilenameWithoutPathAndExt(iocInfo), iocInfo, compositeKey, status); | ||||
| 				pwDatabase.Open(databaseData ?? fileStorage.OpenFileForRead(iocInfo), filename, iocInfo, compositeKey, status); | ||||
| 				LastFileVersion = fileVersion; | ||||
| 			} | ||||
| 			catch (Exception) | ||||
| @@ -135,8 +120,13 @@ namespace keepass2android | ||||
| 					//if we don't get a password, we don't know whether this means "empty password" or "no password" | ||||
| 					//retry without password: | ||||
| 					compositeKey.RemoveUserKey(compositeKey.GetUserKey(typeof (KcpPassword))); | ||||
| 					pwDatabase.Open(iocInfo, compositeKey, status); | ||||
| 				} | ||||
| 					if (databaseData != null) | ||||
| 					{ | ||||
| 						databaseData.Seek(0, SeekOrigin.Begin); | ||||
| 					} | ||||
| 					var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo); | ||||
| 					pwDatabase.Open(databaseData ?? fileStorage.OpenFileForRead(iocInfo), filename, iocInfo, compositeKey, status); | ||||
| 					LastFileVersion = fileVersion;				} | ||||
| 				else throw; | ||||
| 			} | ||||
| 			 | ||||
| @@ -151,15 +141,6 @@ namespace keepass2android | ||||
| 			SearchHelper = new SearchDbHelper(app); | ||||
| 		} | ||||
|  | ||||
| 		public bool QuickUnlockEnabled { get; set; } | ||||
|  | ||||
| 		//KeyLength of QuickUnlock at time of loading the database. | ||||
| 		//This is important to not allow an attacker to set the length to 1 when QuickUnlock is started already. | ||||
| 		public int QuickUnlockKeyLength | ||||
| 		{ | ||||
| 			get; | ||||
| 			set; | ||||
| 		} | ||||
| 		 | ||||
| 		public PwGroup SearchForText(String str) { | ||||
| 			PwGroup group = SearchHelper.SearchForText(this, str); | ||||
| @@ -225,7 +206,6 @@ namespace keepass2android | ||||
| 			Root = null; | ||||
| 			KpDatabase = null; | ||||
| 			_loaded = false; | ||||
| 			_locked = false; | ||||
| 			_reloadRequested = false; | ||||
| 		} | ||||
| 		 | ||||
|   | ||||
| @@ -79,7 +79,7 @@ namespace keepass2android | ||||
| 							else | ||||
| 							{ | ||||
| 								// Let's not bother recovering from a failure to save a deleted entry.  It is too much work. | ||||
| 								App.SetShutdown(); | ||||
| 								App.LockDatabase(); | ||||
| 							} | ||||
| 						}, OnFinishToRun); | ||||
| 				} | ||||
| @@ -99,7 +99,7 @@ namespace keepass2android | ||||
| 							Db.Dirty.Add(pgRecycleBin); | ||||
| 						} else { | ||||
| 							// Let's not bother recovering from a failure to save a deleted entry.  It is too much work. | ||||
| 							App.SetShutdown(); | ||||
| 							App.LockDatabase(); | ||||
| 						} | ||||
| 						 | ||||
| 					}, OnFinishToRun); | ||||
|   | ||||
| @@ -108,7 +108,7 @@ namespace keepass2android | ||||
| 						Db.Dirty.Add(pgParent); | ||||
| 					} else { | ||||
| 						// Let's not bother recovering from a failure to save a deleted group.  It is too much work. | ||||
| 						App.SetShutdown(); | ||||
| 						App.LockDatabase(); | ||||
| 					} | ||||
| 				}, OnFinishToRun); | ||||
| 			} | ||||
| @@ -146,7 +146,7 @@ namespace keepass2android | ||||
| 					} | ||||
| 				} else { | ||||
| 					// Let's not bother recovering from a failure to save a deleted group.  It is too much work. | ||||
| 					_app.SetShutdown(); | ||||
| 					_app.LockDatabase(); | ||||
| 				} | ||||
| 				 | ||||
| 				base.Run(); | ||||
|   | ||||
| @@ -16,21 +16,25 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using System.IO; | ||||
| using System.Threading.Tasks; | ||||
| using KeePassLib.Serialization; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| 	public class LoadDb : RunnableOnFinish { | ||||
| 		private readonly IOConnectionInfo _ioc; | ||||
| 		private readonly Task<MemoryStream> _databaseData; | ||||
| 		private readonly String _pass; | ||||
| 		private readonly String _key; | ||||
| 		private readonly IKp2aApp _app; | ||||
| 		private readonly bool _rememberKeyfile; | ||||
| 		 | ||||
| 		public LoadDb(IKp2aApp app, IOConnectionInfo ioc, String pass, String key, OnFinish finish): base(finish) | ||||
| 		public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, String pass, String key, OnFinish finish): base(finish) | ||||
| 		{ | ||||
| 			_app = app; | ||||
| 			_ioc = ioc; | ||||
| 			_databaseData = databaseData; | ||||
| 			_pass = pass; | ||||
| 			_key = key; | ||||
|  | ||||
| @@ -44,7 +48,7 @@ namespace keepass2android | ||||
| 			try | ||||
| 			{ | ||||
| 				StatusLogger.UpdateMessage(UiStringKey.loading_database); | ||||
| 				_app.GetDb().LoadData (_app, _ioc, _pass, _key, StatusLogger); | ||||
| 				_app.LoadDatabase(_ioc, _databaseData == null ? null : _databaseData.Result, _pass, _key, StatusLogger); | ||||
| 				SaveFileData (_ioc, _key); | ||||
| 				 | ||||
| 			} catch (KeyFileException) { | ||||
|   | ||||
| @@ -95,7 +95,7 @@ namespace keepass2android | ||||
|  | ||||
| 			Database db = App.Kp2a.GetDb(); | ||||
| 			// Likely the app has been killed exit the activity  | ||||
| 			if (! db.Loaded) | ||||
| 			if (!db.Loaded || (App.Kp2a.QuickLocked)) | ||||
| 			{ | ||||
| 				Finish(); | ||||
| 				return; | ||||
| @@ -600,9 +600,7 @@ namespace keepass2android | ||||
| 				} | ||||
| 					return true; | ||||
| 			case Resource.Id.menu_lock: | ||||
|                 App.Kp2a.SetShutdown(); | ||||
| 				SetResult(KeePass.ExitLock); | ||||
| 				Finish(); | ||||
|                 App.Kp2a.LockDatabase(); | ||||
| 				return true; | ||||
| 			case Resource.Id.menu_translate: | ||||
| 				try { | ||||
|   | ||||
| @@ -92,8 +92,7 @@ namespace keepass2android | ||||
| 			_closeForReload = false; | ||||
|  | ||||
| 			// Likely the app has been killed exit the activity | ||||
| 			Database db = App.Kp2a.GetDb(); | ||||
| 			if (! db.Open) | ||||
| 			if (!App.Kp2a.DatabaseIsUnlocked) | ||||
| 			{ | ||||
| 				Finish(); | ||||
| 				return; | ||||
| @@ -107,6 +106,8 @@ namespace keepass2android | ||||
|  | ||||
| 			} else | ||||
| 			{ | ||||
| 				Database db = App.Kp2a.GetDb(); | ||||
|  | ||||
| 				App.Kp2a.EntryEditActivityState = new EntryEditActivityState(); | ||||
| 				ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this); | ||||
| 				State.ShowPassword = ! prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default)); | ||||
|   | ||||
| @@ -240,9 +240,7 @@ namespace keepass2android | ||||
| 				 | ||||
| 				return true; | ||||
| 			case Resource.Id.menu_lock: | ||||
| 				App.Kp2a.SetShutdown(); | ||||
| 				SetResult(KeePass.ExitLock); | ||||
| 				Finish(); | ||||
| 				App.Kp2a.LockDatabase(); | ||||
| 				return true; | ||||
|  | ||||
| 			case Resource.Id.menu_search: | ||||
| @@ -251,7 +249,7 @@ namespace keepass2android | ||||
| 				return true; | ||||
| 				 | ||||
| 			case Resource.Id.menu_app_settings: | ||||
| 				AppSettingsActivity.Launch(this); | ||||
| 				DatabaseSettingsActivity.Launch(this); | ||||
| 				return true; | ||||
| 				 | ||||
| 			case Resource.Id.menu_change_master_key: | ||||
| @@ -353,8 +351,7 @@ namespace keepass2android | ||||
| 						Toast.MakeText(_act,  "Unrecoverable error: " + Message, ToastLength.Long).Show(); | ||||
| 					}); | ||||
|  | ||||
| 					App.Kp2a.SetShutdown(); | ||||
| 					_act.Finish(); | ||||
| 					App.Kp2a.LockDatabase(false); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
|   | ||||
| @@ -24,6 +24,7 @@ using Android.Preferences; | ||||
| using Android.Content.PM; | ||||
| using Android.Text; | ||||
| using Android.Text.Method; | ||||
| using KeePassLib.Serialization; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| @@ -37,12 +38,8 @@ namespace keepass2android | ||||
| 		public const Result ExitLock = Result.FirstUser+1; | ||||
| 		public const Result ExitRefresh = Result.FirstUser+2; | ||||
| 		public const Result ExitRefreshTitle = Result.FirstUser+3; | ||||
| 		public const Result ExitForceLock = Result.FirstUser+4; | ||||
| 		public const Result ExitQuickUnlock = Result.FirstUser+5; | ||||
| 		public const Result ExitCloseAfterTaskComplete = Result.FirstUser+6; | ||||
| 		public const Result ExitChangeDb = Result.FirstUser+7; | ||||
| 		public const Result ExitForceLockAndChangeDb = Result.FirstUser+8; | ||||
| 		public const Result ExitReloadDb = Result.FirstUser+9; | ||||
| 		public const Result ExitCloseAfterTaskComplete = Result.FirstUser+4; | ||||
| 		public const Result ExitReloadDb = Result.FirstUser+6; | ||||
|  | ||||
| 		AppTask _appTask; | ||||
|  | ||||
| @@ -106,7 +103,7 @@ namespace keepass2android | ||||
| 				Dialog dialog = builder.Create(); | ||||
| 				dialog.DismissEvent += (sender, e) =>  | ||||
| 				{ | ||||
| 					StartFileSelect(); | ||||
| 					LaunchNextActivity(); | ||||
| 				}; | ||||
| 				dialog.Show(); | ||||
| 				TextView message = (TextView) dialog.FindViewById(Android.Resource.Id.Message); | ||||
| @@ -119,7 +116,7 @@ namespace keepass2android | ||||
|  | ||||
| 			} else | ||||
| 			{ | ||||
| 				StartFileSelect(); | ||||
| 				LaunchNextActivity(); | ||||
| 			} | ||||
|  | ||||
|  | ||||
| @@ -157,12 +154,42 @@ namespace keepass2android | ||||
|  | ||||
| 		} | ||||
| 		 | ||||
| 		private void StartFileSelect() { | ||||
| 			Intent intent = new Intent(this, typeof(FileSelectActivity)); | ||||
| 			//TEST Intent intent = new Intent(this, typeof(EntryActivity)); | ||||
| 			//Intent intent = new Intent(this, typeof(SearchActivity)); | ||||
| 			//Intent intent = new Intent(this, typeof(QuickUnlock)); | ||||
| 		IOConnectionInfo LoadIoc(string defaultFileName) | ||||
| 		{ | ||||
| 			return App.Kp2a.FileDbHelper.CursorToIoc(App.Kp2a.FileDbHelper.FetchFileByName(defaultFileName)); | ||||
| 		} | ||||
|  | ||||
| 		private void LaunchNextActivity() { | ||||
|  | ||||
| 			if (!App.Kp2a.GetDb().Loaded) | ||||
| 			{ | ||||
| 				// Load default database | ||||
| 				ISharedPreferences prefs = Android.Preferences.PreferenceManager.GetDefaultSharedPreferences(this); | ||||
| 				String defaultFileName = prefs.GetString(PasswordActivity.KeyDefaultFilename, ""); | ||||
|  | ||||
| 				if (defaultFileName.Length > 0) | ||||
| 				{ | ||||
| 					try | ||||
| 					{ | ||||
| 						PasswordActivity.Launch(this, LoadIoc(defaultFileName), _appTask); | ||||
| 						Finish(); | ||||
| 						return; | ||||
| 					} | ||||
| 					catch (Exception e) | ||||
| 					{ | ||||
| 						Toast.MakeText(this, e.Message, ToastLength.Long); | ||||
| 						// Ignore exception | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				PasswordActivity.Launch(this, App.Kp2a.GetDb().Ioc, _appTask); | ||||
| 				Finish(); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			Intent intent = new Intent(this, typeof(FileSelectActivity)); | ||||
| 			_appTask.ToIntent(intent); | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -56,8 +56,7 @@ namespace keepass2android | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				Kp2aLog.Log(" Loaded=" + App.Kp2a.GetDb().Loaded + ", Locked=" + App.Kp2a.GetDb().Locked  | ||||
| 					+ ", shutdown=" + App.Kp2a.IsShutdown()); | ||||
| 				Kp2aLog.Log(" DatabaseIsUnlocked=" + App.Kp2a.DatabaseIsUnlocked); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,10 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file | ||||
|   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>. | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using Android.App; | ||||
| using KeePassLib.Serialization; | ||||
|  | ||||
| namespace keepass2android | ||||
| @@ -26,12 +29,25 @@ namespace keepass2android | ||||
| 	/// Checks in OnResume whether the timeout occured and the database must be locked/closed. | ||||
| 	public class LockCloseActivity : LockingActivity { | ||||
|  | ||||
| 		IOConnectionInfo _ioc; | ||||
| 		private IOConnectionInfo _ioc; | ||||
| 		private BroadcastReceiver _intentReceiver; | ||||
|  | ||||
| 		protected override void OnCreate(Bundle savedInstanceState) | ||||
| 		{ | ||||
| 			base.OnCreate(savedInstanceState); | ||||
| 			_ioc = App.Kp2a.GetDb().Ioc; | ||||
|  | ||||
| 			_intentReceiver = new LockCloseActivityBroadcastReceiver(this); | ||||
| 			IntentFilter filter = new IntentFilter(); | ||||
| 			filter.AddAction(Intents.DatabaseLocked); | ||||
| 			RegisterReceiver(_intentReceiver, filter); | ||||
| 		} | ||||
|  | ||||
| 		protected override void OnDestroy() | ||||
| 		{ | ||||
| 			UnregisterReceiver(_intentReceiver); | ||||
|  | ||||
| 			base.OnDestroy(); | ||||
| 		} | ||||
|  | ||||
|  | ||||
| @@ -49,9 +65,32 @@ namespace keepass2android | ||||
| 			App.Kp2a.CheckForOpenFileChanged(this); | ||||
| 		} | ||||
|  | ||||
| 		private void OnLockDatabase() | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Finishing " + ComponentName.ClassName + " due to database lock"); | ||||
|  | ||||
| 			SetResult(KeePass.ExitLock); | ||||
| 			Finish(); | ||||
| 		} | ||||
|  | ||||
| 		private class LockCloseActivityBroadcastReceiver : BroadcastReceiver | ||||
| 		{			 | ||||
| 			readonly LockCloseActivity _service; | ||||
| 			public LockCloseActivityBroadcastReceiver(LockCloseActivity service) | ||||
| 			{ | ||||
| 				_service = service; | ||||
| 			} | ||||
|  | ||||
| 			public override void OnReceive(Context context, Intent intent) | ||||
| 			{ | ||||
| 				switch (intent.Action) | ||||
| 				{ | ||||
| 					case Intents.DatabaseLocked: | ||||
| 						_service.OnLockDatabase(); | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using Android.Runtime; | ||||
| using KeePassLib.Serialization; | ||||
| @@ -33,11 +34,18 @@ namespace keepass2android | ||||
| 		} | ||||
|  | ||||
| 		IOConnectionInfo _ioc; | ||||
| 		private BroadcastReceiver _intentReceiver; | ||||
| 		 | ||||
| 		protected override void OnCreate(Bundle savedInstanceState) | ||||
| 		{ | ||||
| 			base.OnCreate(savedInstanceState); | ||||
| 			_ioc = App.Kp2a.GetDb().Ioc; | ||||
|  | ||||
| 			_intentReceiver = new LockCloseListActivityBroadcastReceiver(this); | ||||
| 			IntentFilter filter = new IntentFilter(); | ||||
| 			filter.AddAction(Intents.DatabaseLocked); | ||||
| 			RegisterReceiver(_intentReceiver, filter); | ||||
|  | ||||
| 		} | ||||
| 		 | ||||
| 		public LockCloseListActivity (IntPtr javaReference, JniHandleOwnership transfer) | ||||
| @@ -57,6 +65,39 @@ namespace keepass2android | ||||
| 			App.Kp2a.CheckForOpenFileChanged(this); | ||||
| 		} | ||||
|  | ||||
| 		protected override void OnDestroy() | ||||
| 		{ | ||||
| 			UnregisterReceiver(_intentReceiver); | ||||
|  | ||||
| 			base.OnDestroy(); | ||||
| 		} | ||||
|  | ||||
| 		private void OnLockDatabase() | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Finishing " + ComponentName.ClassName + " due to database lock"); | ||||
|  | ||||
| 			SetResult(KeePass.ExitLock); | ||||
| 			Finish(); | ||||
| 		} | ||||
|  | ||||
| 		private class LockCloseListActivityBroadcastReceiver : BroadcastReceiver | ||||
| 		{ | ||||
| 			readonly LockCloseListActivity _service; | ||||
| 			public LockCloseListActivityBroadcastReceiver(LockCloseListActivity service) | ||||
| 			{ | ||||
| 				_service = service; | ||||
| 			} | ||||
|  | ||||
| 			public override void OnReceive(Context context, Intent intent) | ||||
| 			{ | ||||
| 				switch (intent.Action) | ||||
| 				{ | ||||
| 					case Intents.DatabaseLocked: | ||||
| 						_service.OnLockDatabase(); | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -15,6 +15,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file | ||||
|   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>. | ||||
|   */ | ||||
|  | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using KeePassLib.Serialization; | ||||
|  | ||||
| @@ -25,11 +26,18 @@ namespace keepass2android | ||||
|  | ||||
| 		 | ||||
| 		IOConnectionInfo _ioc; | ||||
| 		private BroadcastReceiver _intentReceiver; | ||||
| 		 | ||||
| 		protected override void OnCreate(Bundle savedInstanceState) | ||||
| 		{ | ||||
| 			base.OnCreate(savedInstanceState); | ||||
| 			_ioc = App.Kp2a.GetDb().Ioc; | ||||
|  | ||||
|  | ||||
| 			_intentReceiver = new LockingClosePreferenceActivityBroadcastReceiver(this); | ||||
| 			IntentFilter filter = new IntentFilter(); | ||||
| 			filter.AddAction(Intents.DatabaseLocked); | ||||
| 			RegisterReceiver(_intentReceiver, filter); | ||||
| 		} | ||||
|  | ||||
| 		protected override void OnResume() { | ||||
| @@ -37,6 +45,41 @@ namespace keepass2android | ||||
| 			 | ||||
| 			TimeoutHelper.CheckShutdown(this, _ioc); | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		protected override void OnDestroy() | ||||
| 		{ | ||||
| 			UnregisterReceiver(_intentReceiver); | ||||
|  | ||||
| 			base.OnDestroy(); | ||||
| 		} | ||||
|  | ||||
| 		private void OnLockDatabase() | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Finishing " + ComponentName.ClassName + " due to database lock"); | ||||
|  | ||||
| 			SetResult(KeePass.ExitLock); | ||||
| 			Finish(); | ||||
| 		} | ||||
|  | ||||
| 		private class LockingClosePreferenceActivityBroadcastReceiver : BroadcastReceiver | ||||
| 		{ | ||||
| 			readonly LockingClosePreferenceActivity _service; | ||||
| 			public LockingClosePreferenceActivityBroadcastReceiver(LockingClosePreferenceActivity service) | ||||
| 			{ | ||||
| 				_service = service; | ||||
| 			} | ||||
|  | ||||
| 			public override void OnReceive(Context context, Intent intent) | ||||
| 			{ | ||||
| 				switch (intent.Action) | ||||
| 				{ | ||||
| 					case Intents.DatabaseLocked: | ||||
| 						_service.OnLockDatabase(); | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| using Android.App; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| @@ -29,6 +30,9 @@ using Android.Text; | ||||
| using Android.Content.PM; | ||||
| using KeePassLib.Keys; | ||||
| using KeePassLib.Serialization; | ||||
| using KeePassLib.Utility; | ||||
|  | ||||
| using MemoryStream = System.IO.MemoryStream; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| @@ -49,11 +53,14 @@ namespace keepass2android | ||||
|  | ||||
| 		private const String ViewIntent = "android.intent.action.VIEW"; | ||||
|  | ||||
| 		private Task<MemoryStream> _loadDbTask; | ||||
| 		private IOConnectionInfo _ioConnection; | ||||
| 		private String _keyFile; | ||||
| 		private bool _rememberKeyfile; | ||||
| 		ISharedPreferences _prefs; | ||||
|  | ||||
| 		private bool _started; | ||||
|  | ||||
| 		public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer) | ||||
| 			: base(javaReference, transfer) | ||||
| 		{ | ||||
| @@ -123,114 +130,28 @@ namespace keepass2android | ||||
| 		public void LaunchNextActivity() | ||||
| 		{ | ||||
| 			AppTask.AfterUnlockDatabase(this); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		void UnloadDatabase() | ||||
| 		{ | ||||
| 			App.Kp2a.GetDb().Clear(); | ||||
| 			StopService(new Intent(this, typeof(QuickUnlockForegroundService))); | ||||
| 		} | ||||
|  | ||||
| 		void LockDatabase() | ||||
| 		{ | ||||
| 			SetResult(KeePass.ExitLock); | ||||
| 			SetEditText(Resource.Id.password, ""); | ||||
| 			if (App.Kp2a.GetDb().QuickUnlockEnabled) | ||||
| 				App.Kp2a.GetDb().Locked = true; | ||||
| 			else | ||||
| 			{ | ||||
| 				UnloadDatabase(); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		void LockAndClose() | ||||
| 		{ | ||||
| 			LockDatabase(); | ||||
| 			Finish(); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		bool TryStartQuickUnlock() | ||||
| 		{ | ||||
| 			if (!App.Kp2a.GetDb().QuickUnlockEnabled) | ||||
| 				return false; | ||||
|  | ||||
| 			if (App.Kp2a.GetDb().KpDatabase.MasterKey.ContainsType(typeof(KcpPassword)) == false) | ||||
| 				return false; | ||||
| 			KcpPassword kcpPassword = (KcpPassword)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpPassword)); | ||||
| 			String password = kcpPassword.Password.ReadString(); | ||||
|  | ||||
| 			if (password.Length == 0) | ||||
| 				return false; | ||||
|  | ||||
| 			App.Kp2a.GetDb().Locked = true; | ||||
|  | ||||
| 			Intent i = new Intent(this, typeof(QuickUnlock)); | ||||
| 			PutIoConnectionToIntent(_ioConnection, i); | ||||
| 			Kp2aLog.Log("Starting QuickUnlock"); | ||||
| 			StartActivityForResult(i,0); | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		public void StartQuickUnlockForegroundService() | ||||
| 		{ | ||||
| 			if (App.Kp2a.GetDb().QuickUnlockEnabled) | ||||
| 			{ | ||||
| 				StartService(new Intent(this, typeof(QuickUnlockForegroundService))); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		bool _startedWithActivityResult; | ||||
|  | ||||
| 		protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) | ||||
| 		{ | ||||
| 			base.OnActivityResult(requestCode, resultCode, data); | ||||
|  | ||||
| 			_startedWithActivityResult = true; | ||||
| 			Kp2aLog.Log("PasswordActivity.OnActivityResult "+resultCode+"/"+requestCode); | ||||
|  | ||||
| 			if (resultCode != KeePass.ExitCloseAfterTaskComplete) | ||||
| 			{ | ||||
| 				//Stop service when app activity is left | ||||
| 				StopService(new Intent(this, typeof(CopyToClipboardService))); | ||||
| 			} | ||||
|  | ||||
| 			//NOTE: original code from k eepassdroid used switch ((Android.App.Result)requestCode) { (but doesn't work here, although k eepassdroid works) | ||||
| 			switch(resultCode) { | ||||
|  | ||||
| 				case KeePass.ExitNormal: | ||||
| 					if (!TryStartQuickUnlock()) | ||||
| 					{ | ||||
| 						SetEditText(Resource.Id.password, ""); | ||||
| 					} | ||||
| 				case KeePass.ExitNormal: // Returned to this screen using the Back key, treat as locking the database | ||||
| 					App.Kp2a.LockDatabase(); | ||||
| 					break; | ||||
| 					 | ||||
| 				case KeePass.ExitLock: | ||||
| 					if (!TryStartQuickUnlock()) | ||||
| 					{ | ||||
| 						LockAndClose(); | ||||
| 					} | ||||
| 					break; | ||||
| 				case KeePass.ExitForceLock: | ||||
| 					SetEditText(Resource.Id.password, ""); | ||||
| 					UnloadDatabase(); | ||||
| 					break; | ||||
| 				case KeePass.ExitForceLockAndChangeDb: | ||||
| 					UnloadDatabase(); | ||||
| 					Finish(); | ||||
| 					break; | ||||
| 				case KeePass.ExitChangeDb: | ||||
| 					LockAndClose(); | ||||
| 					// The database has already been locked, and the quick unlock screen will be shown if appropriate | ||||
| 					break; | ||||
| 				case KeePass.ExitCloseAfterTaskComplete: | ||||
| 					// Do not lock the database | ||||
| 					SetResult(KeePass.ExitCloseAfterTaskComplete); | ||||
| 					Finish(); | ||||
| 					break; | ||||
| 				case KeePass.ExitQuickUnlock: | ||||
| 					App.Kp2a.GetDb().Locked = false; | ||||
| 					LaunchNextActivity(); | ||||
| 					break; | ||||
| 				case KeePass.ExitReloadDb: | ||||
| 					//if the activity was killed, fill password/keyfile so the user can directly hit load again | ||||
| 					if (App.Kp2a.GetDb().Loaded) | ||||
| @@ -252,9 +173,9 @@ namespace keepass2android | ||||
| 							SetEditText(Resource.Id.pass_keyfile, kcpKeyfile.Path); | ||||
| 						} | ||||
| 					} | ||||
| 					UnloadDatabase(); | ||||
| 					App.Kp2a.LockDatabase(false); | ||||
| 					break; | ||||
| 				case Result.Ok: | ||||
| 				case Result.Ok: // Key file browse dialog OK'ed. | ||||
| 					if (requestCode == Intents.RequestCodeFileBrowseForKeyfile) { | ||||
| 						string filename = Util.IntentToFilename(data); | ||||
| 						if (filename != null) { | ||||
| @@ -331,6 +252,13 @@ namespace keepass2android | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (App.Kp2a.GetDb().Loaded && App.Kp2a.GetDb().Ioc != null && | ||||
| 				App.Kp2a.GetDb().Ioc.GetDisplayName() != _ioConnection.GetDisplayName()) | ||||
| 			{ | ||||
| 				// A different database is currently loaded, unload it before loading the new one requested | ||||
| 				App.Kp2a.LockDatabase(false); | ||||
| 			} | ||||
|  | ||||
| 			AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); | ||||
| 			 | ||||
| 			SetContentView(Resource.Layout.password); | ||||
| @@ -343,8 +271,7 @@ namespace keepass2android | ||||
| 			Window.SetSoftInputMode(SoftInput.StateVisible); | ||||
|  | ||||
| 			Button confirmButton = (Button)FindViewById(Resource.Id.pass_ok); | ||||
| 			confirmButton.Click += (sender, e) =>  | ||||
| 			{ | ||||
| 			confirmButton.Click += (sender, e) => { | ||||
| 				String pass = GetEditText(Resource.Id.password); | ||||
| 				String key = GetEditText(Resource.Id.pass_keyfile); | ||||
| 				if (pass.Length == 0 && key.Length == 0) | ||||
| @@ -353,20 +280,16 @@ namespace keepass2android | ||||
| 					return; | ||||
| 				} | ||||
|  | ||||
| 				// Clear before we load | ||||
| 				UnloadDatabase(); | ||||
| 				 | ||||
| 				// Clear the shutdown flag | ||||
| 				App.Kp2a.ClearShutdown(); | ||||
|  | ||||
| 				CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock); | ||||
| 				App.Kp2a.GetDb().QuickUnlockEnabled = cbQuickUnlock.Checked; | ||||
| 				App.Kp2a.GetDb().QuickUnlockKeyLength = int.Parse(_prefs.GetString(GetString(Resource.String.QuickUnlockLength_key), GetString(Resource.String.QuickUnlockLength_default))); | ||||
| 				App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked); | ||||
|  | ||||
| 				Handler handler = new Handler(); | ||||
| 				LoadDb task = new LoadDb(App.Kp2a, _ioConnection, pass, key, new AfterLoad(handler, this)); | ||||
| 				ProgressTask pt = new ProgressTask(App.Kp2a, this, task); | ||||
| 				pt.Run(); | ||||
| 				LoadDb task = new LoadDb(App.Kp2a, _ioConnection, _loadDbTask, pass, key, new AfterLoad(handler, this)); | ||||
| 				_loadDbTask = null; // prevent accidental re-use | ||||
|  | ||||
| 				SetNewDefaultFile(); | ||||
|  | ||||
| 				new ProgressTask(App.Kp2a, this, task).Run(); | ||||
| 			}; | ||||
| 			 | ||||
| 			/*CheckBox checkBox = (CheckBox) FindViewById(Resource.Id.show_password); | ||||
| @@ -394,31 +317,7 @@ namespace keepass2android | ||||
| 				} | ||||
| 			}; | ||||
| 			 | ||||
| 			CheckBox defaultCheck = (CheckBox)FindViewById(Resource.Id.default_database); | ||||
| 			//Don't allow the current file to be the default if we don't have stored credentials | ||||
| 			if ((_ioConnection.IsLocalFile() == false) && (_ioConnection.CredSaveMode != IOCredSaveMode.SaveCred)) | ||||
| 			{ | ||||
| 				defaultCheck.Enabled = false; | ||||
| 			} else | ||||
| 			{ | ||||
| 				defaultCheck.Enabled = true; | ||||
| 			} | ||||
| 			defaultCheck.CheckedChange += (sender, e) =>  | ||||
| 			{ | ||||
| 				String newDefaultFileName; | ||||
| 			 | ||||
| 				if (e.IsChecked) | ||||
| 				{ | ||||
| 					newDefaultFileName = _ioConnection.Path; | ||||
| 				} else | ||||
| 				{ | ||||
| 					newDefaultFileName = ""; | ||||
| 				} | ||||
| 				 | ||||
| 				ISharedPreferencesEditor editor = _prefs.Edit(); | ||||
| 				editor.PutString(KeyDefaultFilename, newDefaultFileName); | ||||
| 				EditorCompat.Apply(editor); | ||||
| 			}; | ||||
| 			 | ||||
| 			ImageButton browse = (ImageButton)FindViewById(Resource.Id.browse_button); | ||||
| 			browse.Click += (sender, evt) =>  | ||||
| @@ -438,8 +337,66 @@ namespace keepass2android | ||||
| 			}; | ||||
| 			 | ||||
| 			RetrieveSettings(); | ||||
| 		} | ||||
|  | ||||
| 		private void SetNewDefaultFile() | ||||
| 		{ | ||||
| //Don't allow the current file to be the default if we don't have stored credentials | ||||
| 			bool makeFileDefault; | ||||
| 			if ((_ioConnection.IsLocalFile() == false) && (_ioConnection.CredSaveMode != IOCredSaveMode.SaveCred)) | ||||
| 			{ | ||||
| 				makeFileDefault = false; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				makeFileDefault = true; | ||||
| 			} | ||||
| 			String newDefaultFileName; | ||||
|  | ||||
| 			if (makeFileDefault) | ||||
| 			{ | ||||
| 				newDefaultFileName = _ioConnection.Path; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				newDefaultFileName = ""; | ||||
| 			} | ||||
|  | ||||
| 			ISharedPreferencesEditor editor = _prefs.Edit(); | ||||
| 			editor.PutString(KeyDefaultFilename, newDefaultFileName); | ||||
| 			EditorCompat.Apply(editor); | ||||
| 		} | ||||
|  | ||||
| 		protected override void OnStart() | ||||
| 		{ | ||||
| 			base.OnStart(); | ||||
| 			_started = true; | ||||
| 		} | ||||
|  | ||||
| 		private MemoryStream LoadDbFile() | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Pre-loading database file starting"); | ||||
| 			var fileStorage = App.Kp2a.GetFileStorage(_ioConnection); | ||||
| 			var stream = fileStorage.OpenFileForRead(_ioConnection); | ||||
|  | ||||
| 			var memoryStream = stream as MemoryStream; | ||||
| 			if (memoryStream == null) | ||||
| 			{ | ||||
| 				// Read the file into memory | ||||
| 				int capacity = 4096; // Default initial capacity, if stream can't report it. | ||||
| 				if (stream.CanSeek) | ||||
| 				{ | ||||
| 					capacity = (int)stream.Length; | ||||
| 				} | ||||
| 				memoryStream = new MemoryStream(capacity); | ||||
| 				stream.CopyTo(memoryStream); | ||||
| 				stream.Close(); | ||||
| 				memoryStream.Seek(0, System.IO.SeekOrigin.Begin); | ||||
| 			} | ||||
|  | ||||
| 			Kp2aLog.Log("Pre-loading database file completed"); | ||||
|  | ||||
| 			return memoryStream; | ||||
| 		} | ||||
|  | ||||
| 		protected override void OnSaveInstanceState(Bundle outState) | ||||
| @@ -448,41 +405,35 @@ namespace keepass2android | ||||
| 			AppTask.ToBundle(outState); | ||||
| 		} | ||||
| 		 | ||||
| 		protected override void OnResume() { | ||||
| 		protected override void OnResume() | ||||
| 		{ | ||||
| 			base.OnResume(); | ||||
|  | ||||
| 			// If the application was shutdown make sure to clear the password field, if it | ||||
| 			// was saved in the instance state | ||||
| 			if (App.Kp2a.IsShutdown()) { | ||||
| 				LockDatabase(); | ||||
| 			} | ||||
|  | ||||
| 			// Clear the shutdown flag | ||||
|             App.Kp2a.ClearShutdown(); | ||||
|  | ||||
| 			if (_startedWithActivityResult) | ||||
| 				return; | ||||
|  | ||||
| 			if (App.Kp2a.GetDb().Loaded && (App.Kp2a.GetDb().Ioc != null) | ||||
| 			    && (_ioConnection != null) && (App.Kp2a.GetDb().Ioc.GetDisplayName() == _ioConnection.GetDisplayName())) | ||||
| 			// OnResume is run every time the activity comes to the foreground. This code should only run when the activity is started (OnStart), but must | ||||
| 			// be run in OnResume rather than OnStart so that it always occurrs after OnActivityResult (when re-creating a killed activity, OnStart occurs before OnActivityResult) | ||||
| 			if (_started && !IsFinishing)  //use !IsFinishing to make sure we're not starting another activity when we're already finishing (e.g. due to TaskComplete in OnActivityResult) | ||||
| 			{ | ||||
| 				if (App.Kp2a.GetDb().Locked == false) | ||||
| 				_started = false; | ||||
| 				if (App.Kp2a.DatabaseIsUnlocked) | ||||
| 				{ | ||||
| 					LaunchNextActivity(); | ||||
| 				} | ||||
| 				else  | ||||
| 				else if (App.Kp2a.QuickUnlockEnabled && App.Kp2a.QuickLocked) | ||||
| 				{ | ||||
| 					TryStartQuickUnlock(); | ||||
| 					var i = new Intent(this, typeof(QuickUnlock)); | ||||
| 					PutIoConnectionToIntent(_ioConnection, i); | ||||
| 					Kp2aLog.Log("Starting QuickUnlock"); | ||||
| 					StartActivityForResult(i, 0); | ||||
| 				} | ||||
| 				else if (_loadDbTask == null && _prefs.GetBoolean(GetString(Resource.String.PreloadDatabaseEnabled_key), true)) | ||||
| 				{ | ||||
| 					// Create task to kick off file loading while the user enters the password | ||||
| 					_loadDbTask = Task.Factory.StartNew<MemoryStream>(LoadDbFile); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		private void RetrieveSettings() { | ||||
| 			String defaultFilename = _prefs.GetString(KeyDefaultFilename, ""); | ||||
| 			if (!String.IsNullOrEmpty(_ioConnection.Path) && _ioConnection.Path.Equals(defaultFilename)) { | ||||
| 				CheckBox checkbox = (CheckBox) FindViewById(Resource.Id.default_database); | ||||
| 				checkbox.Checked = true; | ||||
| 			} | ||||
| 			CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock); | ||||
| 			cbQuickUnlock.Checked = _prefs.GetBoolean(GetString(Resource.String.QuickUnlockDefaultEnabled_key), true); | ||||
| 		} | ||||
| @@ -540,31 +491,50 @@ namespace keepass2android | ||||
| 		 | ||||
| 		public override bool OnOptionsItemSelected(IMenuItem item) { | ||||
| 			switch ( item.ItemId ) { | ||||
| 			case Resource.Id.menu_about: | ||||
| 				AboutDialog dialog = new AboutDialog(this); | ||||
| 				dialog.Show(); | ||||
| 				return true; | ||||
| 				case Resource.Id.menu_about: | ||||
| 					AboutDialog dialog = new AboutDialog(this); | ||||
| 					dialog.Show(); | ||||
| 					return true; | ||||
| 				 | ||||
| 				case Resource.Id.menu_app_settings: | ||||
| 					AppSettingsActivity.Launch(this); | ||||
| 					return true; | ||||
|  | ||||
| 				case Resource.Id.menu_change_db: | ||||
| 					Intent intent = new Intent(this, typeof(FileSelectActivity)); | ||||
| 					AppTask.ToIntent(intent); | ||||
| 					StartActivityForResult(intent, 0); | ||||
| 					Finish(); | ||||
| 					return true; | ||||
|  | ||||
|  | ||||
| 			case Resource.Id.menu_app_settings: | ||||
| 				AppSettingsActivity.Launch(this); | ||||
| 				return true; | ||||
| 			} | ||||
|  | ||||
| 			 | ||||
| 			 | ||||
| 			return base.OnOptionsItemSelected(item); | ||||
| 		} | ||||
| 		 | ||||
| 		private class AfterLoad : OnFinish { | ||||
| 			readonly PasswordActivity _act; | ||||
| 			public AfterLoad(Handler handler, PasswordActivity act):base(handler) { | ||||
|  | ||||
| 			public AfterLoad(Handler handler, PasswordActivity act):base(handler) | ||||
| 			{ | ||||
| 				_act = act; | ||||
| 			} | ||||
|  | ||||
|  | ||||
| 			public override void Run() { | ||||
| 				if ( Success ) { | ||||
| 					_act.StartQuickUnlockForegroundService(); | ||||
| 				if ( Success )  | ||||
| 				{ | ||||
| 					_act.SetEditText(Resource.Id.password, ""); | ||||
|  | ||||
| 					_act.LaunchNextActivity(); | ||||
| 				} else { | ||||
|  | ||||
| 					GC.Collect(); // Ensure temporary memory used while loading is collected - it will contain sensitive data such as username and password, and also the large data of the encrypted database file | ||||
| 				}  | ||||
| 				else | ||||
| 				{ | ||||
| 					DisplayMessage(_act); | ||||
| 				} | ||||
| 			} | ||||
|   | ||||
| @@ -72,18 +72,13 @@ namespace keepass2android | ||||
|  | ||||
| 			TextView txtLabel = (TextView)FindViewById(Resource.Id.QuickUnlock_label); | ||||
|  | ||||
| 			int quickUnlockLength = App.Kp2a.GetDb().QuickUnlockKeyLength; | ||||
| 			int quickUnlockLength = App.Kp2a.QuickUnlockKeyLength; | ||||
|  | ||||
| 			txtLabel.Text = GetString(Resource.String.QuickUnlock_label, new Java.Lang.Object[]{quickUnlockLength}); | ||||
|  | ||||
| 			EditText pwd= (EditText)FindViewById(Resource.Id.QuickUnlock_password); | ||||
| 			pwd.SetEms(quickUnlockLength); | ||||
| 			pwd.PostDelayed(() => { | ||||
| 				InputMethodManager keyboard = (InputMethodManager)GetSystemService(Context.InputMethodService); | ||||
| 				keyboard.ShowSoftInput(pwd, 0); | ||||
| 			}, 50); | ||||
|  | ||||
| 			SetResult(KeePass.ExitChangeDb); | ||||
|  | ||||
| 			Button btnUnlock = (Button)FindViewById(Resource.Id.QuickUnlock_button); | ||||
| 			btnUnlock.Click += (object sender, EventArgs e) =>  | ||||
| @@ -93,11 +88,11 @@ namespace keepass2android | ||||
| 				String expectedPasswordPart = password.Substring(Math.Max(0,password.Length-quickUnlockLength),Math.Min(password.Length, quickUnlockLength)); | ||||
| 				if (pwd.Text == expectedPasswordPart) | ||||
| 				{ | ||||
| 					SetResult(KeePass.ExitQuickUnlock); | ||||
| 					App.Kp2a.UnlockDatabase(); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					SetResult(KeePass.ExitForceLock); | ||||
| 					App.Kp2a.LockDatabase(false); | ||||
| 					Toast.MakeText(this, GetString(Resource.String.QuickUnlock_fail), ToastLength.Long).Show(); | ||||
| 				} | ||||
| 				Finish(); | ||||
| @@ -106,21 +101,21 @@ namespace keepass2android | ||||
| 			Button btnLock = (Button)FindViewById(Resource.Id.QuickUnlock_buttonLock); | ||||
| 			btnLock.Click += (object sender, EventArgs e) =>  | ||||
| 			{ | ||||
| 				SetResult(KeePass.ExitForceLockAndChangeDb); | ||||
| 				App.Kp2a.LockDatabase(false); | ||||
| 				Finish(); | ||||
| 			}; | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		protected override void OnResume() | ||||
| 		{ | ||||
| 			base.OnResume(); | ||||
|  | ||||
| 			if ( ! App.Kp2a.GetDb().Loaded ) { | ||||
| 				SetResult(KeePass.ExitChangeDb); | ||||
| 				Finish(); | ||||
| 				return; | ||||
| 			} | ||||
| 			EditText pwd = (EditText)FindViewById(Resource.Id.QuickUnlock_password); | ||||
| 			pwd.PostDelayed(() => | ||||
| 			{ | ||||
| 				InputMethodManager keyboard = (InputMethodManager)GetSystemService(Context.InputMethodService); | ||||
| 				keyboard.ShowSoftInput(pwd, 0); | ||||
| 			}, 50); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								src/keepass2android/Resources/drawable-hdpi/ic_launcher_red.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/keepass2android/Resources/drawable-mdpi/ic_launcher_red.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/keepass2android/Resources/drawable-v11/ic_unlocked_gray.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/keepass2android/Resources/drawable-xhdpi/ic_launcher_red.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.2 KiB | 
| After Width: | Height: | Size: 5.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/keepass2android/Resources/drawable/ic_launcher_red.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/keepass2android/Resources/drawable/ic_unlocked_gray.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 KiB | 
| @@ -91,16 +91,10 @@ | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_below="@id/keyfileLine" /> | ||||
|     <CheckBox | ||||
|         android:id="@+id/default_database" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_below="@id/pass_ok" | ||||
|         android:text="@string/default_checkbox" /> | ||||
|     <CheckBox | ||||
|         android:id="@+id/enable_quickunlock" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_below="@id/default_database" | ||||
|         android:layout_below="@id/pass_ok" | ||||
|         android:text="@string/enable_quickunlock" /> | ||||
| </RelativeLayout> | ||||
| @@ -79,16 +79,10 @@ | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_below="@id/pass_keyfile" /> | ||||
|     <CheckBox | ||||
|         android:id="@+id/default_database" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_below="@id/pass_ok" | ||||
|         android:text="@string/default_checkbox" /> | ||||
|     <CheckBox | ||||
|         android:id="@+id/enable_quickunlock" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_below="@id/default_database" | ||||
|         android:layout_below="@id/pass_ok" | ||||
|         android:text="@string/enable_quickunlock" /> | ||||
| </RelativeLayout> | ||||
| @@ -20,6 +20,11 @@ | ||||
|         android:title="@string/menu_about" | ||||
|         android:icon="@android:drawable/ic_menu_help" | ||||
|         android:showAsAction="ifRoom" | ||||
|     /> | ||||
| 	<item android:id="@+id/menu_change_db" | ||||
|         android:icon="@drawable/collections_collection" | ||||
|         android:title="@string/menu_change_db" | ||||
| 		android:showAsAction="ifRoom" | ||||
|     /> | ||||
|     <item android:id="@+id/menu_app_settings" | ||||
|         android:title="@string/menu_app_settings" | ||||
|   | ||||
| @@ -19,6 +19,10 @@ | ||||
|     <item android:id="@+id/menu_app_settings" | ||||
|         android:icon="@android:drawable/ic_menu_preferences" | ||||
|         android:title="@string/menu_app_settings" | ||||
|     /> | ||||
| 	<item android:id="@+id/menu_change_db" | ||||
|         android:icon="@drawable/collections_collection" | ||||
|         android:title="@string/menu_change_db" | ||||
|     /> | ||||
|     <item android:id="@+id/menu_about" | ||||
|         android:icon="@android:drawable/ic_menu_help" | ||||
|   | ||||
| @@ -39,7 +39,6 @@ | ||||
|     <string name="omitbackup_key">omitbackup</string>     | ||||
|     <string name="list_size_key">list_size</string> | ||||
|     <string name="sort_key">sort_key</string> | ||||
|     <string name="timeout_key">timeout_key</string> | ||||
| 	<string name="TanExpiresOnUse_key">TanExpiresOnUse_key</string> | ||||
| 	<string name="ShowUsernameInList_key">ShowUsernameInList_key</string> | ||||
| 	<string name="RememberRecentFiles_key">RememberRecentFiles_key</string> | ||||
| @@ -92,4 +91,11 @@ | ||||
|     	<item>20</item> | ||||
|     	<item>28</item> | ||||
|     </string-array> | ||||
|  | ||||
| 	<string name="ShowUnlockedNotification_key">ShowUnlockedNotification</string> | ||||
| 	<bool name="ShowUnlockedNotification_default">true</bool> | ||||
|  | ||||
| 	<string name="PreloadDatabaseEnabled_key">PreloadDatabaseEnabled</string> | ||||
| 	<bool name="PreloadDatabaseEnabled_default">true</bool> | ||||
|  | ||||
| </resources> | ||||
| @@ -131,6 +131,7 @@ | ||||
|   <string name="menu_search">Search</string> | ||||
|   <string name="menu_search_advanced">Advanced Search</string> | ||||
|   <string name="menu_url">Go to URL</string> | ||||
| 	<string name="menu_change_db">Change database…</string> | ||||
|   <string name="minus">Minus</string> | ||||
|   <string name="never">Never</string> | ||||
|   <string name="yes">Yes</string> | ||||
| @@ -218,7 +219,8 @@ | ||||
|   <string name="add_binary">Add file attachment...</string> | ||||
|   <string name="add_extra_string">Add additional string</string> | ||||
|   <string name="delete_extra_string">Delete additional string</string> | ||||
|   <string name="database_loaded_quickunlock_enabled">Database loaded, QuickUnlock enabled.</string> | ||||
| 	<string name="database_loaded_quickunlock_enabled">%1$s: Locked. QuickUnlock enabled.</string> | ||||
| 	<string name="database_loaded_unlocked">%1$s: Unlocked.</string> | ||||
|   <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> | ||||
| @@ -232,7 +234,13 @@ | ||||
|   <string name="OpenKp2aKeyboardAutomatically_title">Keyboard selection dialog</string> | ||||
|   <string name="OpenKp2aKeyboardAutomatically_summary">Open keyboard selection dialog when entry is available through KP2A keyboard after search.</string> | ||||
|  | ||||
|   <string name="AskOverwriteBinary">Do you want to overwrite the existing binary with the same name?</string> | ||||
| 	<string name="ShowUnlockedNotification_title">Notification while unlocked</string> | ||||
| 	<string name="ShowUnlockedNotification_summary">Show an ongoing notification while the database is unlocked.</string> | ||||
|  | ||||
| 	<string name="PreloadDatabaseEnabled_title">Pre-load database file</string> | ||||
| 	<string name="PreloadDatabaseEnabled_summary">Start background loading or downloading of the database file during password entry.</string> | ||||
|  | ||||
| 	<string name="AskOverwriteBinary">Do you want to overwrite the existing binary with the same name?</string> | ||||
|   <string name="AskOverwriteBinary_title">Overwrite existing binary?</string> | ||||
|   <string name="AskOverwriteBinary_yes">Overwrite</string> | ||||
|   <string name="AskOverwriteBinary_no">Rename</string> | ||||
|   | ||||
| @@ -71,6 +71,13 @@ | ||||
| 		  android:entryValues="@array/clipboard_timeout_values" | ||||
| 		  android:dialogTitle="@string/app_timeout" | ||||
| 		  android:defaultValue="@string/clipboard_timeout_default"/> | ||||
| 		<CheckBoxPreference | ||||
| 			android:enabled="true" | ||||
| 				 android:persistent="true" | ||||
| 				 android:summary="@string/ShowUnlockedNotification_summary" | ||||
| 				 android:defaultValue="@bool/ShowUnlockedNotification_default" | ||||
| 				 android:title="@string/ShowUnlockedNotification_title" | ||||
| 				 android:key="@string/ShowUnlockedNotification_key" /> | ||||
| 		<CheckBoxPreference | ||||
| 		  android:key="@string/maskpass_key" | ||||
| 		  android:title="@string/maskpass_title" | ||||
| @@ -162,6 +169,12 @@ | ||||
| 			  android:defaultValue="true" | ||||
| 			  android:title="@string/CheckForFileChangesOnSave_title" | ||||
| 			  android:key="@string/CheckForFileChangesOnSave_key" /> | ||||
|  | ||||
| 		<CheckBoxPreference | ||||
| 			android:enabled="true" | ||||
| 				 android:persistent="true" | ||||
| 				 android:summary="@string/PreloadDatabaseEnabled_summary" | ||||
| 				 android:defaultValue="@bool/PreloadDatabaseEnabled_default" | ||||
| 				 android:title="@string/PreloadDatabaseEnabled_title" | ||||
| 				 android:key="@string/PreloadDatabaseEnabled_key" /> | ||||
| 	</PreferenceScreen> | ||||
| </PreferenceScreen> | ||||
|   | ||||
| @@ -71,7 +71,7 @@ namespace keepass2android | ||||
|  | ||||
| 				Finish(); | ||||
| 			} | ||||
| 			else if (_db.Locked) | ||||
| 			else if (App.Kp2a.QuickLocked) | ||||
| 			{ | ||||
| 				PasswordActivity.Launch(this,_db.Ioc, AppTask); | ||||
| 				Finish(); | ||||
|   | ||||
| @@ -16,11 +16,14 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using Android.App; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using Android.Runtime; | ||||
| using Android.Widget; | ||||
| using KeePassLib.Keys; | ||||
| using KeePassLib.Serialization; | ||||
| using Android.Preferences; | ||||
| using keepass2android.Io; | ||||
| @@ -60,25 +63,96 @@ namespace keepass2android | ||||
| 	/// </summary> | ||||
|     public class Kp2aApp: IKp2aApp, ICacheSupervisor | ||||
| 	{ | ||||
|         public bool IsShutdown() | ||||
|         { | ||||
|             return _shutdown; | ||||
| 		public void LockDatabase(bool allowQuickUnlock = true) | ||||
| 		{ | ||||
| 			if (GetDb().Loaded) | ||||
| 			{ | ||||
| 				if (QuickUnlockEnabled && allowQuickUnlock && | ||||
| 					_db.KpDatabase.MasterKey.ContainsType(typeof(KcpPassword)) && | ||||
| 					!((KcpPassword)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpPassword))).Password.IsEmpty) | ||||
| 				{ | ||||
| 					if (!QuickLocked) | ||||
| 					{ | ||||
| 						Kp2aLog.Log("QuickLocking database"); | ||||
|  | ||||
| 						QuickLocked = true; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						Kp2aLog.Log("Database already QuickLocked"); | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					Kp2aLog.Log("Locking database"); | ||||
|  | ||||
| 					// Couldn't quick-lock, so unload database instead | ||||
| 					_db.Clear(); | ||||
| 					QuickLocked = false; | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				Kp2aLog.Log("Database not loaded, couldn't lock"); | ||||
| 			} | ||||
|  | ||||
| 			UpdateOngoingNotification(); | ||||
| 			Application.Context.SendBroadcast(new Intent(Intents.DatabaseLocked)); | ||||
|         } | ||||
|  | ||||
|         public void SetShutdown() | ||||
|         { | ||||
| 	        Kp2aLog.Log("set shutdown"); | ||||
|             _shutdown = true; | ||||
|         } | ||||
| 		public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string password, string keyFile, ProgressDialogStatusLogger statusLogger) | ||||
| 		{ | ||||
| 			_db.LoadData(this, ioConnectionInfo, memoryStream, password, keyFile, statusLogger); | ||||
|  | ||||
|         public void ClearShutdown() | ||||
|         { | ||||
| 			Kp2aLog.Log("clear shutdown"); | ||||
|             _shutdown = false; | ||||
|         } | ||||
| 			UpdateOngoingNotification(); | ||||
| 		} | ||||
|  | ||||
|         private Database _db; | ||||
|         private bool _shutdown; | ||||
| 		internal void UnlockDatabase() | ||||
| 		{ | ||||
| 			QuickLocked = false; | ||||
|  | ||||
| 			UpdateOngoingNotification(); | ||||
| 		} | ||||
|  | ||||
| 		private void UpdateOngoingNotification() | ||||
| 		{ | ||||
| 			// Start or update the notification icon service to reflect the current state | ||||
| 			var ctx = Application.Context; | ||||
| 			ctx.StartService(new Intent(ctx, typeof(OngoingNotificationsService))); | ||||
| 		} | ||||
|  | ||||
| 		public bool DatabaseIsUnlocked | ||||
| 		{ | ||||
| 			get { return _db.Loaded && !QuickLocked; } | ||||
| 		} | ||||
|  | ||||
| 		#region QuickUnlock | ||||
| 		public void SetQuickUnlockEnabled(bool enabled) | ||||
| 		{ | ||||
| 			if (enabled) | ||||
| 			{ | ||||
| 				//Set KeyLength of QuickUnlock at time of enabling. | ||||
| 				//This is important to not allow an attacker to set the length to 1 when QuickUnlock is started already. | ||||
|  | ||||
| 				var ctx = Application.Context; | ||||
| 				var prefs = PreferenceManager.GetDefaultSharedPreferences(ctx); | ||||
| 				QuickUnlockKeyLength = Math.Max(1, int.Parse(prefs.GetString(ctx.GetString(Resource.String.QuickUnlockLength_key), ctx.GetString(Resource.String.QuickUnlockLength_default)))); | ||||
| 			} | ||||
| 			QuickUnlockEnabled = enabled; | ||||
| 		} | ||||
|  | ||||
| 		public bool QuickUnlockEnabled { get; private set; } | ||||
|  | ||||
| 		public int QuickUnlockKeyLength { get; private set; } | ||||
|      | ||||
| 		/// <summary> | ||||
| 		/// If true, the database must be regarded as locked and not exposed to the user. | ||||
| 		/// </summary> | ||||
| 		public bool QuickLocked { get; private set; } | ||||
| 		 | ||||
| 		#endregion | ||||
|  | ||||
| 		private Database _db; | ||||
|          | ||||
|         /// <summary> | ||||
|         /// See comments to EntryEditActivityState. | ||||
| @@ -124,6 +198,7 @@ namespace keepass2android | ||||
|             { | ||||
|                 if (_db.ReloadRequested) | ||||
|                 { | ||||
| 	                LockDatabase(false); | ||||
|                     activity.SetResult(KeePass.ExitReloadDb); | ||||
|                     activity.Finish(); | ||||
| 					//todo: return? | ||||
| @@ -143,6 +218,7 @@ namespace keepass2android | ||||
| 				(dlgSender, dlgEvt) => | ||||
| 				{ | ||||
| 					_db.ReloadRequested = true; | ||||
| 					LockDatabase(false); | ||||
| 					activity.SetResult(KeePass.ExitReloadDb); | ||||
| 					activity.Finish(); | ||||
|  | ||||
| @@ -223,7 +299,7 @@ namespace keepass2android | ||||
|  | ||||
| 			public RealProgressDialog(Context ctx) | ||||
| 			{ | ||||
| 				this._pd = new ProgressDialog(ctx); | ||||
| 				_pd = new ProgressDialog(ctx); | ||||
| 			} | ||||
|  | ||||
| 			public void SetTitle(string title) | ||||
|   | ||||
							
								
								
									
										25
									
								
								src/keepass2android/app/ApplicationBroadcastReceiver.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,25 @@ | ||||
| using System; | ||||
| using System.Linq; | ||||
| using Android.Content; | ||||
| using Android.App; | ||||
|  | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| 	[BroadcastReceiver] | ||||
| 	[IntentFilter(new[] { Intents.LockDatabase })] | ||||
| 	public class ApplicationBroadcastReceiver : BroadcastReceiver | ||||
| 	{ | ||||
| 		public override void OnReceive(Context context, Intent intent) | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Received broadcast intent: " + intent.Action); | ||||
|  | ||||
| 			switch (intent.Action) | ||||
| 			{ | ||||
| 				case Intents.LockDatabase: | ||||
| 					App.Kp2a.LockDatabase(); | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -63,11 +63,6 @@ namespace keepass2android | ||||
|  | ||||
| 		internal AppTask AppTask; | ||||
|  | ||||
| 		IOConnectionInfo LoadIoc(string defaultFileName) | ||||
| 		{ | ||||
| 			return _DbHelper.CursorToIoc(_DbHelper.FetchFileByName(defaultFileName)); | ||||
| 		} | ||||
|  | ||||
| 		void ShowFilenameDialog(bool showOpenButton, bool showCreateButton, bool showBrowseButton, string defaultFilename, string detailsText, int requestCodeBrowse) | ||||
| 		{ | ||||
| 			AlertDialog.Builder builder = new AlertDialog.Builder(this); | ||||
| @@ -319,14 +314,20 @@ namespace keepass2android | ||||
| 			 | ||||
| 			public override void Run() { | ||||
| 				if (Success) { | ||||
| 					// Update the ongoing notification | ||||
| 					_activity.StartService(new Intent(_activity, typeof(OngoingNotificationsService))); | ||||
|  | ||||
|  | ||||
| 					if (_activity.RememberRecentFiles()) | ||||
| 					{ | ||||
| 						// Add to recent files | ||||
| 						FileDbHelper dbHelper = App.Kp2a.FileDbHelper; | ||||
|  | ||||
| 					 | ||||
| 						//TODO: getFilename always returns "" -> bug? | ||||
| 						dbHelper.CreateFile(_ioc, Filename); | ||||
| 					} | ||||
|  | ||||
| 					GroupActivity.Launch(_activity, _activity.AppTask); | ||||
| 					 | ||||
| 				} else { | ||||
| @@ -391,6 +392,7 @@ namespace keepass2android | ||||
| 				        ioc.Password = password; | ||||
| 				        ioc.CredSaveMode = (IOCredSaveMode)credentialRememberMode; | ||||
| 				        PasswordActivity.Launch(this, ioc, AppTask); | ||||
| 						Finish(); | ||||
| 				    }); | ||||
| 				builder.SetView(LayoutInflater.Inflate(Resource.Layout.url_credentials, null)); | ||||
| 				builder.SetNeutralButton(GetString(Android.Resource.String.Cancel),  | ||||
| @@ -406,6 +408,7 @@ namespace keepass2android | ||||
| 				try | ||||
| 				{ | ||||
| 					PasswordActivity.Launch(this, ioc, AppTask); | ||||
| 					Finish(); | ||||
| 				} catch (Java.IO.FileNotFoundException) | ||||
| 				{ | ||||
| 					Toast.MakeText(this,     Resource.String.FileNotFound, ToastLength.Long).Show(); | ||||
| @@ -489,36 +492,6 @@ namespace keepass2android | ||||
|  | ||||
| 			_fileSelectButtons.UpdateExternalStorageWarning(); | ||||
|  | ||||
| 			if (!_createdWithActivityResult) | ||||
| 			{ | ||||
| 				if ((Intent.Action == Intent.ActionSend) && (App.Kp2a.GetDb().Loaded)) | ||||
| 				{ | ||||
| 					PasswordActivity.Launch(this, App.Kp2a.GetDb().Ioc , AppTask); | ||||
| 				} else | ||||
| 				{ | ||||
| 					 | ||||
| 					// Load default database | ||||
| 					ISharedPreferences prefs = Android.Preferences.PreferenceManager.GetDefaultSharedPreferences(this); | ||||
| 					String defaultFileName = prefs.GetString(PasswordActivity.KeyDefaultFilename, ""); | ||||
| 					 | ||||
| 					if (defaultFileName.Length > 0) | ||||
| 					{ | ||||
| 						Java.IO.File db = new Java.IO.File(defaultFileName); | ||||
| 						 | ||||
| 						if (db.Exists()) | ||||
| 						{ | ||||
| 							try | ||||
| 							{ | ||||
| 								PasswordActivity.Launch(this, LoadIoc(defaultFileName), AppTask); | ||||
| 							} catch (Exception e) | ||||
| 							{ | ||||
| 								Toast.MakeText(this, e.Message, ToastLength.Long); | ||||
| 								// Ignore exception | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| @@ -526,6 +499,15 @@ namespace keepass2android | ||||
| 		{ | ||||
| 			base.OnStart(); | ||||
| 			Kp2aLog.Log("FileSelect.OnStart"); | ||||
|  | ||||
| 			var db = App.Kp2a.GetDb(); | ||||
| 			if (db.Loaded) | ||||
| 			{ | ||||
| 				PasswordActivity.Launch(this, db.Ioc, AppTask); | ||||
| 				Finish(); | ||||
| 			} | ||||
|  | ||||
| 			 | ||||
| 		} | ||||
| 		public override bool OnCreateOptionsMenu(IMenu menu) { | ||||
| 			base.OnCreateOptionsMenu(menu); | ||||
|   | ||||
| @@ -22,8 +22,13 @@ namespace keepass2android | ||||
| 	/// <summary> | ||||
| 	/// Contains constants to be used in intents | ||||
| 	/// </summary> | ||||
| 	public class Intents { | ||||
| 		public const String Timeout = "keepass2android.timeout"; | ||||
| 	public class Intents  | ||||
| 	{ | ||||
| 		/// <summary>Broadcast this intent to lock the database</summary> | ||||
| 		public const String LockDatabase = "keepass2android.lock_database"; | ||||
| 		 | ||||
| 		/// <summary>This intent will be broadcast once the database has been locked. Sensitive information displayed should be hidden and unloaded.</summary> | ||||
| 		public const String DatabaseLocked = "keepass2android.database_locked"; | ||||
|  | ||||
| 		public const String CopyUsername = "keepass2android.copy_username"; | ||||
| 		public const String CopyPassword = "keepass2android.copy_password"; | ||||
|   | ||||
| @@ -77,6 +77,7 @@ | ||||
|     <Reference Include="Mono.Android.Support.v4" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="app\ApplicationBroadcastReceiver.cs" /> | ||||
|     <Compile Include="icons\DrawableFactory.cs" /> | ||||
|     <Compile Include="icons\Icons.cs" /> | ||||
|     <Compile Include="Resources\Resource.designer.cs" /> | ||||
| @@ -86,6 +87,8 @@ | ||||
|     <Compile Include="fileselect\FileSelectActivity.cs" /> | ||||
|     <Compile Include="fileselect\FileDbHelper.cs" /> | ||||
|     <Compile Include="search\SearchProvider.cs" /> | ||||
|     <Compile Include="services\OngoingNotificationsService.cs" /> | ||||
|     <Compile Include="settings\DatabaseSettingsActivity.cs" /> | ||||
|     <Compile Include="Utils\Util.cs" /> | ||||
|     <Compile Include="intents\Intents.cs" /> | ||||
|     <Compile Include="fileselect\BrowserDialog.cs" /> | ||||
| @@ -127,12 +130,10 @@ | ||||
|     <Compile Include="compat\EditorCompat.cs" /> | ||||
|     <Compile Include="compat\ActivityCompat.cs" /> | ||||
|     <Compile Include="ShareUrlResults.cs" /> | ||||
|     <Compile Include="services\TimeoutService.cs" /> | ||||
|     <Compile Include="services\CopyToClipboardService.cs" /> | ||||
|     <Compile Include="search\SearchActivity.cs" /> | ||||
|     <Compile Include="QuickUnlock.cs" /> | ||||
|     <Compile Include="LifecycleDebugActivity.cs" /> | ||||
|     <Compile Include="services\QuickUnlockForegroundService.cs" /> | ||||
|     <Compile Include="AssemblyInfo.cs" /> | ||||
|     <Compile Include="views\FileSelectButtons.cs" /> | ||||
|     <Compile Include="EntryEditActivityState.cs" /> | ||||
| @@ -703,4 +704,25 @@ | ||||
|       <SubType>Designer</SubType> | ||||
|     </AndroidResource> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable\ic_unlocked_gray.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable-v11\ic_unlocked_gray.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable-mdpi\ic_launcher_red.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable-xhdpi\ic_launcher_red.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable-xxhdpi\ic_launcher_red.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable-hdpi\ic_launcher_red.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable\ic_launcher_red.png" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
| @@ -70,7 +70,7 @@ namespace keepass2android.search | ||||
|  | ||||
| 		public override Android.Database.ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder) | ||||
| 		{ | ||||
| 			if (_db.Open) // Can't show suggestions if the database is locked! | ||||
| 			if (App.Kp2a.DatabaseIsUnlocked) // Can't show suggestions if the database is locked! | ||||
| 			{ | ||||
| 				switch ((UriMatches)UriMatcher.Match(uri)) | ||||
| 				{ | ||||
|   | ||||
| @@ -56,11 +56,9 @@ namespace keepass2android.search | ||||
|  | ||||
| 		private void ProcessIntent(Intent intent) | ||||
| 		{ | ||||
| 			_db = App.Kp2a.GetDb(); | ||||
| 			 | ||||
|  | ||||
| 			// Likely the app has been killed exit the activity  | ||||
| 			if ( ! _db.Open ) { | ||||
| 			if (!App.Kp2a.DatabaseIsUnlocked) | ||||
| 			{ | ||||
| 				Finish(); | ||||
| 			} | ||||
|  | ||||
|   | ||||
| @@ -57,7 +57,7 @@ namespace keepass2android | ||||
|  | ||||
| 		CopyToClipboardBroadcastReceiver _copyToClipBroadcastReceiver; | ||||
| 		NotificationDeletedBroadcastReceiver _notificationDeletedBroadcastReceiver; | ||||
|  | ||||
| 		StopOnLockBroadcastReceiver _stopOnLockBroadcastReceiver; | ||||
|  | ||||
| 		public CopyToClipboardService() | ||||
| 		{ | ||||
| @@ -74,6 +74,11 @@ namespace keepass2android | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Received intent to provide access to entry"); | ||||
|  | ||||
| 			_stopOnLockBroadcastReceiver = new StopOnLockBroadcastReceiver(this); | ||||
| 			IntentFilter filter = new IntentFilter(); | ||||
| 			filter.AddAction(Intents.DatabaseLocked); | ||||
| 			RegisterReceiver(_stopOnLockBroadcastReceiver, filter); | ||||
|  | ||||
| 			String uuidBytes =  intent.GetStringExtra(EntryActivity.KeyEntry); | ||||
| 			bool closeAfterCreate = intent.GetBooleanExtra(EntryActivity.KeyCloseAfterCreate, false); | ||||
| 			 | ||||
| @@ -99,12 +104,25 @@ namespace keepass2android | ||||
| 			return StartCommandResult.RedeliverIntent; | ||||
| 		} | ||||
|  | ||||
| 		private void OnLockDatabase() | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Stopping clipboard service due to database lock"); | ||||
|  | ||||
| 			StopSelf(); | ||||
| 		} | ||||
|  | ||||
| 		private NotificationManager _notificationManager; | ||||
| 		private int _numElementsToWaitFor; | ||||
| 		 | ||||
| 		public override void OnDestroy() | ||||
| 		{ | ||||
| 			Kp2aLog.Log("CopyToClipboardService.OnDestroy"); | ||||
|  | ||||
| 			// These members might never get initialized if the app timed out | ||||
| 			if (_stopOnLockBroadcastReceiver != null) | ||||
| 			{ | ||||
| 				UnregisterReceiver(_stopOnLockBroadcastReceiver); | ||||
| 			} | ||||
| 			if (_copyToClipBroadcastReceiver != null) | ||||
| 			{ | ||||
| 				UnregisterReceiver(_copyToClipBroadcastReceiver); | ||||
| @@ -114,7 +132,10 @@ namespace keepass2android | ||||
| 				UnregisterReceiver(_notificationDeletedBroadcastReceiver); | ||||
| 			} | ||||
| 			if ( _notificationManager != null ) { | ||||
| 				_notificationManager.CancelAll(); | ||||
| 				_notificationManager.Cancel(NotifyPassword); | ||||
| 				_notificationManager.Cancel(NotifyUsername); | ||||
| 				_notificationManager.Cancel(NotifyKeyboard); | ||||
|  | ||||
| 				_numElementsToWaitFor= 0; | ||||
| 				clearKeyboard(); | ||||
| 			} | ||||
| @@ -143,7 +164,9 @@ namespace keepass2android | ||||
| 			// Notification Manager | ||||
| 			_notificationManager = (NotificationManager)GetSystemService(NotificationService); | ||||
|  | ||||
| 			_notificationManager.CancelAll(); | ||||
| 			_notificationManager.Cancel(NotifyPassword); | ||||
| 			_notificationManager.Cancel(NotifyUsername); | ||||
| 			_notificationManager.Cancel(NotifyKeyboard); | ||||
| 			_numElementsToWaitFor = 0; | ||||
| 			clearKeyboard(); | ||||
|  | ||||
| @@ -358,7 +381,24 @@ namespace keepass2android | ||||
| 			return notify; | ||||
| 		} | ||||
|  | ||||
| 		private class StopOnLockBroadcastReceiver : BroadcastReceiver | ||||
| 		{ | ||||
| 			readonly CopyToClipboardService _service; | ||||
| 			public StopOnLockBroadcastReceiver(CopyToClipboardService service) | ||||
| 			{ | ||||
| 				_service = service; | ||||
| 			} | ||||
|  | ||||
| 			public override void OnReceive(Context context, Intent intent) | ||||
| 			{ | ||||
| 				switch (intent.Action) | ||||
| 				{ | ||||
| 					case Intents.DatabaseLocked: | ||||
| 						_service.OnLockDatabase(); | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		class CopyToClipboardBroadcastReceiver: BroadcastReceiver | ||||
| 		{ | ||||
|   | ||||
							
								
								
									
										187
									
								
								src/keepass2android/services/OngoingNotificationsService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,187 @@ | ||||
| /* | ||||
| This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin. | ||||
|  | ||||
|   Keepass2Android is free software: you can redistribute it and/or modify | ||||
|   it under the terms of the GNU General Public License as published by | ||||
|   the Free Software Foundation, either version 2 of the License, or | ||||
|   (at your option) any later version. | ||||
|  | ||||
|   Keepass2Android is distributed in the hope that it will be useful, | ||||
|   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|   GNU General Public License for more details. | ||||
|  | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>. | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using Android.App; | ||||
| using Android.Content; | ||||
| using Android.Graphics; | ||||
| using Android.OS; | ||||
| using Android.Preferences; | ||||
| using Android.Support.V4.App; | ||||
| using KeePassLib.Utility; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Service for showing ongoing notifications | ||||
| 	///  | ||||
| 	/// Shows database unlocked warning persistent notification | ||||
| 	/// Shows Quick-Unlock notification | ||||
| 	/// </summary> | ||||
| 	[Service] | ||||
| 	public class OngoingNotificationsService : Service | ||||
| 	{ | ||||
| 		 | ||||
| 		#region Service | ||||
| 		private const int QuickUnlockId = 100; | ||||
| 		private const int UnlockedWarningId = 200; | ||||
|  | ||||
| 		public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Starting/Updating OngoingNotificationsService. Database " + (App.Kp2a.DatabaseIsUnlocked ? "Unlocked" : (App.Kp2a.QuickLocked ? "QuickLocked" : "Locked"))); | ||||
|  | ||||
| 			var notificationManager = (NotificationManager)GetSystemService(NotificationService); | ||||
| 					 | ||||
| 			// Set the icon to reflect the current state | ||||
| 			if (App.Kp2a.DatabaseIsUnlocked) | ||||
| 			{ | ||||
| 				// Clear current foreground status and QuickUnlock icon | ||||
| 				StopForeground(true); | ||||
|  | ||||
| 				if (ShowUnlockedNotification) | ||||
| 				{ | ||||
| 					// No need for task to get foreground priority, we don't need any special treatment just for showing that the database is unlocked | ||||
| 					notificationManager.Notify(UnlockedWarningId, GetUnlockedNotification()); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					notificationManager.Cancel(UnlockedWarningId); | ||||
| 				} | ||||
| 			} | ||||
| 			else  | ||||
| 			{ | ||||
| 				notificationManager.Cancel(UnlockedWarningId); | ||||
|  | ||||
| 				if (App.Kp2a.QuickLocked) | ||||
| 				{ | ||||
| 					// Show the Quick Unlock notification | ||||
| 					StartForeground(QuickUnlockId, GetQuickUnlockNotification()); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					// Not showing any notification, database is locked, no point in keeping running | ||||
| 					StopSelf(); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			return StartCommandResult.NotSticky; | ||||
| 		} | ||||
|  | ||||
| 		private bool ShowUnlockedNotification | ||||
| 		{ | ||||
| 			get { return PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(GetString(Resource.String.ShowUnlockedNotification_key), Resources.GetBoolean(Resource.Boolean.ShowUnlockedNotification_default)); } | ||||
| 		} | ||||
|  | ||||
| 		public override void OnTaskRemoved(Intent rootIntent) | ||||
| 		{ | ||||
| 			base.OnTaskRemoved(rootIntent); | ||||
|  | ||||
| 			Kp2aLog.Log("OngoingNotificationsService.OnTaskRemoved: " + rootIntent.Action); | ||||
|  | ||||
| 			// If the user has closed the task (probably by swiping it out of the recent apps list) then lock the database | ||||
| 			App.Kp2a.LockDatabase(); | ||||
| 		} | ||||
|  | ||||
| 		public override void OnDestroy() | ||||
| 		{ | ||||
| 			base.OnDestroy(); | ||||
|  | ||||
| 			var notificationManager = (NotificationManager)GetSystemService(NotificationService); | ||||
| 			notificationManager.Cancel(UnlockedWarningId); | ||||
| 			// Quick Unlock notification should be removed automatically by the service (if present), as it was the foreground notification. | ||||
|  | ||||
| 			Kp2aLog.Log("OngoingNotificationsService.OnDestroy"); | ||||
|  | ||||
| 			// If the service is killed, then lock the database immediately (as the unlocked warning icon will no longer display). | ||||
| 			if (App.Kp2a.DatabaseIsUnlocked) | ||||
| 			{ | ||||
| 				App.Kp2a.LockDatabase(); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		public override IBinder OnBind(Intent intent) | ||||
| 		{ | ||||
| 			return null; | ||||
| 		} | ||||
|  | ||||
| 		#endregion | ||||
|  | ||||
| 		#region QuickUnlock | ||||
|  | ||||
| 		private Notification GetQuickUnlockNotification() | ||||
| 		{ | ||||
| 			NotificationCompat.Builder builder =  | ||||
| 				new NotificationCompat.Builder(this) | ||||
| 					.SetSmallIcon(Resource.Drawable.ic_launcher_gray) | ||||
| 					.SetLargeIcon(BitmapFactory.DecodeResource(Resources, AppNames.LauncherIcon)) | ||||
| 					.SetContentTitle(GetString(Resource.String.app_name)) | ||||
| 					.SetContentText(GetString(Resource.String.database_loaded_quickunlock_enabled, GetDatabaseName())); | ||||
|  | ||||
| 			var startKp2APendingIntent = GetSwitchToAppPendingIntent(); | ||||
| 			builder.SetContentIntent(startKp2APendingIntent); | ||||
|  | ||||
| 			return builder.Build(); | ||||
| 		} | ||||
|  | ||||
| 		#endregion | ||||
|  | ||||
| 		#region Unlocked Warning | ||||
|  | ||||
| 		private Notification GetUnlockedNotification() | ||||
| 		{ | ||||
| 			NotificationCompat.Builder builder = | ||||
| 				new NotificationCompat.Builder(this) | ||||
| 					.SetOngoing(true) | ||||
| 					.SetSmallIcon(Resource.Drawable.ic_unlocked_gray) | ||||
| 					.SetLargeIcon(BitmapFactory.DecodeResource(Resources, Resource.Drawable.ic_launcher_red)) | ||||
| 					.SetContentTitle(GetString(Resource.String.app_name)) | ||||
| 					.SetContentText(GetString(Resource.String.database_loaded_unlocked, GetDatabaseName())); | ||||
|  | ||||
| 			// Default action is to show Kp2A | ||||
| 			builder.SetContentIntent(GetSwitchToAppPendingIntent()); | ||||
| 			// Additional action to allow locking the database | ||||
| 			builder.AddAction(Android.Resource.Drawable.IcLockLock, GetString(Resource.String.menu_lock), PendingIntent.GetBroadcast(this, 0, new Intent(Intents.LockDatabase), PendingIntentFlags.UpdateCurrent)); | ||||
| 			 | ||||
| 			return builder.Build(); | ||||
| 		} | ||||
|  | ||||
| 		private PendingIntent GetSwitchToAppPendingIntent() | ||||
| 		{ | ||||
| 			var startKp2aIntent = new Intent(this, typeof(KeePass)); | ||||
| 			startKp2aIntent.SetAction(Intent.ActionMain); | ||||
| 			startKp2aIntent.AddCategory(Intent.CategoryLauncher); | ||||
|  | ||||
| 			return PendingIntent.GetActivity(this, 0, startKp2aIntent, PendingIntentFlags.UpdateCurrent); | ||||
| 		} | ||||
|  | ||||
| 		private static string GetDatabaseName() | ||||
| 		{ | ||||
| 			 | ||||
| 			var db = App.Kp2a.GetDb().KpDatabase; | ||||
| 			var name = db.Name; | ||||
| 			if (String.IsNullOrEmpty(name)) | ||||
| 			{ | ||||
| 				//todo: if paranoid ("don't remember recent files") return "***" | ||||
| 				name = UrlUtil.StripExtension(UrlUtil.GetFileName(db.IOConnectionInfo.Path)); | ||||
| 			} | ||||
|  | ||||
| 			return name; | ||||
| 		} | ||||
| 		#endregion | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1,79 +0,0 @@ | ||||
| /* | ||||
| This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.  | ||||
|  | ||||
|   Keepass2Android is free software: you can redistribute it and/or modify | ||||
|   it under the terms of the GNU General Public License as published by | ||||
|   the Free Software Foundation, either version 2 of the License, or | ||||
|   (at your option) any later version. | ||||
|  | ||||
|   Keepass2Android is distributed in the hope that it will be useful, | ||||
|   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|   GNU General Public License for more details. | ||||
|  | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>. | ||||
|   */ | ||||
|  | ||||
| using Android.App; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using Android.Support.V4.App; | ||||
| using Android.Graphics; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// This service is started as soon as a Database with QuickUnlock enabled is opened. | ||||
| 	/// Its only purpose is to be a foreground service which prevents the App from being killed (in most situations) | ||||
| 	/// </summary> | ||||
| [Service] | ||||
| 	public class QuickUnlockForegroundService : Service | ||||
| 	{ | ||||
| 		public override IBinder OnBind(Intent intent) | ||||
| 		{ | ||||
| 			return null; | ||||
| 		}		 | ||||
|  | ||||
| 		const int QuickUnlockForegroundServiceId = 238787; | ||||
|  | ||||
| 		public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) | ||||
| 		{ | ||||
| 			Kp2aLog.Log("Starting QuickUnlockForegroundService"); | ||||
|  | ||||
| 			//create notification item | ||||
| 			NotificationCompat.Builder mBuilder = | ||||
| 				new NotificationCompat.Builder(this) | ||||
| 					.SetSmallIcon(Resource.Drawable.ic_launcher_gray) | ||||
| 					.SetLargeIcon(BitmapFactory.DecodeResource(Resources,  AppNames.LauncherIcon)) | ||||
|  					.SetContentTitle(GetText(Resource.String.app_name)) | ||||
| 					.SetContentText(GetText(Resource.String.database_loaded_quickunlock_enabled)); | ||||
|  | ||||
| 			Intent startKp2aIntent = new Intent(this, typeof(KeePass)); | ||||
| 			startKp2aIntent.SetAction(Intent.ActionMain); | ||||
| 			startKp2aIntent.AddCategory(Intent.CategoryLauncher); | ||||
|  | ||||
| 			PendingIntent startKp2APendingIntent = | ||||
| 				PendingIntent.GetActivity(this, 0, startKp2aIntent, PendingIntentFlags.UpdateCurrent); | ||||
| 			mBuilder.SetContentIntent(startKp2APendingIntent); | ||||
|   | ||||
| 			StartForeground(QuickUnlockForegroundServiceId , mBuilder.Build()); | ||||
|  | ||||
| 			return StartCommandResult.NotSticky; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		public override void OnCreate() | ||||
| 		{ | ||||
| 			base.OnCreate(); | ||||
| 			Kp2aLog.Log("Creating QuickUnlockForegroundService"); | ||||
| 		} | ||||
|  | ||||
| 		public override void OnDestroy() | ||||
| 		{ | ||||
| 			base.OnDestroy(); | ||||
| 			Kp2aLog.Log("Destroying QuickUnlockForegroundService"); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1,129 +0,0 @@ | ||||
| /* | ||||
| This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin. | ||||
|  | ||||
|   Keepass2Android is free software: you can redistribute it and/or modify | ||||
|   it under the terms of the GNU General Public License as published by | ||||
|   the Free Software Foundation, either version 2 of the License, or | ||||
|   (at your option) any later version. | ||||
|  | ||||
|   Keepass2Android is distributed in the hope that it will be useful, | ||||
|   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|   GNU General Public License for more details. | ||||
|  | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>. | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using Android.App; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using Android.Runtime; | ||||
| using Android.Util; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Manages timeout to lock the database after some idle time | ||||
| 	/// </summary> | ||||
| 	[Service] | ||||
| 	public class TimeoutService : Service { | ||||
| 		private const String Tag = "KeePass2Android Timer";  | ||||
| 		private BroadcastReceiver _intentReceiver; | ||||
|  | ||||
|  | ||||
| 		public TimeoutService (IntPtr javaReference, JniHandleOwnership transfer) | ||||
| 			: base(javaReference, transfer) | ||||
| 		{ | ||||
| 			_binder	= new TimeoutBinder(this); | ||||
| 		} | ||||
| 		public TimeoutService() | ||||
| 		{ | ||||
| 			_binder	= new TimeoutBinder(this); | ||||
| 		} | ||||
| 		 | ||||
| 		public override void OnCreate() { | ||||
| 			base.OnCreate(); | ||||
| 			 | ||||
| 			_intentReceiver = new MyBroadcastReceiver(this); | ||||
|  | ||||
| 			IntentFilter filter = new IntentFilter(); | ||||
| 			filter.AddAction(Intents.Timeout); | ||||
| 			RegisterReceiver(_intentReceiver, filter); | ||||
| 			 | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		public override void OnStart(Intent intent, int startId) { | ||||
| 			base.OnStart(intent, startId); | ||||
| 			 | ||||
| 			Log.Debug(Tag, "Timeout service started"); | ||||
| 		} | ||||
| 		 | ||||
| 		private void Timeout() { | ||||
| 			Log.Debug(Tag, "Timeout"); | ||||
|             App.Kp2a.SetShutdown(); | ||||
| 			 | ||||
| 			NotificationManager nm = (NotificationManager) GetSystemService(NotificationService); | ||||
| 			nm.CancelAll(); | ||||
| 			StopService(new Intent(this, typeof(CopyToClipboardService))); | ||||
| 			StopSelf(); | ||||
| 		} | ||||
| 		 | ||||
| 		public override void OnDestroy() { | ||||
| 			base.OnDestroy(); | ||||
| 			 | ||||
| 			Log.Debug(Tag, "Timeout service stopped"); | ||||
| 			 | ||||
| 			UnregisterReceiver(_intentReceiver); | ||||
| 		} | ||||
| 		 | ||||
| 		public class TimeoutBinder : Binder  | ||||
| 		{ | ||||
| 			readonly TimeoutService _service; | ||||
|  | ||||
| 			public TimeoutBinder(TimeoutService service) | ||||
| 			{ | ||||
| 				_service = service; | ||||
| 			} | ||||
|  | ||||
| 			public TimeoutService GetService() { | ||||
| 				return _service; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		private readonly IBinder _binder; | ||||
| 		 | ||||
| 		 | ||||
| 		public override IBinder OnBind(Intent intent) { | ||||
| 			return _binder; | ||||
| 		} | ||||
|  | ||||
| 		[BroadcastReceiver] | ||||
| 		public class MyBroadcastReceiver: BroadcastReceiver | ||||
| 		{ | ||||
| 			public MyBroadcastReceiver() | ||||
| 			{ | ||||
| 				//dummy constructor required for MonoForAndroid, not called. | ||||
| 				throw new NotImplementedException(); | ||||
| 			} | ||||
|  | ||||
| 			readonly TimeoutService _timeoutService; | ||||
| 			public MyBroadcastReceiver (TimeoutService timeoutService) | ||||
| 			{ | ||||
| 				_timeoutService = timeoutService; | ||||
| 			} | ||||
|  | ||||
| 			public override void OnReceive(Context context, Intent intent) { | ||||
| 				String action = intent.Action; | ||||
| 				 | ||||
| 				if ( action.Equals(Intents.Timeout) ) { | ||||
| 					_timeoutService.Timeout(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -16,122 +16,49 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using System.Globalization; | ||||
| using Android.App; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using Android.Widget; | ||||
| using Android.Preferences; | ||||
| using KeePassLib.Cryptography.Cipher; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Activity to configure the app | ||||
| 	/// Activity to configure the application, without database settings. Does not require an unlocked database, or close when the database is locked | ||||
| 	/// </summary> | ||||
| 	[Activity (Label = "@string/app_name", Theme="@style/NoTitleBar")]			 | ||||
| 	public class AppSettingsActivity : LockingClosePreferenceActivity { | ||||
| 		public static bool KeyfileDefault = false; | ||||
| 		 | ||||
| 		public static void Launch(Context ctx) { | ||||
| 			Intent i = new Intent(ctx, typeof(AppSettingsActivity)); | ||||
| 			 | ||||
| 			ctx.StartActivity(i); | ||||
| 	public class AppSettingsActivity : LockingPreferenceActivity | ||||
| 	{ | ||||
| 		public static void Launch(Context ctx) | ||||
| 		{ | ||||
| 			ctx.StartActivity(new Intent(ctx, typeof(AppSettingsActivity))); | ||||
| 		} | ||||
|  | ||||
| 		protected override void OnCreate(Bundle savedInstanceState) { | ||||
| 		protected override void OnCreate(Bundle savedInstanceState)  | ||||
| 		{ | ||||
| 			base.OnCreate(savedInstanceState); | ||||
| 			 | ||||
| 			AddPreferencesFromResource(Resource.Xml.preferences); | ||||
| 			 | ||||
| 			Preference keyFile = FindPreference(GetString(Resource.String.keyfile_key)); | ||||
| 			keyFile.PreferenceChange += (sender, e) =>  | ||||
| 			FindPreference(GetString(Resource.String.keyfile_key)).PreferenceChange += OnRememberKeyFileHistoryChanged; | ||||
| 			FindPreference(GetString(Resource.String.ShowUnlockedNotification_key)).PreferenceChange += OnShowUnlockedNotificationChanged;; | ||||
|  | ||||
| 			FindPreference(GetString(Resource.String.db_key)).Enabled = false; | ||||
| 		} | ||||
|  | ||||
| 		internal static void OnRememberKeyFileHistoryChanged(object sender, Preference.PreferenceChangeEventArgs eventArgs) | ||||
| 		{ | ||||
| 			if (!(bool)eventArgs.NewValue) | ||||
| 			{ | ||||
| 				bool value = (bool) e.NewValue; | ||||
| 				 | ||||
| 				if ( ! value ) { | ||||
| 					FileDbHelper helper = App.Kp2a.FileDbHelper; | ||||
| 					 | ||||
| 					helper.DeleteAllKeys(); | ||||
| 				} | ||||
| 			}; | ||||
| 			 | ||||
| 			Database db = App.Kp2a.GetDb(); | ||||
| 			if ( db.Open ) { | ||||
| 				Preference rounds = FindPreference(GetString(Resource.String.rounds_key)); | ||||
| 				rounds.PreferenceChange += (sender, e) =>  | ||||
| 				{ | ||||
| 					setRounds(App.Kp2a.GetDb(), e.Preference); | ||||
| 				}; | ||||
|  | ||||
| 				Preference defaultUser = FindPreference(GetString(Resource.String.default_username_key)); | ||||
| 				((EditTextPreference)defaultUser).EditText.Text = db.KpDatabase.DefaultUserName; | ||||
| 				((EditTextPreference)defaultUser).Text = db.KpDatabase.DefaultUserName; | ||||
| 				defaultUser.PreferenceChange += (sender, e) =>  | ||||
| 				{ | ||||
| 					DateTime previousUsernameChanged = db.KpDatabase.DefaultUserNameChanged; | ||||
| 					String previousUsername = db.KpDatabase.DefaultUserName; | ||||
| 					db.KpDatabase.DefaultUserName = e.NewValue.ToString(); | ||||
| 				 | ||||
| 					SaveDb save = new SaveDb(this, App.Kp2a, new ActionOnFinish( (success, message) =>  | ||||
| 					                                                         { | ||||
| 						if (!success) | ||||
| 						{ | ||||
| 							db.KpDatabase.DefaultUserName = previousUsername; | ||||
| 							db.KpDatabase.DefaultUserNameChanged = previousUsernameChanged; | ||||
| 							Toast.MakeText(this, message, ToastLength.Long).Show(); | ||||
| 						} | ||||
| 					})); | ||||
| 					ProgressTask pt = new ProgressTask(App.Kp2a, this, save); | ||||
| 					pt.Run(); | ||||
| 				}; | ||||
|  | ||||
| 				Preference databaseName = FindPreference(GetString(Resource.String.database_name_key)); | ||||
| 				((EditTextPreference)databaseName).EditText.Text = db.KpDatabase.Name; | ||||
| 				((EditTextPreference)databaseName).Text = db.KpDatabase.Name; | ||||
| 				databaseName.PreferenceChange += (sender, e) =>  | ||||
| 				{ | ||||
| 					DateTime previousNameChanged = db.KpDatabase.NameChanged; | ||||
| 					String previousName = db.KpDatabase.Name; | ||||
| 					db.KpDatabase.Name = e.NewValue.ToString(); | ||||
| 					 | ||||
| 					SaveDb save = new SaveDb(this, App.Kp2a, new ActionOnFinish( (success, message) =>  | ||||
| 					                                                               { | ||||
| 						if (!success) | ||||
| 						{ | ||||
| 							db.KpDatabase.Name = previousName; | ||||
| 							db.KpDatabase.NameChanged = previousNameChanged; | ||||
| 							Toast.MakeText(this, message, ToastLength.Long).Show(); | ||||
| 						} | ||||
| 					})); | ||||
|                     ProgressTask pt = new ProgressTask(App.Kp2a, this, save); | ||||
| 					pt.Run(); | ||||
| 				}; | ||||
| 				 | ||||
|  | ||||
|  | ||||
| 				setRounds(db, rounds); | ||||
| 				 | ||||
| 				Preference algorithm = FindPreference(GetString(Resource.String.algorithm_key)); | ||||
| 				setAlgorithm(db, algorithm); | ||||
| 				 | ||||
| 			} else { | ||||
| 				Preference dbSettings = FindPreference(GetString(Resource.String.db_key)); | ||||
| 				dbSettings.Enabled = false; | ||||
| 				App.Kp2a.FileDbHelper.DeleteAllKeys(); | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		private void setRounds(Database db, Preference rounds) { | ||||
| 			rounds.Summary = db.KpDatabase.KeyEncryptionRounds.ToString(CultureInfo.InvariantCulture); | ||||
| 		internal static void OnShowUnlockedNotificationChanged(object sender, Preference.PreferenceChangeEventArgs eventArgs) | ||||
| 		{ | ||||
| 			var ctx = ((Preference)sender).Context; | ||||
| 			ctx.StartService(new Intent(ctx, typeof(OngoingNotificationsService))); | ||||
| 		} | ||||
| 		 | ||||
| 		private void setAlgorithm(Database db, Preference algorithm) { | ||||
|  | ||||
| 			algorithm.Summary = CipherPool.GlobalPool.GetCipher(db.KpDatabase.DataCipherUuid).DisplayName; | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										121
									
								
								src/keepass2android/settings/DatabaseSettingsActivity.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,121 @@ | ||||
| /* | ||||
| This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin. | ||||
|  | ||||
|   Keepass2Android is free software: you can redistribute it and/or modify | ||||
|   it under the terms of the GNU General Public License as published by | ||||
|   the Free Software Foundation, either version 2 of the License, or | ||||
|   (at your option) any later version. | ||||
|  | ||||
|   Keepass2Android is distributed in the hope that it will be useful, | ||||
|   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|   GNU General Public License for more details. | ||||
|  | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>. | ||||
|   */ | ||||
|  | ||||
| using System; | ||||
| using System.Globalization; | ||||
| using Android.App; | ||||
| using Android.Content; | ||||
| using Android.OS; | ||||
| using Android.Widget; | ||||
| using Android.Preferences; | ||||
| using KeePassLib.Cryptography.Cipher; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Activity to configure the application and database settings. The database must be unlocked, and this activity will close if it becomes locked. | ||||
| 	/// </summary> | ||||
| 	[Activity (Label = "@string/app_name", Theme="@style/NoTitleBar")]			 | ||||
| 	public class DatabaseSettingsActivity : LockingClosePreferenceActivity  | ||||
| 	{ | ||||
| 		public static void Launch(Context ctx) | ||||
| 		{ | ||||
| 			ctx.StartActivity(new Intent(ctx, typeof(DatabaseSettingsActivity))); | ||||
| 		} | ||||
|  | ||||
| 		protected override void OnCreate(Bundle savedInstanceState)  | ||||
| 		{ | ||||
| 			base.OnCreate(savedInstanceState); | ||||
| 			 | ||||
| 			AddPreferencesFromResource(Resource.Xml.preferences); | ||||
|  | ||||
| 			// Re-use the change handlers for the application settings | ||||
| 			FindPreference(GetString(Resource.String.keyfile_key)).PreferenceChange += AppSettingsActivity.OnRememberKeyFileHistoryChanged; | ||||
| 			FindPreference(GetString(Resource.String.ShowUnlockedNotification_key)).PreferenceChange += AppSettingsActivity.OnShowUnlockedNotificationChanged; | ||||
|  | ||||
| 			Database db = App.Kp2a.GetDb(); | ||||
| 			 | ||||
| 			Preference rounds = FindPreference(GetString(Resource.String.rounds_key)); | ||||
| 			rounds.PreferenceChange += (sender, e) => SetRounds(db, e.Preference); | ||||
|  | ||||
| 			Preference defaultUser = FindPreference(GetString(Resource.String.default_username_key)); | ||||
| 			((EditTextPreference)defaultUser).EditText.Text = db.KpDatabase.DefaultUserName; | ||||
| 			((EditTextPreference)defaultUser).Text = db.KpDatabase.DefaultUserName; | ||||
| 			defaultUser.PreferenceChange += (sender, e) =>  | ||||
| 			{ | ||||
| 				DateTime previousUsernameChanged = db.KpDatabase.DefaultUserNameChanged; | ||||
| 				String previousUsername = db.KpDatabase.DefaultUserName; | ||||
| 				db.KpDatabase.DefaultUserName = e.NewValue.ToString(); | ||||
| 				 | ||||
| 				SaveDb save = new SaveDb(this, App.Kp2a, new ActionOnFinish( (success, message) =>  | ||||
| 				{ | ||||
| 					if (!success) | ||||
| 					{ | ||||
| 						db.KpDatabase.DefaultUserName = previousUsername; | ||||
| 						db.KpDatabase.DefaultUserNameChanged = previousUsernameChanged; | ||||
| 						Toast.MakeText(this, message, ToastLength.Long).Show(); | ||||
| 					} | ||||
| 				})); | ||||
| 				ProgressTask pt = new ProgressTask(App.Kp2a, this, save); | ||||
| 				pt.Run(); | ||||
| 			}; | ||||
|  | ||||
| 			Preference databaseName = FindPreference(GetString(Resource.String.database_name_key)); | ||||
| 			((EditTextPreference)databaseName).EditText.Text = db.KpDatabase.Name; | ||||
| 			((EditTextPreference)databaseName).Text = db.KpDatabase.Name; | ||||
| 			databaseName.PreferenceChange += (sender, e) =>  | ||||
| 			{ | ||||
| 				DateTime previousNameChanged = db.KpDatabase.NameChanged; | ||||
| 				String previousName = db.KpDatabase.Name; | ||||
| 				db.KpDatabase.Name = e.NewValue.ToString(); | ||||
| 					 | ||||
| 				SaveDb save = new SaveDb(this, App.Kp2a, new ActionOnFinish( (success, message) =>  | ||||
| 				{ | ||||
| 					if (!success) | ||||
| 					{ | ||||
| 						db.KpDatabase.Name = previousName; | ||||
| 						db.KpDatabase.NameChanged = previousNameChanged; | ||||
| 						Toast.MakeText(this, message, ToastLength.Long).Show(); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						// Name is reflected in notification, so update it | ||||
| 						StartService(new Intent(this, typeof(OngoingNotificationsService))); | ||||
| 					} | ||||
| 				})); | ||||
|                 ProgressTask pt = new ProgressTask(App.Kp2a, this, save); | ||||
| 				pt.Run(); | ||||
| 			}; | ||||
|  | ||||
| 			SetRounds(db, rounds); | ||||
| 				 | ||||
| 			Preference algorithm = FindPreference(GetString(Resource.String.algorithm_key)); | ||||
| 			SetAlgorithm(db, algorithm); | ||||
| 		} | ||||
|  | ||||
| 		private void SetRounds(Database db, Preference rounds) | ||||
| 		{ | ||||
| 			rounds.Summary = db.KpDatabase.KeyEncryptionRounds.ToString(CultureInfo.InvariantCulture); | ||||
| 		} | ||||
| 		 | ||||
| 		private void SetAlgorithm(Database db, Preference algorithm)  | ||||
| 		{ | ||||
| 			algorithm.Summary = CipherPool.GlobalPool.GetCipher(db.KpDatabase.DataCipherUuid).DisplayName; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -31,7 +31,6 @@ namespace keepass2android.settings | ||||
| 	/// </summary> | ||||
| 	public class RoundsPreference : DialogPreference { | ||||
| 		 | ||||
| 		internal PwDatabase PwDatabase; | ||||
| 		internal TextView RoundsView; | ||||
| 		 | ||||
| 		protected override View OnCreateDialogView() { | ||||
| @@ -40,8 +39,7 @@ namespace keepass2android.settings | ||||
| 			RoundsView = (TextView) view.FindViewById(Resource.Id.rounds); | ||||
| 			 | ||||
| 			Database db = App.Kp2a.GetDb(); | ||||
| 			PwDatabase = db.KpDatabase; | ||||
| 			ulong numRounds = PwDatabase.KeyEncryptionRounds; | ||||
| 			ulong numRounds = db.KpDatabase.KeyEncryptionRounds; | ||||
| 			RoundsView.Text = numRounds.ToString(CultureInfo.InvariantCulture); | ||||
|  | ||||
| 			return view; | ||||
| @@ -70,14 +68,16 @@ namespace keepass2android.settings | ||||
| 					rounds = 1; | ||||
| 				} | ||||
|  | ||||
| 				ulong oldRounds = PwDatabase.KeyEncryptionRounds; | ||||
| 				Database db = App.Kp2a.GetDb(); | ||||
|  | ||||
| 				ulong oldRounds = db.KpDatabase.KeyEncryptionRounds; | ||||
|  | ||||
| 				if (oldRounds == rounds) | ||||
| 				{ | ||||
| 					return; | ||||
| 				} | ||||
|  | ||||
| 				PwDatabase.KeyEncryptionRounds = rounds; | ||||
| 				db.KpDatabase.KeyEncryptionRounds = rounds; | ||||
|  | ||||
| 				Handler handler = new Handler(); | ||||
| 				SaveDb save = new SaveDb(Context, App.Kp2a, new AfterSave(Context, handler, oldRounds, this)); | ||||
| @@ -108,7 +108,8 @@ namespace keepass2android.settings | ||||
| 					} | ||||
| 				} else { | ||||
| 					DisplayMessage(_ctx); | ||||
| 					_pref.PwDatabase.KeyEncryptionRounds = _oldRounds; | ||||
|  | ||||
| 					App.Kp2a.GetDb().KpDatabase.KeyEncryptionRounds = _oldRounds; | ||||
| 				} | ||||
| 				 | ||||
| 				base.Run(); | ||||
|   | ||||
| @@ -31,21 +31,15 @@ namespace keepass2android | ||||
|  | ||||
| 		private static class Timeout | ||||
| 		{ | ||||
| 			private const int RequestId = 0; | ||||
| 			private const long DefaultTimeout = 5 * 60 * 1000;  // 5 minutes | ||||
| 			 | ||||
| 			private static PendingIntent BuildIntent(Context ctx) | ||||
| 			{ | ||||
| 				Intent intent = new Intent(Intents.Timeout); | ||||
| 				PendingIntent sender = PendingIntent.GetBroadcast(ctx, RequestId, intent, PendingIntentFlags.CancelCurrent); | ||||
|  | ||||
| 				return sender; | ||||
| 				return PendingIntent.GetBroadcast(ctx, 0, new Intent(Intents.LockDatabase), PendingIntentFlags.UpdateCurrent); | ||||
| 			} | ||||
|  | ||||
| 			public static void Start(Context ctx) | ||||
| 			{ | ||||
|  | ||||
|  | ||||
| 				ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(ctx); | ||||
| 				String sTimeout = prefs.GetString(ctx.GetString(Resource.String.app_timeout_key), ctx.GetString(Resource.String.clipboard_timeout_default)); | ||||
|  | ||||
| @@ -61,8 +55,6 @@ namespace keepass2android | ||||
| 					return; | ||||
| 				} | ||||
|  | ||||
| 				ctx.StartService(new Intent(ctx, typeof(TimeoutService))); | ||||
|  | ||||
| 				long triggerTime = Java.Lang.JavaSystem.CurrentTimeMillis() + timeout; | ||||
| 				AlarmManager am = (AlarmManager)ctx.GetSystemService(Context.AlarmService); | ||||
|  | ||||
| @@ -76,69 +68,25 @@ namespace keepass2android | ||||
|  | ||||
| 				Kp2aLog.Log("Timeout cancel"); | ||||
| 				am.Cancel(BuildIntent(ctx)); | ||||
|  | ||||
| 				ctx.StopService(new Intent(ctx, typeof(TimeoutService))); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		public static void Pause(Activity act) { | ||||
| 			// Record timeout time in case timeout service is killed | ||||
| 			long time = Java.Lang.JavaSystem.CurrentTimeMillis(); | ||||
| 			 | ||||
| 			ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(act); | ||||
| 			ISharedPreferencesEditor edit = prefs.Edit(); | ||||
| 			edit.PutLong(act.GetString(Resource.String.timeout_key), time); | ||||
|  | ||||
| 			Kp2aLog.Log("Pause: start at " + time); | ||||
| 			 | ||||
| 			EditorCompat.Apply(edit); | ||||
| 			 | ||||
| 			if ( App.Kp2a.GetDb().Open ) { | ||||
| 		public static void Pause(Activity act)  | ||||
| 		{ | ||||
| 			if ( App.Kp2a.DatabaseIsUnlocked ) | ||||
| 			{ | ||||
| 				Timeout.Start(act); | ||||
| 			} | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
| 		public static void Resume(Activity act) { | ||||
| 			if ( App.Kp2a.GetDb().Loaded ) { | ||||
| 		public static void Resume(Activity act)  | ||||
| 		{ | ||||
| 			if ( App.Kp2a.GetDb().Loaded )  | ||||
| 			{ | ||||
| 				Timeout.Cancel(act); | ||||
| 			} | ||||
| 			 | ||||
| 			 | ||||
| 			// Check whether the timeout has expired | ||||
| 			long curTime = Java.Lang.JavaSystem.CurrentTimeMillis(); | ||||
| 			 | ||||
| 			ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(act); | ||||
| 			long timeoutStart = prefs.GetLong(act.GetString(Resource.String.timeout_key), -1); | ||||
| 			Kp2aLog.Log("timeoutStart=" + timeoutStart); | ||||
| 			// The timeout never started | ||||
| 			if (timeoutStart == -1) { | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			 | ||||
| 			String sTimeout = prefs.GetString(act.GetString(Resource.String.app_timeout_key), act.GetString(Resource.String.clipboard_timeout_default)); | ||||
| 			long timeout; | ||||
| 			if (!long.TryParse(sTimeout, out timeout) || (timeout == -1)) | ||||
| 			{ | ||||
| 				Kp2aLog.Log("exit with timeout=" + timeout + "/"+sTimeout); | ||||
| 				// We are set to never timeout | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			long diff = curTime - timeoutStart; | ||||
| 			if (diff >= timeout) | ||||
| 			{ | ||||
| 				// We have timed out | ||||
| 				Kp2aLog.Log("Shutdown due to " + diff + ">=" + timeout); | ||||
| 				App.Kp2a.SetShutdown(); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				Kp2aLog.Log("No shutdown due to " + diff + "<" + timeout); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		static bool IocChanged(IOConnectionInfo ioc, IOConnectionInfo other) | ||||
| @@ -148,11 +96,10 @@ namespace keepass2android | ||||
| 		} | ||||
| 		 | ||||
| 		public static bool CheckShutdown(Activity act, IOConnectionInfo ioc) { | ||||
| 			if ((  App.Kp2a.GetDb().Loaded && (App.Kp2a.IsShutdown() || App.Kp2a.GetDb().Locked) )  | ||||
| 			if ((  !App.Kp2a.DatabaseIsUnlocked )  | ||||
| 			    || (IocChanged(ioc, App.Kp2a.GetDb().Ioc))) //file was changed from ActionSend-Intent | ||||
| 			{ | ||||
| 				act.SetResult(KeePass.ExitLock); | ||||
| 				act.Finish(); | ||||
| 				App.Kp2a.LockDatabase(); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
|   | ||||
 Philipp Crocoll
					Philipp Crocoll