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
This commit is contained in:
Philipp Crocoll 2013-08-10 20:25:10 +02:00
commit 370c937578
48 changed files with 1002 additions and 729 deletions

25
.gitignore vendored
View File

@ -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/todos.cs
/src/keepass2android/obj /src/keepass2android/obj
/src/keepass2android/bin /src/keepass2android/bin

View File

@ -26,17 +26,30 @@ namespace keepass2android
{ {
private static bool? _logToFile; private static bool? _logToFile;
private static object _fileLocker = new object();
public static void Log(string message) public static void Log(string message)
{ {
Android.Util.Log.Debug("KP2A", message); Android.Util.Log.Debug("KP2A", message);
if (LogToFile) if (LogToFile)
{
lock (_fileLocker)
{
try
{ {
using (var streamWriter = File.AppendText(LogFilename)) using (var streamWriter = File.AppendText(LogFilename))
{ {
string stringToLog = DateTime.Now+":"+DateTime.Now.Millisecond+ " -- " + message; string stringToLog = DateTime.Now + ":" + DateTime.Now.Millisecond + " -- " + message;
streamWriter.WriteLine(stringToLog); streamWriter.WriteLine(stringToLog);
} }
} }
catch (Exception e)
{
Android.Util.Log.Debug("KP2A", "Couldn't write to log file. " + e);
}
}
}
} }

View File

@ -162,17 +162,6 @@ namespace KeePassLib.Serialization
finally { CommonCleanUpRead(sSource, hashedStream); } 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) private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
{ {
hashedStream.Close(); hashedStream.Close();

View File

@ -1,5 +1,6 @@
using System; using System;
using Android.App; using Android.App;
using System.IO;
using Android.Content; using Android.Content;
using Android.OS; using Android.OS;
using KeePassLib.Serialization; using KeePassLib.Serialization;
@ -13,11 +14,15 @@ namespace keepass2android
/// This also contains methods which are UI specific and should be replacable for testing. /// This also contains methods which are UI specific and should be replacable for testing.
public interface IKp2aApp 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> /// <summary>
/// Set the flag that the database needs to be locked. /// Loads the specified data as the currently open database, as unlocked.
/// </summary> /// </summary>
void SetShutdown(); void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string s, string keyFile, ProgressDialogStatusLogger statusLogger);
/// <summary> /// <summary>
/// Returns the current database /// Returns the current database

View File

@ -72,25 +72,6 @@ namespace keepass2android
set { _loaded = value; } 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() public bool DidOpenFileChange()
{ {
if (Loaded == false) 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(); PwDatabase pwDatabase = new PwDatabase();
@ -121,11 +105,12 @@ namespace keepass2android
} }
} }
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
var filename = fileStorage.GetFilenameWithoutPathAndExt(iocInfo);
try try
{ {
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(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; LastFileVersion = fileVersion;
} }
catch (Exception) 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" //if we don't get a password, we don't know whether this means "empty password" or "no password"
//retry without password: //retry without password:
compositeKey.RemoveUserKey(compositeKey.GetUserKey(typeof (KcpPassword))); 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; else throw;
} }
@ -151,15 +141,6 @@ namespace keepass2android
SearchHelper = new SearchDbHelper(app); 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) { public PwGroup SearchForText(String str) {
PwGroup group = SearchHelper.SearchForText(this, str); PwGroup group = SearchHelper.SearchForText(this, str);
@ -225,7 +206,6 @@ namespace keepass2android
Root = null; Root = null;
KpDatabase = null; KpDatabase = null;
_loaded = false; _loaded = false;
_locked = false;
_reloadRequested = false; _reloadRequested = false;
} }

View File

@ -79,7 +79,7 @@ namespace keepass2android
else else
{ {
// Let's not bother recovering from a failure to save a deleted entry. It is too much work. // Let's not bother recovering from a failure to save a deleted entry. It is too much work.
App.SetShutdown(); App.LockDatabase();
} }
}, OnFinishToRun); }, OnFinishToRun);
} }
@ -99,7 +99,7 @@ namespace keepass2android
Db.Dirty.Add(pgRecycleBin); Db.Dirty.Add(pgRecycleBin);
} else { } else {
// Let's not bother recovering from a failure to save a deleted entry. It is too much work. // Let's not bother recovering from a failure to save a deleted entry. It is too much work.
App.SetShutdown(); App.LockDatabase();
} }
}, OnFinishToRun); }, OnFinishToRun);

View File

@ -108,7 +108,7 @@ namespace keepass2android
Db.Dirty.Add(pgParent); Db.Dirty.Add(pgParent);
} else { } else {
// Let's not bother recovering from a failure to save a deleted group. It is too much work. // Let's not bother recovering from a failure to save a deleted group. It is too much work.
App.SetShutdown(); App.LockDatabase();
} }
}, OnFinishToRun); }, OnFinishToRun);
} }
@ -146,7 +146,7 @@ namespace keepass2android
} }
} else { } else {
// Let's not bother recovering from a failure to save a deleted group. It is too much work. // Let's not bother recovering from a failure to save a deleted group. It is too much work.
_app.SetShutdown(); _app.LockDatabase();
} }
base.Run(); base.Run();

View File

