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/obj
/src/keepass2android/bin

View File

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

View File

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

View File

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

View File

@ -72,25 +72,6 @@ namespace keepass2android
set { _loaded = value; }
}
public bool Open
{
get { return Loaded && (!Locked); }
}
bool _locked;
public bool Locked
{
get
{
return _locked;
}
set
{
Kp2aLog.Log("Locked=" + _locked);
_locked = value;
}
}
public bool DidOpenFileChange()
{
if (Loaded == false)
@ -102,7 +83,10 @@ namespace keepass2android
}
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, String password, String keyfile, ProgressDialogStatusLogger status)
/// <summary>
/// Do not call this method directly. Call App.Kp2a.LoadDatabase instead.
/// </summary>
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, String password, String keyfile, ProgressDialogStatusLogger status)
{
PwDatabase pwDatabase = new PwDatabase();
@ -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;
}

View File

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

View File

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

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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

View File

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

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/>.
*/
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;
}
}
}
}
}

View File

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

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/>.
*/
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;
}
}
}
}
}

View File

@ -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<MemoryStream> _loadDbTask;
private IOConnectionInfo _ioConnection;
private String _keyFile;
private bool _rememberKeyfile;
ISharedPreferences _prefs;
private bool _started;
public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
@ -123,114 +130,28 @@ namespace keepass2android
public void LaunchNextActivity()
{
AppTask.AfterUnlockDatabase(this);
}
void UnloadDatabase()
{
App.Kp2a.GetDb().Clear();
StopService(new Intent(this, typeof(QuickUnlockForegroundService)));
}
void LockDatabase()
{
SetResult(KeePass.ExitLock);
SetEditText(Resource.Id.password, "");
if (App.Kp2a.GetDb().QuickUnlockEnabled)
App.Kp2a.GetDb().Locked = true;
else
{
UnloadDatabase();
}
}
void LockAndClose()
{
LockDatabase();
Finish();
}
bool TryStartQuickUnlock()
{
if (!App.Kp2a.GetDb().QuickUnlockEnabled)
return false;
if (App.Kp2a.GetDb().KpDatabase.MasterKey.ContainsType(typeof(KcpPassword)) == false)
return false;
KcpPassword kcpPassword = (KcpPassword)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpPassword));
String password = kcpPassword.Password.ReadString();
if (password.Length == 0)
return false;
App.Kp2a.GetDb().Locked = true;
Intent i = new Intent(this, typeof(QuickUnlock));
PutIoConnectionToIntent(_ioConnection, i);
Kp2aLog.Log("Starting QuickUnlock");
StartActivityForResult(i,0);
return true;
}
public void StartQuickUnlockForegroundService()
{
if (App.Kp2a.GetDb().QuickUnlockEnabled)
{
StartService(new Intent(this, typeof(QuickUnlockForegroundService)));
}
}
bool _startedWithActivityResult;
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
_startedWithActivityResult = true;
Kp2aLog.Log("PasswordActivity.OnActivityResult "+resultCode+"/"+requestCode);
if (resultCode != KeePass.ExitCloseAfterTaskComplete)
{
//Stop service when app activity is left
StopService(new Intent(this, typeof(CopyToClipboardService)));
}
//NOTE: original code from k eepassdroid used switch ((Android.App.Result)requestCode) { (but doesn't work here, although k eepassdroid works)
switch(resultCode) {
case KeePass.ExitNormal:
if (!TryStartQuickUnlock())
{
SetEditText(Resource.Id.password, "");
}
case KeePass.ExitNormal: // Returned to this screen using the Back key, treat as locking the database
App.Kp2a.LockDatabase();
break;
case KeePass.ExitLock:
if (!TryStartQuickUnlock())
{
LockAndClose();
}
break;
case KeePass.ExitForceLock:
SetEditText(Resource.Id.password, "");
UnloadDatabase();
break;
case KeePass.ExitForceLockAndChangeDb:
UnloadDatabase();
Finish();
break;
case KeePass.ExitChangeDb:
LockAndClose();
// The database has already been locked, and the quick unlock screen will be shown if appropriate
break;
case KeePass.ExitCloseAfterTaskComplete:
// Do not lock the database
SetResult(KeePass.ExitCloseAfterTaskComplete);
Finish();
break;
case KeePass.ExitQuickUnlock:
App.Kp2a.GetDb().Locked = false;
LaunchNextActivity();
break;
case KeePass.ExitReloadDb:
//if the activity was killed, fill password/keyfile so the user can directly hit load again
if (App.Kp2a.GetDb().Loaded)
@ -252,9 +173,9 @@ namespace keepass2android
SetEditText(Resource.Id.pass_keyfile, kcpKeyfile.Path);
}
}
UnloadDatabase();
App.Kp2a.LockDatabase(false);
break;
case Result.Ok:
case Result.Ok: // Key file browse dialog OK'ed.
if (requestCode == Intents.RequestCodeFileBrowseForKeyfile) {
string filename = Util.IntentToFilename(data);
if (filename != null) {
@ -331,6 +252,13 @@ namespace keepass2android
}
}
if (App.Kp2a.GetDb().Loaded && App.Kp2a.GetDb().Ioc != null &&
App.Kp2a.GetDb().Ioc.GetDisplayName() != _ioConnection.GetDisplayName())
{
// A different database is currently loaded, unload it before loading the new one requested
App.Kp2a.LockDatabase(false);
}
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
SetContentView(Resource.Layout.password);
@ -343,8 +271,7 @@ namespace keepass2android
Window.SetSoftInputMode(SoftInput.StateVisible);
Button confirmButton = (Button)FindViewById(Resource.Id.pass_ok);
confirmButton.Click += (sender, e) =>
{
confirmButton.Click += (sender, e) => {
String pass = GetEditText(Resource.Id.password);
String key = GetEditText(Resource.Id.pass_keyfile);
if (pass.Length == 0 && key.Length == 0)
@ -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<MemoryStream>(LoadDbFile);
}
}
}
private void RetrieveSettings() {
String defaultFilename = _prefs.GetString(KeyDefaultFilename, "");
if (!String.IsNullOrEmpty(_ioConnection.Path) && _ioConnection.Path.Equals(defaultFilename)) {
CheckBox checkbox = (CheckBox) FindViewById(Resource.Id.default_database);
checkbox.Checked = true;
}
CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock);
cbQuickUnlock.Checked = _prefs.GetBoolean(GetString(Resource.String.QuickUnlockDefaultEnabled_key), true);
}
@ -540,31 +491,50 @@ namespace keepass2android
public override bool OnOptionsItemSelected(IMenuItem item) {
switch ( item.ItemId ) {
case Resource.Id.menu_about:
AboutDialog dialog = new AboutDialog(this);
dialog.Show();
return true;
case Resource.Id.menu_about:
AboutDialog dialog = new AboutDialog(this);
dialog.Show();
return true;
case Resource.Id.menu_app_settings:
AppSettingsActivity.Launch(this);
return true;
case Resource.Id.menu_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);
}
}

