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