@ -16,21 +16,25 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/ */
using System; using System;
using System.IO;
using System.Threading.Tasks;
using KeePassLib.Serialization; using KeePassLib.Serialization;
namespace keepass2android namespace keepass2android
{ {
public class LoadDb : RunnableOnFinish { public class LoadDb : RunnableOnFinish {
private readonly IOConnectionInfo _ioc; private readonly IOConnectionInfo _ioc;
private readonly Task<MemoryStream> _databaseData;
private readonly String _pass; private readonly String _pass;
private readonly String _key; private readonly String _key;
private readonly IKp2aApp _app; private readonly IKp2aApp _app;
private readonly bool _rememberKeyfile; 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; _app = app;
_ioc = ioc; _ioc = ioc;
_databaseData = databaseData;
_pass = pass; _pass = pass;
_key = key; _key = key;
@ -44,7 +48,7 @@ namespace keepass2android
try try
{ {
StatusLogger.UpdateMessage(UiStringKey.loading_database); 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); SaveFileData (_ioc, _key);
} catch (KeyFileException) { } catch (KeyFileException) {

View File

@ -95,7 +95,7 @@ namespace keepass2android
Database db = App.Kp2a.GetDb(); Database db = App.Kp2a.GetDb();
// Likely the app has been killed exit the activity // Likely the app has been killed exit the activity
if (! db.Loaded) if (!db.Loaded || (App.Kp2a.QuickLocked))
{ {
Finish(); Finish();
return; return;
@ -600,9 +600,7 @@ namespace keepass2android
} }
return true; return true;
case Resource.Id.menu_lock: case Resource.Id.menu_lock:
App.Kp2a.SetShutdown(); App.Kp2a.LockDatabase();
SetResult(KeePass.ExitLock);
Finish();
return true; return true;
case Resource.Id.menu_translate: case Resource.Id.menu_translate:
try { try {

View File

@ -92,8 +92,7 @@ namespace keepass2android
_closeForReload = false; _closeForReload = false;
// Likely the app has been killed exit the activity // Likely the app has been killed exit the activity
Database db = App.Kp2a.GetDb(); if (!App.Kp2a.DatabaseIsUnlocked)
if (! db.Open)
{ {
Finish(); Finish();
return; return;
@ -107,6 +106,8 @@ namespace keepass2android
} else } else
{ {
Database db = App.Kp2a.GetDb();
App.Kp2a.EntryEditActivityState = new EntryEditActivityState(); App.Kp2a.EntryEditActivityState = new EntryEditActivityState();
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this); ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
State.ShowPassword = ! prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default)); State.ShowPassword = ! prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));

View File

@ -240,9 +240,7 @@ namespace keepass2android
return true; return true;
case Resource.Id.menu_lock: case Resource.Id.menu_lock:
App.Kp2a.SetShutdown(); App.Kp2a.LockDatabase();
SetResult(KeePass.ExitLock);
Finish();
return true; return true;
case Resource.Id.menu_search: case Resource.Id.menu_search:
@ -251,7 +249,7 @@ namespace keepass2android
return true; return true;
case Resource.Id.menu_app_settings: case Resource.Id.menu_app_settings:
AppSettingsActivity.Launch(this); DatabaseSettingsActivity.Launch(this);
return true; return true;
case Resource.Id.menu_change_master_key: case Resource.Id.menu_change_master_key:
@ -353,8 +351,7 @@ namespace keepass2android
Toast.MakeText(_act, "Unrecoverable error: " + Message, ToastLength.Long).Show(); Toast.MakeText(_act, "Unrecoverable error: " + Message, ToastLength.Long).Show();
}); });
App.Kp2a.SetShutdown(); App.Kp2a.LockDatabase(false);
_act.Finish();
} }
} }

View File

@ -24,6 +24,7 @@ using Android.Preferences;
using Android.Content.PM; using Android.Content.PM;
using Android.Text; using Android.Text;
using Android.Text.Method; using Android.Text.Method;
using KeePassLib.Serialization;
namespace keepass2android namespace keepass2android
{ {
@ -37,12 +38,8 @@ namespace keepass2android
public const Result ExitLock = Result.FirstUser+1; public const Result ExitLock = Result.FirstUser+1;
public const Result ExitRefresh = Result.FirstUser+2; public const Result ExitRefresh = Result.FirstUser+2;
public const Result ExitRefreshTitle = Result.FirstUser+3; public const Result ExitRefreshTitle = Result.FirstUser+3;
public const Result ExitForceLock = Result.FirstUser+4; public const Result ExitCloseAfterTaskComplete = Result.FirstUser+4;
public const Result ExitQuickUnlock = Result.FirstUser+5; public const Result ExitReloadDb = Result.FirstUser+6;
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;
AppTask _appTask; AppTask _appTask;
@ -106,7 +103,7 @@ namespace keepass2android
Dialog dialog = builder.Create(); Dialog dialog = builder.Create();
dialog.DismissEvent += (sender, e) => dialog.DismissEvent += (sender, e) =>
{ {
StartFileSelect(); LaunchNextActivity();
}; };
dialog.Show(); dialog.Show();
TextView message = (TextView) dialog.FindViewById(Android.Resource.Id.Message); TextView message = (TextView) dialog.FindViewById(Android.Resource.Id.Message);
@ -119,7 +116,7 @@ namespace keepass2android
} else } else
{ {
StartFileSelect(); LaunchNextActivity();
} }
@ -157,12 +154,42 @@ namespace keepass2android
} }
private void StartFileSelect() { IOConnectionInfo LoadIoc(string defaultFileName)
Intent intent = new Intent(this, typeof(FileSelectActivity)); {
//TEST Intent intent = new Intent(this, typeof(EntryActivity)); return App.Kp2a.FileDbHelper.CursorToIoc(App.Kp2a.FileDbHelper.FetchFileByName(defaultFileName));
//Intent intent = new Intent(this, typeof(SearchActivity)); }
//Intent intent = new Intent(this, typeof(QuickUnlock));
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); _appTask.ToIntent(intent);

View File

@ -56,8 +56,7 @@ namespace keepass2android
} }
else else
{ {
Kp2aLog.Log(" Loaded=" + App.Kp2a.GetDb().Loaded + ", Locked=" + App.Kp2a.GetDb().Locked Kp2aLog.Log(" DatabaseIsUnlocked=" + App.Kp2a.DatabaseIsUnlocked);
+ ", shutdown=" + App.Kp2a.IsShutdown());
} }
} }

View File

@ -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/>. along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/ */
using System;
using Android.Content;
using Android.OS; using Android.OS;
using Android.App;
using KeePassLib.Serialization; using KeePassLib.Serialization;
namespace keepass2android namespace keepass2android
@ -26,12 +29,25 @@ namespace keepass2android
/// Checks in OnResume whether the timeout occured and the database must be locked/closed. /// Checks in OnResume whether the timeout occured and the database must be locked/closed.
public class LockCloseActivity : LockingActivity { public class LockCloseActivity : LockingActivity {
IOConnectionInfo _ioc; private IOConnectionInfo _ioc;
private BroadcastReceiver _intentReceiver;
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
_ioc = App.Kp2a.GetDb().Ioc; _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); 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;
}
}
}
} }
} }

View File