View File

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

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

View File

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

View File

@ -20,6 +20,11 @@
android:title="@string/menu_about"
android:icon="@android:drawable/ic_menu_help"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/menu_change_db"
android:icon="@drawable/collections_collection"
android:title="@string/menu_change_db"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/menu_app_settings"
android:title="@string/menu_app_settings"

View File

@ -19,6 +19,10 @@
<item android:id="@+id/menu_app_settings"
android:icon="@android:drawable/ic_menu_preferences"
android:title="@string/menu_app_settings"
/>
<item android:id="@+id/menu_change_db"
android:icon="@drawable/collections_collection"
android:title="@string/menu_change_db"
/>
<item android:id="@+id/menu_about"
android:icon="@android:drawable/ic_menu_help"

View File

@ -39,7 +39,6 @@
<string name="omitbackup_key">omitbackup</string>
<string name="list_size_key">list_size</string>
<string name="sort_key">sort_key</string>
<string name="timeout_key">timeout_key</string>
<string name="TanExpiresOnUse_key">TanExpiresOnUse_key</string>
<string name="ShowUsernameInList_key">ShowUsernameInList_key</string>
<string name="RememberRecentFiles_key">RememberRecentFiles_key</string>
@ -92,4 +91,11 @@
<item>20</item>
<item>28</item>
</string-array>
<string name="ShowUnlockedNotification_key">ShowUnlockedNotification</string>
<bool name="ShowUnlockedNotification_default">true</bool>
<string name="PreloadDatabaseEnabled_key">PreloadDatabaseEnabled</string>
<bool name="PreloadDatabaseEnabled_default">true</bool>
</resources>

