diff --git a/.gitignore b/.gitignore
index 31b691ec..e3e3b265 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/src/KeePassLib2Android/Kp2aLog.cs b/src/KeePassLib2Android/Kp2aLog.cs
index 9a0eb96f..f7a4d1ac 100644
--- a/src/KeePassLib2Android/Kp2aLog.cs
+++ b/src/KeePassLib2Android/Kp2aLog.cs
@@ -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);
+ }
}
+
}
}
diff --git a/src/KeePassLib2Android/Serialization/KdbxFile.Read.cs b/src/KeePassLib2Android/Serialization/KdbxFile.Read.cs
index a5ef3c81..1ce77040 100644
--- a/src/KeePassLib2Android/Serialization/KdbxFile.Read.cs
+++ b/src/KeePassLib2Android/Serialization/KdbxFile.Read.cs
@@ -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();
diff --git a/src/Kp2aBusinessLogic/IKp2aApp.cs b/src/Kp2aBusinessLogic/IKp2aApp.cs
index 71c8cca1..40e340bd 100644
--- a/src/Kp2aBusinessLogic/IKp2aApp.cs
+++ b/src/Kp2aBusinessLogic/IKp2aApp.cs
@@ -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
{
+ ///
+ /// Locks the currently open database, quicklocking if available (unless false is passed for allowQuickUnlock)
+ ///
+ void LockDatabase(bool allowQuickUnlock = true);
///
- /// Set the flag that the database needs to be locked.
+ /// Loads the specified data as the currently open database, as unlocked.
///
- void SetShutdown();
+ void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string s, string keyFile, ProgressDialogStatusLogger statusLogger);
///
/// Returns the current database
diff --git a/src/Kp2aBusinessLogic/database/Database.cs b/src/Kp2aBusinessLogic/database/Database.cs
index 93e2a542..50e78af7 100644
--- a/src/Kp2aBusinessLogic/database/Database.cs
+++ b/src/Kp2aBusinessLogic/database/Database.cs
@@ -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)
+ ///
+ /// Do not call this method directly. Call App.Kp2a.LoadDatabase instead.
+ ///
+ public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, String password, String keyfile, ProgressDialogStatusLogger status)
{
PwDatabase pwDatabase = new PwDatabase();
@@ -120,12 +104,13 @@ namespace keepass2android
throw new KeyFileException();
}
}
-
+
+ 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;
}
diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs b/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs
index 9ba8c469..7b572d16 100644
--- a/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs
+++ b/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs
@@ -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);
diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs b/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs
index 1586b0df..1c481bcd 100644
--- a/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs
+++ b/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs
@@ -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();
diff --git a/src/Kp2aBusinessLogic/database/edit/LoadDB.cs b/src/Kp2aBusinessLogic/database/edit/LoadDB.cs
index 239c04a9..9e71164d 100644
--- a/src/Kp2aBusinessLogic/database/edit/LoadDB.cs
+++ b/src/Kp2aBusinessLogic/database/edit/LoadDB.cs
@@ -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 _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 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) {
diff --git a/src/keepass2android/EntryActivity.cs b/src/keepass2android/EntryActivity.cs
index 48125078..a6595c7c 100644
--- a/src/keepass2android/EntryActivity.cs
+++ b/src/keepass2android/EntryActivity.cs
@@ -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 {
diff --git a/src/keepass2android/EntryEditActivity.cs b/src/keepass2android/EntryEditActivity.cs
index e4859e0d..9d7cdda9 100644
--- a/src/keepass2android/EntryEditActivity.cs
+++ b/src/keepass2android/EntryEditActivity.cs
@@ -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));
diff --git a/src/keepass2android/GroupBaseActivity.cs b/src/keepass2android/GroupBaseActivity.cs
index e4c8d296..395c5a77 100644
--- a/src/keepass2android/GroupBaseActivity.cs
+++ b/src/keepass2android/GroupBaseActivity.cs
@@ -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);
}
}
diff --git a/src/keepass2android/KeePass.cs b/src/keepass2android/KeePass.cs
index a6962594..514f6bbb 100644
--- a/src/keepass2android/KeePass.cs
+++ b/src/keepass2android/KeePass.cs
@@ -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);
diff --git a/src/keepass2android/LifecycleDebugActivity.cs b/src/keepass2android/LifecycleDebugActivity.cs
index 25bbc295..cba77ac9 100644
--- a/src/keepass2android/LifecycleDebugActivity.cs
+++ b/src/keepass2android/LifecycleDebugActivity.cs
@@ -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);
}
}
diff --git a/src/keepass2android/LockCloseActivity.cs b/src/keepass2android/LockCloseActivity.cs
index 78b08b2f..89336ad7 100644
--- a/src/keepass2android/LockCloseActivity.cs
+++ b/src/keepass2android/LockCloseActivity.cs
@@ -15,7 +15,10 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
along with Keepass2Android. If not, see .
*/
+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;
+ }
+ }
+ }
}
}
diff --git a/src/keepass2android/LockCloseListActivity.cs b/src/keepass2android/LockCloseListActivity.cs
index 3a0117e0..dae9f657 100644
--- a/src/keepass2android/LockCloseListActivity.cs
+++ b/src/keepass2android/LockCloseListActivity.cs
@@ -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;
+ }
+ }
+ }
}
}
diff --git a/src/keepass2android/LockingClosePreferenceActivity.cs b/src/keepass2android/LockingClosePreferenceActivity.cs
index d0a688e3..c6f152ee 100644
--- a/src/keepass2android/LockingClosePreferenceActivity.cs
+++ b/src/keepass2android/LockingClosePreferenceActivity.cs
@@ -15,6 +15,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
along with Keepass2Android. If not, see .
*/
+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;
+ }
+ }
+ }
}
}
diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs
index 21738fbb..3e4e7dc1 100644
--- a/src/keepass2android/PasswordActivity.cs
+++ b/src/keepass2android/PasswordActivity.cs
@@ -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
{
@@ -48,12 +52,15 @@ namespace keepass2android
private const String KeyServercredmode = "serverCredRememberMode";
private const String ViewIntent = "android.intent.action.VIEW";
-
+
+ private Task _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)
@@ -352,21 +279,17 @@ namespace keepass2android
ErrorMessage(Resource.String.error_nopass);
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(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_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;
+
+
}
+
+
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);
}
}
diff --git a/src/keepass2android/QuickUnlock.cs b/src/keepass2android/QuickUnlock.cs
index 72070451..df26a061 100644
--- a/src/keepass2android/QuickUnlock.cs
+++ b/src/keepass2android/QuickUnlock.cs
@@ -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);
}
}
}
diff --git a/src/keepass2android/Resources/drawable-hdpi/ic_launcher_red.png b/src/keepass2android/Resources/drawable-hdpi/ic_launcher_red.png
new file mode 100644
index 00000000..797eb52f
Binary files /dev/null and b/src/keepass2android/Resources/drawable-hdpi/ic_launcher_red.png differ
diff --git a/src/keepass2android/Resources/drawable-mdpi/ic_launcher_red.png b/src/keepass2android/Resources/drawable-mdpi/ic_launcher_red.png
new file mode 100644
index 00000000..d01a118a
Binary files /dev/null and b/src/keepass2android/Resources/drawable-mdpi/ic_launcher_red.png differ
diff --git a/src/keepass2android/Resources/drawable-v11/ic_unlocked_gray.png b/src/keepass2android/Resources/drawable-v11/ic_unlocked_gray.png
new file mode 100644
index 00000000..c054e5e3
Binary files /dev/null and b/src/keepass2android/Resources/drawable-v11/ic_unlocked_gray.png differ
diff --git a/src/keepass2android/Resources/drawable-xhdpi/ic_launcher_red.png b/src/keepass2android/Resources/drawable-xhdpi/ic_launcher_red.png
new file mode 100644
index 00000000..a048b8c6
Binary files /dev/null and b/src/keepass2android/Resources/drawable-xhdpi/ic_launcher_red.png differ
diff --git a/src/keepass2android/Resources/drawable-xxhdpi/ic_launcher_red.png b/src/keepass2android/Resources/drawable-xxhdpi/ic_launcher_red.png
new file mode 100644
index 00000000..c96017cb
Binary files /dev/null and b/src/keepass2android/Resources/drawable-xxhdpi/ic_launcher_red.png differ
diff --git a/src/keepass2android/Resources/drawable/ic_launcher_red.png b/src/keepass2android/Resources/drawable/ic_launcher_red.png
new file mode 100644
index 00000000..d01a118a
Binary files /dev/null and b/src/keepass2android/Resources/drawable/ic_launcher_red.png differ
diff --git a/src/keepass2android/Resources/drawable/ic_unlocked_gray.png b/src/keepass2android/Resources/drawable/ic_unlocked_gray.png
new file mode 100644
index 00000000..3a0ca74b
Binary files /dev/null and b/src/keepass2android/Resources/drawable/ic_unlocked_gray.png differ
diff --git a/src/keepass2android/Resources/layout-v14/password.xml b/src/keepass2android/Resources/layout-v14/password.xml
index 0c6e927f..134b6b16 100644
--- a/src/keepass2android/Resources/layout-v14/password.xml
+++ b/src/keepass2android/Resources/layout-v14/password.xml
@@ -91,16 +91,10 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/keyfileLine" />
-
\ No newline at end of file
diff --git a/src/keepass2android/Resources/layout/password.xml b/src/keepass2android/Resources/layout/password.xml
index e586320f..5fd886d4 100644
--- a/src/keepass2android/Resources/layout/password.xml
+++ b/src/keepass2android/Resources/layout/password.xml
@@ -79,16 +79,10 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/pass_keyfile" />
-
\ No newline at end of file
diff --git a/src/keepass2android/Resources/menu-v11/password.xml b/src/keepass2android/Resources/menu-v11/password.xml
index f1e24664..3d2e0470 100644
--- a/src/keepass2android/Resources/menu-v11/password.xml
+++ b/src/keepass2android/Resources/menu-v11/password.xml
@@ -20,6 +20,11 @@
android:title="@string/menu_about"
android:icon="@android:drawable/ic_menu_help"
android:showAsAction="ifRoom"
+ />
+
+
- omitbackup
list_size
sort_key
- timeout_key
TanExpiresOnUse_key
ShowUsernameInList_key
RememberRecentFiles_key
@@ -92,4 +91,11 @@
- 20
- 28
+
+ ShowUnlockedNotification
+ true
+
+ PreloadDatabaseEnabled
+ true
+
\ No newline at end of file
diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml
index bfaea71f..db1ea018 100644
--- a/src/keepass2android/Resources/values/strings.xml
+++ b/src/keepass2android/Resources/values/strings.xml
@@ -131,6 +131,7 @@
Search
Advanced Search
Go to URL
+ Change database…
Minus
Never
Yes
@@ -218,7 +219,8 @@
Add file attachment...
Add additional string
Delete additional string
- Database loaded, QuickUnlock enabled.
+ %1$s: Locked. QuickUnlock enabled.
+ %1$s: Unlocked.
Enter server credentials
File transactions
Use file transactions for writing databases
@@ -231,8 +233,14 @@
Make full entry accessible through the KP2A keyboard (recommended).
Keyboard selection dialog
Open keyboard selection dialog when entry is available through KP2A keyboard after search.
-
- Do you want to overwrite the existing binary with the same name?
+
+ Notification while unlocked
+ Show an ongoing notification while the database is unlocked.
+
+ Pre-load database file
+ Start background loading or downloading of the database file during password entry.
+
+ Do you want to overwrite the existing binary with the same name?
Overwrite existing binary?
Overwrite
Rename
diff --git a/src/keepass2android/Resources/xml/preferences.xml b/src/keepass2android/Resources/xml/preferences.xml
index 9fa794ce..b2424ad5 100644
--- a/src/keepass2android/Resources/xml/preferences.xml
+++ b/src/keepass2android/Resources/xml/preferences.xml
@@ -71,6 +71,13 @@
android:entryValues="@array/clipboard_timeout_values"
android:dialogTitle="@string/app_timeout"
android:defaultValue="@string/clipboard_timeout_default"/>
+
-
+
diff --git a/src/keepass2android/ShareUrlResults.cs b/src/keepass2android/ShareUrlResults.cs
index 1628bf15..a95b09d6 100644
--- a/src/keepass2android/ShareUrlResults.cs
+++ b/src/keepass2android/ShareUrlResults.cs
@@ -71,7 +71,7 @@ namespace keepass2android
Finish();
}
- else if (_db.Locked)
+ else if (App.Kp2a.QuickLocked)
{
PasswordActivity.Launch(this,_db.Ioc, AppTask);
Finish();
diff --git a/src/keepass2android/app/App.cs b/src/keepass2android/app/App.cs
index ff4931ac..3038dfe6 100644
--- a/src/keepass2android/app/App.cs
+++ b/src/keepass2android/app/App.cs
@@ -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,26 +63,97 @@ namespace keepass2android
///
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; }
+
+ ///
+ /// If true, the database must be regarded as locked and not exposed to the user.
+ ///
+ public bool QuickLocked { get; private set; }
+
+ #endregion
+
+ private Database _db;
+
///
/// 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)
diff --git a/src/keepass2android/app/ApplicationBroadcastReceiver.cs b/src/keepass2android/app/ApplicationBroadcastReceiver.cs
new file mode 100644
index 00000000..93a6f8c1
--- /dev/null
+++ b/src/keepass2android/app/ApplicationBroadcastReceiver.cs
@@ -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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/keepass2android/fileselect/FileSelectActivity.cs b/src/keepass2android/fileselect/FileSelectActivity.cs
index cc374636..ec9d7d7c 100644
--- a/src/keepass2android/fileselect/FileSelectActivity.cs
+++ b/src/keepass2android/fileselect/FileSelectActivity.cs
@@ -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);
diff --git a/src/keepass2android/intents/Intents.cs b/src/keepass2android/intents/Intents.cs
index 59762065..fae3a268 100644
--- a/src/keepass2android/intents/Intents.cs
+++ b/src/keepass2android/intents/Intents.cs
@@ -22,9 +22,14 @@ namespace keepass2android
///
/// Contains constants to be used in intents
///
- public class Intents {
- public const String Timeout = "keepass2android.timeout";
+ public class Intents
+ {
+ /// Broadcast this intent to lock the database
+ public const String LockDatabase = "keepass2android.lock_database";
+ /// This intent will be broadcast once the database has been locked. Sensitive information displayed should be hidden and unloaded.
+ public const String DatabaseLocked = "keepass2android.database_locked";
+
public const String CopyUsername = "keepass2android.copy_username";
public const String CopyPassword = "keepass2android.copy_password";
public const String CheckKeyboard = "keepass2android.check_keyboard";
diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj
index 4e30551a..ec1cc561 100644
--- a/src/keepass2android/keepass2android.csproj
+++ b/src/keepass2android/keepass2android.csproj
@@ -77,6 +77,7 @@
+
@@ -86,6 +87,8 @@
+
+
@@ -127,12 +130,10 @@
-
-
@@ -703,4 +704,25 @@
Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/keepass2android/search/SearchProvider.cs b/src/keepass2android/search/SearchProvider.cs
index d2282cb0..6b6f5e71 100644
--- a/src/keepass2android/search/SearchProvider.cs
+++ b/src/keepass2android/search/SearchProvider.cs
@@ -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))
{
diff --git a/src/keepass2android/search/SearchResults.cs b/src/keepass2android/search/SearchResults.cs
index 256bd133..bc2d7a84 100644
--- a/src/keepass2android/search/SearchResults.cs
+++ b/src/keepass2android/search/SearchResults.cs
@@ -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();
}
diff --git a/src/keepass2android/services/CopyToClipboardService.cs b/src/keepass2android/services/CopyToClipboardService.cs
index c64e5450..36868f5d 100644
--- a/src/keepass2android/services/CopyToClipboardService.cs
+++ b/src/keepass2android/services/CopyToClipboardService.cs
@@ -57,7 +57,7 @@ namespace keepass2android
CopyToClipboardBroadcastReceiver _copyToClipBroadcastReceiver;
NotificationDeletedBroadcastReceiver _notificationDeletedBroadcastReceiver;
-
+ StopOnLockBroadcastReceiver _stopOnLockBroadcastReceiver;
public CopyToClipboardService()
{
@@ -73,7 +73,12 @@ namespace keepass2android
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
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();
}
@@ -142,8 +163,10 @@ 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
{
diff --git a/src/keepass2android/services/OngoingNotificationsService.cs b/src/keepass2android/services/OngoingNotificationsService.cs
new file mode 100644
index 00000000..d9a620af
--- /dev/null
+++ b/src/keepass2android/services/OngoingNotificationsService.cs
@@ -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 .
+ */
+
+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
+{
+ ///
+ /// Service for showing ongoing notifications
+ ///
+ /// Shows database unlocked warning persistent notification
+ /// Shows Quick-Unlock notification
+ ///
+ [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
+ }
+}
+
diff --git a/src/keepass2android/services/QuickUnlockForegroundService.cs b/src/keepass2android/services/QuickUnlockForegroundService.cs
deleted file mode 100644
index a3d3b813..00000000
--- a/src/keepass2android/services/QuickUnlockForegroundService.cs
+++ /dev/null
@@ -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 .
- */
-
-using Android.App;
-using Android.Content;
-using Android.OS;
-using Android.Support.V4.App;
-using Android.Graphics;
-
-namespace keepass2android
-{
- ///
- /// 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)
- ///
-[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");
- }
- }
-}
-
diff --git a/src/keepass2android/services/TimeoutService.cs b/src/keepass2android/services/TimeoutService.cs
deleted file mode 100644
index 3c960f92..00000000
--- a/src/keepass2android/services/TimeoutService.cs
+++ /dev/null
@@ -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 .
- */
-
-using System;
-using Android.App;
-using Android.Content;
-using Android.OS;
-using Android.Runtime;
-using Android.Util;
-
-namespace keepass2android
-{
- ///
- /// Manages timeout to lock the database after some idle time
- ///
- [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();
- }
- }
- }
-
- }
-}
-
diff --git a/src/keepass2android/settings/AppSettingsActivity.cs b/src/keepass2android/settings/AppSettingsActivity.cs
index 6f2741b7..23596856 100644
--- a/src/keepass2android/settings/AppSettingsActivity.cs
+++ b/src/keepass2android/settings/AppSettingsActivity.cs
@@ -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
{
///
- /// 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
///
[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;
- }
-
-
}
}
diff --git a/src/keepass2android/settings/DatabaseSettingsActivity.cs b/src/keepass2android/settings/DatabaseSettingsActivity.cs
new file mode 100644
index 00000000..b2ce6cb7
--- /dev/null
+++ b/src/keepass2android/settings/DatabaseSettingsActivity.cs
@@ -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 .
+ */
+
+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
+{
+ ///
+ /// Activity to configure the application and database settings. The database must be unlocked, and this activity will close if it becomes locked.
+ ///
+ [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;
+ }
+ }
+}
+
diff --git a/src/keepass2android/settings/RoundsPreference.cs b/src/keepass2android/settings/RoundsPreference.cs
index 9bd50e56..c3e59191 100644
--- a/src/keepass2android/settings/RoundsPreference.cs
+++ b/src/keepass2android/settings/RoundsPreference.cs
@@ -31,7 +31,6 @@ namespace keepass2android.settings
///
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;
@@ -69,15 +67,17 @@ namespace keepass2android.settings
if ( rounds < 1 ) {
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();
diff --git a/src/keepass2android/timeout/TimeoutHelper.cs b/src/keepass2android/timeout/TimeoutHelper.cs
index 107a80bd..ad95a2d4 100644
--- a/src/keepass2android/timeout/TimeoutHelper.cs
+++ b/src/keepass2android/timeout/TimeoutHelper.cs
@@ -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;