@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/ */
using System; using System;
using Android.Content;
using Android.OS; using Android.OS;
using Android.Runtime; using Android.Runtime;
using KeePassLib.Serialization; using KeePassLib.Serialization;
@ -33,11 +34,18 @@ namespace keepass2android
} }
IOConnectionInfo _ioc; IOConnectionInfo _ioc;
private BroadcastReceiver _intentReceiver;
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
_ioc = App.Kp2a.GetDb().Ioc; _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) public LockCloseListActivity (IntPtr javaReference, JniHandleOwnership transfer)
@ -57,6 +65,39 @@ namespace keepass2android
App.Kp2a.CheckForOpenFileChanged(this); 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;
}
}
}
} }
} }

View File

@ -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/>. along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/ */
using Android.Content;
using Android.OS; using Android.OS;
using KeePassLib.Serialization; using KeePassLib.Serialization;
@ -25,11 +26,18 @@ namespace keepass2android
IOConnectionInfo _ioc; IOConnectionInfo _ioc;
private BroadcastReceiver _intentReceiver;
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
_ioc = App.Kp2a.GetDb().Ioc; _ioc = App.Kp2a.GetDb().Ioc;
_intentReceiver = new LockingClosePreferenceActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
RegisterReceiver(_intentReceiver, filter);
} }
protected override void OnResume() { protected override void OnResume() {
@ -37,6 +45,41 @@ namespace keepass2android
TimeoutHelper.CheckShutdown(this, _ioc); 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;
}
}
}
} }
} }

View File