View File

@ -131,6 +131,7 @@
<string name="menu_search">Search</string>
<string name="menu_search_advanced">Advanced Search</string>
<string name="menu_url">Go to URL</string>
<string name="menu_change_db">Change database…</string>
<string name="minus">Minus</string>
<string name="never">Never</string>
<string name="yes">Yes</string>
@ -218,7 +219,8 @@
<string name="add_binary">Add file attachment...</string>
<string name="add_extra_string">Add additional string</string>
<string name="delete_extra_string">Delete additional string</string>
<string name="database_loaded_quickunlock_enabled">Database loaded, QuickUnlock enabled.</string>
<string name="database_loaded_quickunlock_enabled">%1$s: Locked. QuickUnlock enabled.</string>
<string name="database_loaded_unlocked">%1$s: Unlocked.</string>
<string name="credentials_dialog_title">Enter server credentials</string>
<string name="UseFileTransactions_title">File transactions</string>
<string name="UseFileTransactions_summary">Use file transactions for writing databases</string>
@ -231,8 +233,14 @@
<string name="ShowKp2aKeyboardNotification_summary">Make full entry accessible through the KP2A keyboard (recommended).</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="AskOverwriteBinary">Do you want to overwrite the existing binary with the same name?</string>
<string name="ShowUnlockedNotification_title">Notification while unlocked</string>
<string name="ShowUnlockedNotification_summary">Show an ongoing notification while the database is unlocked.</string>
<string name="PreloadDatabaseEnabled_title">Pre-load database file</string>
<string name="PreloadDatabaseEnabled_summary">Start background loading or downloading of the database file during password entry.</string>
<string name="AskOverwriteBinary">Do you want to overwrite the existing binary with the same name?</string>
<string name="AskOverwriteBinary_title">Overwrite existing binary?</string>
<string name="AskOverwriteBinary_yes">Overwrite</string>
<string name="AskOverwriteBinary_no">Rename</string>

View File

@ -71,6 +71,13 @@
android:entryValues="@array/clipboard_timeout_values"
android:dialogTitle="@string/app_timeout"
android:defaultValue="@string/clipboard_timeout_default"/>
<CheckBoxPreference
android:enabled="true"
android:persistent="true"
android:summary="@string/ShowUnlockedNotification_summary"
android:defaultValue="@bool/ShowUnlockedNotification_default"
android:title="@string/ShowUnlockedNotification_title"
android:key="@string/ShowUnlockedNotification_key" />
<CheckBoxPreference
android:key="@string/maskpass_key"
android:title="@string/maskpass_title"
@ -162,6 +169,12 @@
android:defaultValue="true"
android:title="@string/CheckForFileChangesOnSave_title"
android:key="@string/CheckForFileChangesOnSave_key" />
<CheckBoxPreference
android:enabled="true"
android:persistent="true"
android:summary="@string/PreloadDatabaseEnabled_summary"
android:defaultValue="@bool/PreloadDatabaseEnabled_default"
android:title="@string/PreloadDatabaseEnabled_title"
android:key="@string/PreloadDatabaseEnabled_key" />
</PreferenceScreen>
</PreferenceScreen>

View File

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

View File

@ -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
/// </summary>
public class Kp2aApp: IKp2aApp, ICacheSupervisor
{
public bool IsShutdown()
{
return _shutdown;
public void LockDatabase(bool allowQuickUnlock = true)
{
if (GetDb().Loaded)
{
if (QuickUnlockEnabled && allowQuickUnlock &&
_db.KpDatabase.MasterKey.ContainsType(typeof(KcpPassword)) &&
!((KcpPassword)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpPassword))).Password.IsEmpty)
{
if (!QuickLocked)
{
Kp2aLog.Log("QuickLocking database");
QuickLocked = true;
}
else
{
Kp2aLog.Log("Database already QuickLocked");
}
}
else
{
Kp2aLog.Log("Locking database");
// Couldn't quick-lock, so unload database instead
_db.Clear();
QuickLocked = false;
}
}
else
{
Kp2aLog.Log("Database not loaded, couldn't lock");
}
UpdateOngoingNotification();
Application.Context.SendBroadcast(new Intent(Intents.DatabaseLocked));
}
public void SetShutdown()
{
Kp2aLog.Log("set shutdown");
_shutdown = true;
}
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string password, string keyFile, ProgressDialogStatusLogger statusLogger)
{
_db.LoadData(this, ioConnectionInfo, memoryStream, password, keyFile, statusLogger);
public void ClearShutdown()
{
Kp2aLog.Log("clear shutdown");
_shutdown = false;
}
UpdateOngoingNotification();
}
private Database _db;
private bool _shutdown;
internal void UnlockDatabase()
{
QuickLocked = false;
UpdateOngoingNotification();
}
private void UpdateOngoingNotification()
{
// Start or update the notification icon service to reflect the current state
var ctx = Application.Context;
ctx.StartService(new Intent(ctx, typeof(OngoingNotificationsService)));
}
public bool DatabaseIsUnlocked
{
get { return _db.Loaded && !QuickLocked; }
}
#region QuickUnlock
public void SetQuickUnlockEnabled(bool enabled)
{
if (enabled)
{
//Set KeyLength of QuickUnlock at time of enabling.
//This is important to not allow an attacker to set the length to 1 when QuickUnlock is started already.
var ctx = Application.Context;
var prefs = PreferenceManager.GetDefaultSharedPreferences(ctx);
QuickUnlockKeyLength = Math.Max(1, int.Parse(prefs.GetString(ctx.GetString(Resource.String.QuickUnlockLength_key), ctx.GetString(Resource.String.QuickUnlockLength_default))));
}
QuickUnlockEnabled = enabled;
}
public bool QuickUnlockEnabled { get; private set; }
public int QuickUnlockKeyLength { get; private set; }
/// <summary>
/// If true, the database must be regarded as locked and not exposed to the user.
/// </summary>
public bool QuickLocked { get; private set; }
#endregion
private Database _db;
/// <summary>
/// See comments to EntryEditActivityState.
/// </summary>
@ -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)

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;
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);

View File