@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/ */
using System; using System;
using System.Threading.Tasks;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.OS; using Android.OS;
@ -29,6 +30,9 @@ using Android.Text;
using Android.Content.PM; using Android.Content.PM;
using KeePassLib.Keys; using KeePassLib.Keys;
using KeePassLib.Serialization; using KeePassLib.Serialization;
using KeePassLib.Utility;
using MemoryStream = System.IO.MemoryStream;
namespace keepass2android namespace keepass2android
{ {
@ -49,11 +53,14 @@ namespace keepass2android
private const String ViewIntent = "android.intent.action.VIEW"; private const String ViewIntent = "android.intent.action.VIEW";
private Task<MemoryStream> _loadDbTask;
private IOConnectionInfo _ioConnection; private IOConnectionInfo _ioConnection;
private String _keyFile; private String _keyFile;
private bool _rememberKeyfile; private bool _rememberKeyfile;
ISharedPreferences _prefs; ISharedPreferences _prefs;
private bool _started;
public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer) public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer) : base(javaReference, transfer)
{ {
@ -123,114 +130,28 @@ namespace keepass2android
public void LaunchNextActivity() public void LaunchNextActivity()
{ {
AppTask.AfterUnlockDatabase(this); 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) protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{ {
base.OnActivityResult(requestCode, resultCode, data); base.OnActivityResult(requestCode, resultCode, data);
_startedWithActivityResult = true;
Kp2aLog.Log("PasswordActivity.OnActivityResult "+resultCode+"/"+requestCode); 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) //NOTE: original code from k eepassdroid used switch ((Android.App.Result)requestCode) { (but doesn't work here, although k eepassdroid works)
switch(resultCode) { switch(resultCode) {
case KeePass.ExitNormal: case KeePass.ExitNormal: // Returned to this screen using the Back key, treat as locking the database
if (!TryStartQuickUnlock()) App.Kp2a.LockDatabase();
{
SetEditText(Resource.Id.password, "");
}
break; break;
case KeePass.ExitLock: case KeePass.ExitLock:
if (!TryStartQuickUnlock()) // The database has already been locked, and the quick unlock screen will be shown if appropriate
{
LockAndClose();
}
break;
case KeePass.ExitForceLock:
SetEditText(Resource.Id.password, "");
UnloadDatabase();
break;
case KeePass.ExitForceLockAndChangeDb:
UnloadDatabase();
Finish();
break;
case KeePass.ExitChangeDb:
LockAndClose();
break; break;
case KeePass.ExitCloseAfterTaskComplete: case KeePass.ExitCloseAfterTaskComplete:
// Do not lock the database
SetResult(KeePass.ExitCloseAfterTaskComplete); SetResult(KeePass.ExitCloseAfterTaskComplete);
Finish(); Finish();
break; break;
case KeePass.ExitQuickUnlock:
App.Kp2a.GetDb().Locked = false;
LaunchNextActivity();
break;
case KeePass.ExitReloadDb: case KeePass.ExitReloadDb:
//if the activity was killed, fill password/keyfile so the user can directly hit load again //if the activity was killed, fill password/keyfile so the user can directly hit load again
if (App.Kp2a.GetDb().Loaded) if (App.Kp2a.GetDb().Loaded)
@ -252,9 +173,9 @@ namespace keepass2android
SetEditText(Resource.Id.pass_keyfile, kcpKeyfile.Path); SetEditText(Resource.Id.pass_keyfile, kcpKeyfile.Path);
} }
} }
UnloadDatabase(); App.Kp2a.LockDatabase(false);
break; break;
case Result.Ok: case Result.Ok: // Key file browse dialog OK'ed.
if (requestCode == Intents.RequestCodeFileBrowseForKeyfile) { if (requestCode == Intents.RequestCodeFileBrowseForKeyfile) {
string filename = Util.IntentToFilename(data); string filename = Util.IntentToFilename(data);
if (filename != null) { 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); AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
SetContentView(Resource.Layout.password); SetContentView(Resource.Layout.password);
@ -343,8 +271,7 @@ namespace keepass2android
Window.SetSoftInputMode(SoftInput.StateVisible); Window.SetSoftInputMode(SoftInput.StateVisible);
Button confirmButton = (Button)FindViewById(Resource.Id.pass_ok); Button confirmButton = (Button)FindViewById(Resource.Id.pass_ok);
confirmButton.Click += (sender, e) => confirmButton.Click += (sender, e) => {
{
String pass = GetEditText(Resource.Id.password); String pass = GetEditText(Resource.Id.password);
String key = GetEditText(Resource.Id.pass_keyfile); String key = GetEditText(Resource.Id.pass_keyfile);
if (pass.Length == 0 && key.Length == 0) if (pass.Length == 0 && key.Length == 0)
@ -353,20 +280,16 @@ namespace keepass2android
return; return;
} }
// Clear before we load
UnloadDatabase();
// Clear the shutdown flag
App.Kp2a.ClearShutdown();
CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock); CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock);
App.Kp2a.GetDb().QuickUnlockEnabled = cbQuickUnlock.Checked; App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
App.Kp2a.GetDb().QuickUnlockKeyLength = int.Parse(_prefs.GetString(GetString(Resource.String.QuickUnlockLength_key), GetString(Resource.String.QuickUnlockLength_default)));
Handler handler = new Handler(); Handler handler = new Handler();
LoadDb task = new LoadDb(App.Kp2a, _ioConnection, pass, key, new AfterLoad(handler, this)); LoadDb task = new LoadDb(App.Kp2a, _ioConnection, _loadDbTask, pass, key, new AfterLoad(handler, this));
ProgressTask pt = new ProgressTask(App.Kp2a, this, task); _loadDbTask = null; // prevent accidental re-use
pt.Run();
SetNewDefaultFile();
new ProgressTask(App.Kp2a, this, task).Run();
}; };
/*CheckBox checkBox = (CheckBox) FindViewById(Resource.Id.show_password); /*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); ImageButton browse = (ImageButton)FindViewById(Resource.Id.browse_button);
browse.Click += (sender, evt) => browse.Click += (sender, evt) =>
@ -438,8 +337,66 @@ namespace keepass2android
}; };
RetrieveSettings(); 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) protected override void OnSaveInstanceState(Bundle outState)
@ -448,41 +405,35 @@ namespace keepass2android
AppTask.ToBundle(outState); AppTask.ToBundle(outState);
} }
protected override void OnResume() { protected override void OnResume()
{
base.OnResume(); base.OnResume();
// If the application was shutdown make sure to clear the password field, if it // OnResume is run every time the activity comes to the foreground. This code should only run when the activity is started (OnStart), but must
// was saved in the instance state // 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 (App.Kp2a.IsShutdown()) { 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)
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()))
{ {
if (App.Kp2a.GetDb().Locked == false) _started = false;
if (App.Kp2a.DatabaseIsUnlocked)
{ {
LaunchNextActivity(); 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() { 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); CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock);
cbQuickUnlock.Checked = _prefs.GetBoolean(GetString(Resource.String.QuickUnlockDefaultEnabled_key), true); cbQuickUnlock.Checked = _prefs.GetBoolean(GetString(Resource.String.QuickUnlockDefaultEnabled_key), true);
} }
@ -548,23 +499,42 @@ namespace keepass2android
case Resource.Id.menu_app_settings: case Resource.Id.menu_app_settings:
AppSettingsActivity.Launch(this); AppSettingsActivity.Launch(this);
return true; 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); return base.OnOptionsItemSelected(item);
} }
private class AfterLoad : OnFinish { private class AfterLoad : OnFinish {
readonly PasswordActivity _act; readonly PasswordActivity _act;
public AfterLoad(Handler handler, PasswordActivity act):base(handler) {
public AfterLoad(Handler handler, PasswordActivity act):base(handler)
{
_act = act; _act = act;
} }
public override void Run() { public override void Run() {
if ( Success ) { if ( Success )
_act.StartQuickUnlockForegroundService(); {
_act.SetEditText(Resource.Id.password, "");
_act.LaunchNextActivity(); _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); DisplayMessage(_act);
} }
} }

View File

@ -72,18 +72,13 @@ namespace keepass2android
TextView txtLabel = (TextView)FindViewById(Resource.Id.QuickUnlock_label); 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}); txtLabel.Text = GetString(Resource.String.QuickUnlock_label, new Java.Lang.Object[]{quickUnlockLength});
EditText pwd= (EditText)FindViewById(Resource.Id.QuickUnlock_password); EditText pwd= (EditText)FindViewById(Resource.Id.QuickUnlock_password);
pwd.SetEms(quickUnlockLength); 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); Button btnUnlock = (Button)FindViewById(Resource.Id.QuickUnlock_button);
btnUnlock.Click += (object sender, EventArgs e) => 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)); String expectedPasswordPart = password.Substring(Math.Max(0,password.Length-quickUnlockLength),Math.Min(password.Length, quickUnlockLength));
if (pwd.Text == expectedPasswordPart) if (pwd.Text == expectedPasswordPart)
{ {
SetResult(KeePass.ExitQuickUnlock); App.Kp2a.UnlockDatabase();
} }
else else
{ {
SetResult(KeePass.ExitForceLock); App.Kp2a.LockDatabase(false);
Toast.MakeText(this, GetString(Resource.String.QuickUnlock_fail), ToastLength.Long).Show(); Toast.MakeText(this, GetString(Resource.String.QuickUnlock_fail), ToastLength.Long).Show();
} }
Finish(); Finish();
@ -106,21 +101,21 @@ namespace keepass2android
Button btnLock = (Button)FindViewById(Resource.Id.QuickUnlock_buttonLock); Button btnLock = (Button)FindViewById(Resource.Id.QuickUnlock_buttonLock);
btnLock.Click += (object sender, EventArgs e) => btnLock.Click += (object sender, EventArgs e) =>
{ {
SetResult(KeePass.ExitForceLockAndChangeDb); App.Kp2a.LockDatabase(false);
Finish(); Finish();
}; };
} }
protected override void OnResume() protected override void OnResume()
{ {
base.OnResume(); base.OnResume();
if ( ! App.Kp2a.GetDb().Loaded ) { EditText pwd = (EditText)FindViewById(Resource.Id.QuickUnlock_password);
SetResult(KeePass.ExitChangeDb); pwd.PostDelayed(() =>
Finish(); {
return; InputMethodManager keyboard = (InputMethodManager)GetSystemService(Context.InputMethodService);
} keyboard.ShowSoftInput(pwd, 0);
}, 50);
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -91,16 +91,10 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/keyfileLine" /> 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 <CheckBox
android:id="@+id/enable_quickunlock" android:id="@+id/enable_quickunlock"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/default_database" android:layout_below="@id/pass_ok"
android:text="@string/enable_quickunlock" /> android:text="@string/enable_quickunlock" />
</RelativeLayout> </RelativeLayout>

View File

@ -79,16 +79,10 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/pass_keyfile" /> 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 <CheckBox
android:id="@+id/enable_quickunlock" android:id="@+id/enable_quickunlock"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/default_database" android:layout_below="@id/pass_ok"
android:text="@string/enable_quickunlock" /> android:text="@string/enable_quickunlock" />
</RelativeLayout> </RelativeLayout>

View File

@ -20,6 +20,11 @@
android:title="@string/menu_about" android:title="@string/menu_about"
android:icon="@android:drawable/ic_menu_help" android:icon="@android:drawable/ic_menu_help"
android:showAsAction="ifRoom" 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" <item android:id="@+id/menu_app_settings"
android:title="@string/menu_app_settings" android:title="@string/menu_app_settings"

View File

@ -19,6 +19,10 @@
<item android:id="@+id/menu_app_settings" <item android:id="@+id/menu_app_settings"
android:icon="@android:drawable/ic_menu_preferences" android:icon="@android:drawable/ic_menu_preferences"
android:title="@string/menu_app_settings" 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" <item android:id="@+id/menu_about"
android:icon="@android:drawable/ic_menu_help" android:icon="@android:drawable/ic_menu_help"

View File

@ -39,7 +39,6 @@
<string name="omitbackup_key">omitbackup</string> <string name="omitbackup_key">omitbackup</string>
<string name="list_size_key">list_size</string> <string name="list_size_key">list_size</string>
<string name="sort_key">sort_key</string> <string name="sort_key">sort_key</string>
<string name="timeout_key">timeout_key</string>
<string name="TanExpiresOnUse_key">TanExpiresOnUse_key</string> <string name="TanExpiresOnUse_key">TanExpiresOnUse_key</string>
<string name="ShowUsernameInList_key">ShowUsernameInList_key</string> <string name="ShowUsernameInList_key">ShowUsernameInList_key</string>
<string name="RememberRecentFiles_key">RememberRecentFiles_key</string> <string name="RememberRecentFiles_key">RememberRecentFiles_key</string>
@ -92,4 +91,11 @@
<item>20</item> <item>20</item>
<item>28</item> <item>28</item>
</string-array> </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> </resources>

View File

@ -131,6 +131,7 @@
<string name="menu_search">Search</string> <string name="menu_search">Search</string>
<string name="menu_search_advanced">Advanced Search</string> <string name="menu_search_advanced">Advanced Search</string>
<string name="menu_url">Go to URL</string> <string name="menu_url">Go to URL</string>
<string name="menu_change_db">Change database…</string>
<string name="minus">Minus</string> <string name="minus">Minus</string>
<string name="never">Never</string> <string name="never">Never</string>
<string name="yes">Yes</string> <string name="yes">Yes</string>
@ -218,7 +219,8 @@
<string name="add_binary">Add file attachment...</string> <string name="add_binary">Add file attachment...</string>
<string name="add_extra_string">Add additional string</string> <string name="add_extra_string">Add additional string</string>
<string name="delete_extra_string">Delete 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="credentials_dialog_title">Enter server credentials</string>
<string name="UseFileTransactions_title">File transactions</string> <string name="UseFileTransactions_title">File transactions</string>
<string name="UseFileTransactions_summary">Use file transactions for writing databases</string> <string name="UseFileTransactions_summary">Use file transactions for writing databases</string>
@ -232,6 +234,12 @@
<string name="OpenKp2aKeyboardAutomatically_title">Keyboard selection dialog</string> <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="OpenKp2aKeyboardAutomatically_summary">Open keyboard selection dialog when entry is available through KP2A keyboard after search.</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">Do you want to overwrite the existing binary with the same name?</string>
<string name="AskOverwriteBinary_title">Overwrite existing binary?</string> <string name="AskOverwriteBinary_title">Overwrite existing binary?</string>
<string name="AskOverwriteBinary_yes">Overwrite</string> <string name="AskOverwriteBinary_yes">Overwrite</string>

View File

@ -71,6 +71,13 @@
android:entryValues="@array/clipboard_timeout_values" android:entryValues="@array/clipboard_timeout_values"
android:dialogTitle="@string/app_timeout" android:dialogTitle="@string/app_timeout"
android:defaultValue="@string/clipboard_timeout_default"/> 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 <CheckBoxPreference
android:key="@string/maskpass_key" android:key="@string/maskpass_key"
android:title="@string/maskpass_title" android:title="@string/maskpass_title"
@ -162,6 +169,12 @@
android:defaultValue="true" android:defaultValue="true"
android:title="@string/CheckForFileChangesOnSave_title" android:title="@string/CheckForFileChangesOnSave_title"
android:key="@string/CheckForFileChangesOnSave_key" /> 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>
</PreferenceScreen> </PreferenceScreen>

View File

@ -71,7 +71,7 @@ namespace keepass2android
Finish(); Finish();
} }
else if (_db.Locked) else if (App.Kp2a.QuickLocked)
{ {
PasswordActivity.Launch(this,_db.Ioc, AppTask); PasswordActivity.Launch(this,_db.Ioc, AppTask);
Finish(); Finish();

View File

@ -16,11 +16,14 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/ */
using System; using System;
using System.Collections.Generic;
using System.IO;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.OS; using Android.OS;
using Android.Runtime; using Android.Runtime;
using Android.Widget; using Android.Widget;
using KeePassLib.Keys;
using KeePassLib.Serialization; using KeePassLib.Serialization;
using Android.Preferences; using Android.Preferences;
using keepass2android.Io; using keepass2android.Io;
@ -60,25 +63,96 @@ namespace keepass2android
/// </summary> /// </summary>
public class Kp2aApp: IKp2aApp, ICacheSupervisor public class Kp2aApp: IKp2aApp, ICacheSupervisor
{ {
public bool IsShutdown() public void LockDatabase(bool allowQuickUnlock = true)
{ {
return _shutdown; 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");
} }
public void SetShutdown() UpdateOngoingNotification();
{ Application.Context.SendBroadcast(new Intent(Intents.DatabaseLocked));
Kp2aLog.Log("set shutdown");
_shutdown = true;
} }
public void ClearShutdown() public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string password, string keyFile, ProgressDialogStatusLogger statusLogger)
{ {
Kp2aLog.Log("clear shutdown"); _db.LoadData(this, ioConnectionInfo, memoryStream, password, keyFile, statusLogger);
_shutdown = false;
UpdateOngoingNotification();
} }
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; private Database _db;
private bool _shutdown;
/// <summary> /// <summary>
/// See comments to EntryEditActivityState. /// See comments to EntryEditActivityState.
@ -124,6 +198,7 @@ namespace keepass2android
{ {
if (_db.ReloadRequested) if (_db.ReloadRequested)
{ {
LockDatabase(false);
activity.SetResult(KeePass.ExitReloadDb); activity.SetResult(KeePass.ExitReloadDb);
activity.Finish(); activity.Finish();
//todo: return? //todo: return?
@ -143,6 +218,7 @@ namespace keepass2android
(dlgSender, dlgEvt) => (dlgSender, dlgEvt) =>
{ {
_db.ReloadRequested = true; _db.ReloadRequested = true;
LockDatabase(false);
activity.SetResult(KeePass.ExitReloadDb); activity.SetResult(KeePass.ExitReloadDb);
activity.Finish(); activity.Finish();
@ -223,7 +299,7 @@ namespace keepass2android
public RealProgressDialog(Context ctx) public RealProgressDialog(Context ctx)
{ {
this._pd = new ProgressDialog(ctx); _pd = new ProgressDialog(ctx);
} }
public void SetTitle(string title) public void SetTitle(string title)

View 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;
}
}
}
}

View File

@ -63,11 +63,6 @@ namespace keepass2android
internal AppTask AppTask; 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) void ShowFilenameDialog(bool showOpenButton, bool showCreateButton, bool showBrowseButton, string defaultFilename, string detailsText, int requestCodeBrowse)
{ {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
@ -319,14 +314,20 @@ namespace keepass2android
public override void Run() { public override void Run() {
if (Success) { if (Success) {
// Update the ongoing notification
_activity.StartService(new Intent(_activity, typeof(OngoingNotificationsService)));
if (_activity.RememberRecentFiles()) if (_activity.RememberRecentFiles())
{ {
// Add to recent files // Add to recent files
FileDbHelper dbHelper = App.Kp2a.FileDbHelper; FileDbHelper dbHelper = App.Kp2a.FileDbHelper;
//TODO: getFilename always returns "" -> bug? //TODO: getFilename always returns "" -> bug?
dbHelper.CreateFile(_ioc, Filename); dbHelper.CreateFile(_ioc, Filename);
} }
GroupActivity.Launch(_activity, _activity.AppTask); GroupActivity.Launch(_activity, _activity.AppTask);
} else { } else {
@ -391,6 +392,7 @@ namespace keepass2android
ioc.Password = password; ioc.Password = password;
ioc.CredSaveMode = (IOCredSaveMode)credentialRememberMode; ioc.CredSaveMode = (IOCredSaveMode)credentialRememberMode;
PasswordActivity.Launch(this, ioc, AppTask); PasswordActivity.Launch(this, ioc, AppTask);
Finish();
}); });
builder.SetView(LayoutInflater.Inflate(Resource.Layout.url_credentials, null)); builder.SetView(LayoutInflater.Inflate(Resource.Layout.url_credentials, null));
builder.SetNeutralButton(GetString(Android.Resource.String.Cancel), builder.SetNeutralButton(GetString(Android.Resource.String.Cancel),
@ -406,6 +408,7 @@ namespace keepass2android
try try
{ {
PasswordActivity.Launch(this, ioc, AppTask); PasswordActivity.Launch(this, ioc, AppTask);
Finish();
} catch (Java.IO.FileNotFoundException) } catch (Java.IO.FileNotFoundException)
{ {
Toast.MakeText(this, Resource.String.FileNotFound, ToastLength.Long).Show(); Toast.MakeText(this, Resource.String.FileNotFound, ToastLength.Long).Show();
@ -489,36 +492,6 @@ namespace keepass2android
_fileSelectButtons.UpdateExternalStorageWarning(); _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(); base.OnStart();
Kp2aLog.Log("FileSelect.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) { public override bool OnCreateOptionsMenu(IMenu menu) {
base.OnCreateOptionsMenu(menu); base.OnCreateOptionsMenu(menu);

View File

@ -22,8 +22,13 @@ namespace keepass2android
/// <summary> /// <summary>
/// Contains constants to be used in intents /// Contains constants to be used in intents
/// </summary> /// </summary>
public class Intents { public class Intents
public const String Timeout = "keepass2android.timeout"; {
/// <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 CopyUsername = "keepass2android.copy_username";
public const String CopyPassword = "keepass2android.copy_password"; public const String CopyPassword = "keepass2android.copy_password";

View File

@ -77,6 +77,7 @@
<Reference Include="Mono.Android.Support.v4" /> <Reference Include="Mono.Android.Support.v4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="app\ApplicationBroadcastReceiver.cs" />
<Compile Include="icons\DrawableFactory.cs" /> <Compile Include="icons\DrawableFactory.cs" />
<Compile Include="icons\Icons.cs" /> <Compile Include="icons\Icons.cs" />
<Compile Include="Resources\Resource.designer.cs" /> <Compile Include="Resources\Resource.designer.cs" />
@ -86,6 +87,8 @@
<Compile Include="fileselect\FileSelectActivity.cs" /> <Compile Include="fileselect\FileSelectActivity.cs" />
<Compile Include="fileselect\FileDbHelper.cs" /> <Compile Include="fileselect\FileDbHelper.cs" />
<Compile Include="search\SearchProvider.cs" /> <Compile Include="search\SearchProvider.cs" />
<Compile Include="services\OngoingNotificationsService.cs" />
<Compile Include="settings\DatabaseSettingsActivity.cs" />
<Compile Include="Utils\Util.cs" /> <Compile Include="Utils\Util.cs" />
<Compile Include="intents\Intents.cs" /> <Compile Include="intents\Intents.cs" />
<Compile Include="fileselect\BrowserDialog.cs" /> <Compile Include="fileselect\BrowserDialog.cs" />
@ -127,12 +130,10 @@
<Compile Include="compat\EditorCompat.cs" /> <Compile Include="compat\EditorCompat.cs" />
<Compile Include="compat\ActivityCompat.cs" /> <Compile Include="compat\ActivityCompat.cs" />
<Compile Include="ShareUrlResults.cs" /> <Compile Include="ShareUrlResults.cs" />
<Compile Include="services\TimeoutService.cs" />
<Compile Include="services\CopyToClipboardService.cs" /> <Compile Include="services\CopyToClipboardService.cs" />
<Compile Include="search\SearchActivity.cs" /> <Compile Include="search\SearchActivity.cs" />
<Compile Include="QuickUnlock.cs" /> <Compile Include="QuickUnlock.cs" />
<Compile Include="LifecycleDebugActivity.cs" /> <Compile Include="LifecycleDebugActivity.cs" />
<Compile Include="services\QuickUnlockForegroundService.cs" />
<Compile Include="AssemblyInfo.cs" /> <Compile Include="AssemblyInfo.cs" />
<Compile Include="views\FileSelectButtons.cs" /> <Compile Include="views\FileSelectButtons.cs" />
<Compile Include="EntryEditActivityState.cs" /> <Compile Include="EntryEditActivityState.cs" />
@ -703,4 +704,25 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</AndroidResource> </AndroidResource>
</ItemGroup> </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> </Project>

View File

@ -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) 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)) switch ((UriMatches)UriMatcher.Match(uri))
{ {

View File

@ -56,11 +56,9 @@ namespace keepass2android.search
private void ProcessIntent(Intent intent) private void ProcessIntent(Intent intent)
{ {
_db = App.Kp2a.GetDb();
// Likely the app has been killed exit the activity // Likely the app has been killed exit the activity
if ( ! _db.Open ) { if (!App.Kp2a.DatabaseIsUnlocked)
{
Finish(); Finish();
} }

View File

@ -57,7 +57,7 @@ namespace keepass2android
CopyToClipboardBroadcastReceiver _copyToClipBroadcastReceiver; CopyToClipboardBroadcastReceiver _copyToClipBroadcastReceiver;
NotificationDeletedBroadcastReceiver _notificationDeletedBroadcastReceiver; NotificationDeletedBroadcastReceiver _notificationDeletedBroadcastReceiver;
StopOnLockBroadcastReceiver _stopOnLockBroadcastReceiver;
public CopyToClipboardService() public CopyToClipboardService()
{ {
@ -74,6 +74,11 @@ namespace keepass2android
{ {
Kp2aLog.Log("Received intent to provide access to entry"); 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); String uuidBytes = intent.GetStringExtra(EntryActivity.KeyEntry);
bool closeAfterCreate = intent.GetBooleanExtra(EntryActivity.KeyCloseAfterCreate, false); bool closeAfterCreate = intent.GetBooleanExtra(EntryActivity.KeyCloseAfterCreate, false);
@ -99,12 +104,25 @@ namespace keepass2android
return StartCommandResult.RedeliverIntent; return StartCommandResult.RedeliverIntent;
} }
private void OnLockDatabase()
{
Kp2aLog.Log("Stopping clipboard service due to database lock");
StopSelf();
}
private NotificationManager _notificationManager; private NotificationManager _notificationManager;
private int _numElementsToWaitFor; private int _numElementsToWaitFor;
public override void OnDestroy() public override void OnDestroy()
{ {
Kp2aLog.Log("CopyToClipboardService.OnDestroy");
// These members might never get initialized if the app timed out // These members might never get initialized if the app timed out
if (_stopOnLockBroadcastReceiver != null)
{
UnregisterReceiver(_stopOnLockBroadcastReceiver);
}
if (_copyToClipBroadcastReceiver != null) if (_copyToClipBroadcastReceiver != null)
{ {
UnregisterReceiver(_copyToClipBroadcastReceiver); UnregisterReceiver(_copyToClipBroadcastReceiver);
@ -114,7 +132,10 @@ namespace keepass2android
UnregisterReceiver(_notificationDeletedBroadcastReceiver); UnregisterReceiver(_notificationDeletedBroadcastReceiver);
} }
if ( _notificationManager != null ) { if ( _notificationManager != null ) {
_notificationManager.CancelAll(); _notificationManager.Cancel(NotifyPassword);
_notificationManager.Cancel(NotifyUsername);
_notificationManager.Cancel(NotifyKeyboard);
_numElementsToWaitFor= 0; _numElementsToWaitFor= 0;
clearKeyboard(); clearKeyboard();
} }
@ -143,7 +164,9 @@ namespace keepass2android
// Notification Manager // Notification Manager
_notificationManager = (NotificationManager)GetSystemService(NotificationService); _notificationManager = (NotificationManager)GetSystemService(NotificationService);
_notificationManager.CancelAll(); _notificationManager.Cancel(NotifyPassword);
_notificationManager.Cancel(NotifyUsername);
_notificationManager.Cancel(NotifyKeyboard);
_numElementsToWaitFor = 0; _numElementsToWaitFor = 0;
clearKeyboard(); clearKeyboard();
@ -358,7 +381,24 @@ namespace keepass2android
return notify; 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 class CopyToClipboardBroadcastReceiver: BroadcastReceiver
{ {

View 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
}
}

View File

@ -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");
}
}
}

View File

@ -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();
}
}
}
}
}

View File

@ -16,122 +16,49 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/ */
using System; using System;
using System.Globalization;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.OS; using Android.OS;
using Android.Widget;
using Android.Preferences; using Android.Preferences;
using KeePassLib.Cryptography.Cipher;
namespace keepass2android namespace keepass2android
{ {
/// <summary> /// <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> /// </summary>
[Activity (Label = "@string/app_name", Theme="@style/NoTitleBar")] [Activity (Label = "@string/app_name", Theme="@style/NoTitleBar")]
public class AppSettingsActivity : LockingClosePreferenceActivity { public class AppSettingsActivity : LockingPreferenceActivity
public static bool KeyfileDefault = false; {
public static void Launch(Context ctx)
public static void Launch(Context ctx) { {
Intent i = new Intent(ctx, typeof(AppSettingsActivity)); ctx.StartActivity(new Intent(ctx, typeof(AppSettingsActivity)));
ctx.StartActivity(i);
} }
protected override void OnCreate(Bundle savedInstanceState) { protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
AddPreferencesFromResource(Resource.Xml.preferences); AddPreferencesFromResource(Resource.Xml.preferences);
Preference keyFile = FindPreference(GetString(Resource.String.keyfile_key)); FindPreference(GetString(Resource.String.keyfile_key)).PreferenceChange += OnRememberKeyFileHistoryChanged;
keyFile.PreferenceChange += (sender, e) => FindPreference(GetString(Resource.String.ShowUnlockedNotification_key)).PreferenceChange += OnShowUnlockedNotificationChanged;;
{
bool value = (bool) e.NewValue;
if ( ! value ) { FindPreference(GetString(Resource.String.db_key)).Enabled = false;
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;
} }
internal static void OnRememberKeyFileHistoryChanged(object sender, Preference.PreferenceChangeEventArgs eventArgs)
{
if (!(bool)eventArgs.NewValue)
{
App.Kp2a.FileDbHelper.DeleteAllKeys();
}
} }
private void setRounds(Database db, Preference rounds) { internal static void OnShowUnlockedNotificationChanged(object sender, Preference.PreferenceChangeEventArgs eventArgs)
rounds.Summary = db.KpDatabase.KeyEncryptionRounds.ToString(CultureInfo.InvariantCulture); {
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;
}
} }
} }

View 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;
}
}
}

View File

@ -31,7 +31,6 @@ namespace keepass2android.settings
/// </summary> /// </summary>
public class RoundsPreference : DialogPreference { public class RoundsPreference : DialogPreference {
internal PwDatabase PwDatabase;
internal TextView RoundsView; internal TextView RoundsView;
protected override View OnCreateDialogView() { protected override View OnCreateDialogView() {
@ -40,8 +39,7 @@ namespace keepass2android.settings
RoundsView = (TextView) view.FindViewById(Resource.Id.rounds); RoundsView = (TextView) view.FindViewById(Resource.Id.rounds);
Database db = App.Kp2a.GetDb(); Database db = App.Kp2a.GetDb();
PwDatabase = db.KpDatabase; ulong numRounds = db.KpDatabase.KeyEncryptionRounds;
ulong numRounds = PwDatabase.KeyEncryptionRounds;
RoundsView.Text = numRounds.ToString(CultureInfo.InvariantCulture); RoundsView.Text = numRounds.ToString(CultureInfo.InvariantCulture);
return view; return view;
@ -70,14 +68,16 @@ namespace keepass2android.settings
rounds = 1; rounds = 1;
} }
ulong oldRounds = PwDatabase.KeyEncryptionRounds; Database db = App.Kp2a.GetDb();
ulong oldRounds = db.KpDatabase.KeyEncryptionRounds;
if (oldRounds == rounds) if (oldRounds == rounds)
{ {
return; return;
} }
PwDatabase.KeyEncryptionRounds = rounds; db.KpDatabase.KeyEncryptionRounds = rounds;
Handler handler = new Handler(); Handler handler = new Handler();
SaveDb save = new SaveDb(Context, App.Kp2a, new AfterSave(Context, handler, oldRounds, this)); SaveDb save = new SaveDb(Context, App.Kp2a, new AfterSave(Context, handler, oldRounds, this));
@ -108,7 +108,8 @@ namespace keepass2android.settings
} }
} else { } else {
DisplayMessage(_ctx); DisplayMessage(_ctx);
_pref.PwDatabase.KeyEncryptionRounds = _oldRounds;
App.Kp2a.GetDb().KpDatabase.KeyEncryptionRounds = _oldRounds;
} }
base.Run(); base.Run();

View File

@ -31,21 +31,15 @@ namespace keepass2android
private static class Timeout private static class Timeout
{ {
private const int RequestId = 0;
private const long DefaultTimeout = 5 * 60 * 1000; // 5 minutes private const long DefaultTimeout = 5 * 60 * 1000; // 5 minutes
private static PendingIntent BuildIntent(Context ctx) private static PendingIntent BuildIntent(Context ctx)
{ {
Intent intent = new Intent(Intents.Timeout); return PendingIntent.GetBroadcast(ctx, 0, new Intent(Intents.LockDatabase), PendingIntentFlags.UpdateCurrent);
PendingIntent sender = PendingIntent.GetBroadcast(ctx, RequestId, intent, PendingIntentFlags.CancelCurrent);
return sender;
} }
public static void Start(Context ctx) public static void Start(Context ctx)
{ {
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(ctx); ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(ctx);
String sTimeout = prefs.GetString(ctx.GetString(Resource.String.app_timeout_key), ctx.GetString(Resource.String.clipboard_timeout_default)); 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; return;
} }
ctx.StartService(new Intent(ctx, typeof(TimeoutService)));
long triggerTime = Java.Lang.JavaSystem.CurrentTimeMillis() + timeout; long triggerTime = Java.Lang.JavaSystem.CurrentTimeMillis() + timeout;
AlarmManager am = (AlarmManager)ctx.GetSystemService(Context.AlarmService); AlarmManager am = (AlarmManager)ctx.GetSystemService(Context.AlarmService);
@ -76,69 +68,25 @@ namespace keepass2android
Kp2aLog.Log("Timeout cancel"); Kp2aLog.Log("Timeout cancel");
am.Cancel(BuildIntent(ctx)); am.Cancel(BuildIntent(ctx));
ctx.StopService(new Intent(ctx, typeof(TimeoutService)));
} }
} }
public static void Pause(Activity act) { public static void Pause(Activity act)
// Record timeout time in case timeout service is killed {
long time = Java.Lang.JavaSystem.CurrentTimeMillis(); if ( App.Kp2a.DatabaseIsUnlocked )
{
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 ) {
Timeout.Start(act); Timeout.Start(act);
} }
} }
public static void Resume(Activity act) { public static void Resume(Activity act)
if ( App.Kp2a.GetDb().Loaded ) { {
if ( App.Kp2a.GetDb().Loaded )
{
Timeout.Cancel(act); 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) static bool IocChanged(IOConnectionInfo ioc, IOConnectionInfo other)
@ -148,11 +96,10 @@ namespace keepass2android
} }
public static bool CheckShutdown(Activity act, IOConnectionInfo ioc) { 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 || (IocChanged(ioc, App.Kp2a.GetDb().Ioc))) //file was changed from ActionSend-Intent
{ {
act.SetResult(KeePass.ExitLock); App.Kp2a.LockDatabase();
act.Finish();
return true; return true;
} }
return false; return false;