@ -22,9 +22,14 @@ namespace keepass2android
/// <summary>
/// Contains constants to be used in intents
/// </summary>
public class Intents {
public const String Timeout = "keepass2android.timeout";
public class Intents
{
/// <summary>Broadcast this intent to lock the database</summary>
public const String LockDatabase = "keepass2android.lock_database";
/// <summary>This intent will be broadcast once the database has been locked. Sensitive information displayed should be hidden and unloaded.</summary>
public const String DatabaseLocked = "keepass2android.database_locked";
public const String CopyUsername = "keepass2android.copy_username";
public const String CopyPassword = "keepass2android.copy_password";
public const String CheckKeyboard = "keepass2android.check_keyboard";

View File

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

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)
{
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))
{

View File

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

View File

@ -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
{

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.Globalization;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Widget;
using Android.Preferences;
using KeePassLib.Cryptography.Cipher;
namespace keepass2android
{
/// <summary>
/// Activity to configure the app
/// Activity to configure the application, without database settings. Does not require an unlocked database, or close when the database is locked
/// </summary>
[Activity (Label = "@string/app_name", Theme="@style/NoTitleBar")]
public class AppSettingsActivity : LockingClosePreferenceActivity {
public static bool KeyfileDefault = false;
public static void Launch(Context ctx) {
Intent i = new Intent(ctx, typeof(AppSettingsActivity));
ctx.StartActivity(i);
public class AppSettingsActivity : LockingPreferenceActivity
{
public static void Launch(Context ctx)
{
ctx.StartActivity(new Intent(ctx, typeof(AppSettingsActivity)));
}
protected override void OnCreate(Bundle savedInstanceState) {
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
AddPreferencesFromResource(Resource.Xml.preferences);
Preference keyFile = FindPreference(GetString(Resource.String.keyfile_key));
keyFile.PreferenceChange += (sender, e) =>
FindPreference(GetString(Resource.String.keyfile_key)).PreferenceChange += OnRememberKeyFileHistoryChanged;
FindPreference(GetString(Resource.String.ShowUnlockedNotification_key)).PreferenceChange += OnShowUnlockedNotificationChanged;;
FindPreference(GetString(Resource.String.db_key)).Enabled = false;
}
internal static void OnRememberKeyFileHistoryChanged(object sender, Preference.PreferenceChangeEventArgs eventArgs)
{
if (!(bool)eventArgs.NewValue)
{
bool value = (bool) e.NewValue;
if ( ! value ) {
FileDbHelper helper = App.Kp2a.FileDbHelper;
helper.DeleteAllKeys();
}
};
Database db = App.Kp2a.GetDb();
if ( db.Open ) {
Preference rounds = FindPreference(GetString(Resource.String.rounds_key));
rounds.PreferenceChange += (sender, e) =>
{
setRounds(App.Kp2a.GetDb(), e.Preference);
};
Preference defaultUser = FindPreference(GetString(Resource.String.default_username_key));
((EditTextPreference)defaultUser).EditText.Text = db.KpDatabase.DefaultUserName;
((EditTextPreference)defaultUser).Text = db.KpDatabase.DefaultUserName;
defaultUser.PreferenceChange += (sender, e) =>
{
DateTime previousUsernameChanged = db.KpDatabase.DefaultUserNameChanged;
String previousUsername = db.KpDatabase.DefaultUserName;
db.KpDatabase.DefaultUserName = e.NewValue.ToString();
SaveDb save = new SaveDb(this, App.Kp2a, new ActionOnFinish( (success, message) =>
{
if (!success)
{
db.KpDatabase.DefaultUserName = previousUsername;
db.KpDatabase.DefaultUserNameChanged = previousUsernameChanged;
Toast.MakeText(this, message, ToastLength.Long).Show();
}
}));
ProgressTask pt = new ProgressTask(App.Kp2a, this, save);
pt.Run();
};
Preference databaseName = FindPreference(GetString(Resource.String.database_name_key));
((EditTextPreference)databaseName).EditText.Text = db.KpDatabase.Name;
((EditTextPreference)databaseName).Text = db.KpDatabase.Name;
databaseName.PreferenceChange += (sender, e) =>
{
DateTime previousNameChanged = db.KpDatabase.NameChanged;
String previousName = db.KpDatabase.Name;
db.KpDatabase.Name = e.NewValue.ToString();
SaveDb save = new SaveDb(this, App.Kp2a, new ActionOnFinish( (success, message) =>
{
if (!success)
{
db.KpDatabase.Name = previousName;
db.KpDatabase.NameChanged = previousNameChanged;
Toast.MakeText(this, message, ToastLength.Long).Show();
}
}));
ProgressTask pt = new ProgressTask(App.Kp2a, this, save);
pt.Run();
};
setRounds(db, rounds);
Preference algorithm = FindPreference(GetString(Resource.String.algorithm_key));
setAlgorithm(db, algorithm);
} else {
Preference dbSettings = FindPreference(GetString(Resource.String.db_key));
dbSettings.Enabled = false;
App.Kp2a.FileDbHelper.DeleteAllKeys();
}
}
private void setRounds(Database db, Preference rounds) {
rounds.Summary = db.KpDatabase.KeyEncryptionRounds.ToString(CultureInfo.InvariantCulture);
internal static void OnShowUnlockedNotificationChanged(object sender, Preference.PreferenceChangeEventArgs eventArgs)
{
var ctx = ((Preference)sender).Context;
ctx.StartService(new Intent(ctx, typeof(OngoingNotificationsService)));
}
private void setAlgorithm(Database db, Preference algorithm) {
algorithm.Summary = CipherPool.GlobalPool.GetCipher(db.KpDatabase.DataCipherUuid).DisplayName;
}
}
}

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

View File

@ -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;