Drastically improved UI for creating a new database (own activity, file browser for key files, explanation texts, password generator, show password button)

Added DejaVuSansMono.ttf for password display in EntryActivity
This commit is contained in:
Philipp Crocoll 2013-10-12 22:20:02 +02:00
parent 2f3bbff841
commit c81ca4268e
29 changed files with 3470 additions and 2433 deletions

View File

@ -30,13 +30,24 @@ namespace keepass2android
private readonly bool _dontSave;
private readonly Context _ctx;
private readonly IKp2aApp _app;
private CompositeKey _key;
public CreateDb(IKp2aApp app, Context ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave): base(finish) {
_ctx = ctx;
_ioc = ioc;
_dontSave = dontSave;
_app = app;
}
public CreateDb(IKp2aApp app, Context ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key)
: base(finish)
{
_ctx = ctx;
_ioc = ioc;
_dontSave = dontSave;
_app = app;
_key = key;
}
public override void Run() {
@ -44,10 +55,13 @@ namespace keepass2android
Database db = _app.CreateNewDatabase();
db.KpDatabase = new KeePassLib.PwDatabase();
//Key will be changed/created immediately after creation:
CompositeKey tempKey = new CompositeKey();
db.KpDatabase.New(_ioc, tempKey);
if (_key == null)
{
_key = new CompositeKey(); //use a temporary key which should be changed after creation
}
db.KpDatabase.New(_ioc, _key);
db.KpDatabase.KeyEncryptionRounds = DefaultEncryptionRounds;
db.KpDatabase.Name = "Keepass2Android Password Database";

Binary file not shown.

View File

@ -0,0 +1,99 @@
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
Bitstream Vera Fonts Copyright
------------------------------
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute the
Font Software, including without limitation the rights to use, copy, merge,
publish, distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to the
following conditions:
The above copyright and trademark notices and this permission notice shall
be included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional glyphs or characters may be added to the Fonts, only if the fonts
are renamed to names not containing either the words "Bitstream" or the word
"Vera".
This License becomes null and void to the extent applicable to Fonts or Font
Software that has been modified and is distributed under the "Bitstream
Vera" names.
The Font Software may be sold as part of a larger software package but no
copy of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
FONT SOFTWARE.
Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font Software
without prior written authorization from the Gnome Foundation or Bitstream
Inc., respectively. For further information, contact: fonts at gnome dot
org.
Arev Fonts Copyright
------------------------------
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the fonts accompanying this license ("Fonts") and
associated documentation files (the "Font Software"), to reproduce
and distribute the modifications to the Bitstream Vera Font Software,
including without limitation the rights to use, copy, merge, publish,
distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to
the following conditions:
The above copyright and trademark notices and this permission notice
shall be included in all copies of one or more of the Font Software
typefaces.
The Font Software may be modified, altered, or added to, and in
particular the designs of glyphs or characters in the Fonts may be
modified and additional glyphs or characters may be added to the
Fonts, only if the fonts are renamed to names not containing either
the words "Tavmjong Bah" or the word "Arev".
This License becomes null and void to the extent applicable to Fonts
or Font Software that has been modified and is distributed under the
"Tavmjong Bah Arev" names.
The Font Software may be sold as part of a larger software package but
no copy of one or more of the Font Software typefaces may be sold by
itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the name of Tavmjong Bah shall not
be used in advertising or otherwise to promote the sale, use or other
dealings in this Font Software without prior written authorization
from Tavmjong Bah. For further information, contact: tavmjong @ free
. fr.
$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $

Binary file not shown.

View File

@ -0,0 +1,490 @@
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Preferences;
using Android.Text;
using Android.Views;
using Android.Widget;
using Java.IO;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using keepass2android.Io;
using Environment = Android.OS.Environment;
using IOException = Java.IO.IOException;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/Base")]
public class CreateDatabaseActivity : Activity
{
private IOConnectionInfo _ioc;
private string _keyfileFilename;
private bool _restoringInstanceState;
private bool _showPassword;
private const int RequestCodeKeyFile = 0;
private const int RequestCodeDbFilename = 1;
private const string KeyfilefilenameBundleKey = "KeyfileFilename";
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutString(PasswordActivity.KeyFilename, _ioc.Path);
outState.PutString(PasswordActivity.KeyServerusername, _ioc.UserName);
outState.PutString(PasswordActivity.KeyServerpassword, _ioc.Password);
outState.PutInt(PasswordActivity.KeyServercredmode, (int)_ioc.CredSaveMode);
if (_keyfileFilename != null)
outState.PutString(KeyfilefilenameBundleKey, _keyfileFilename);
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.create_database);
SetDefaultIoc();
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Gone;
var keyfileCheckbox = FindViewById<CheckBox>(Resource.Id.use_keyfile);
if (bundle != null)
{
_keyfileFilename = bundle.GetString(KeyfilefilenameBundleKey, null);
if (_keyfileFilename != null)
{
FindViewById<TextView>(Resource.Id.keyfile_filename).Text = ConvertFilenameToIocPath(_keyfileFilename);
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Visible;
keyfileCheckbox.Checked = true;
}
if (bundle.GetString(PasswordActivity.KeyFilename, null) != null)
{
_ioc = new IOConnectionInfo
{
Path = bundle.GetString(PasswordActivity.KeyFilename),
UserName = bundle.GetString(PasswordActivity.KeyServerusername),
Password = bundle.GetString(PasswordActivity.KeyServerpassword),
CredSaveMode = (IOCredSaveMode) bundle.GetInt(PasswordActivity.KeyServercredmode),
};
}
}
UpdateIocView();
keyfileCheckbox.CheckedChange += (sender, args) =>
{
if (keyfileCheckbox.Checked)
{
if (_restoringInstanceState)
return;
string defaulFilename = _keyfileFilename;
if (_keyfileFilename == null)
{
defaulFilename = _keyfileFilename = SdDir + "keepass/keyfile.txt";
if (defaulFilename.StartsWith("file://") == false)
defaulFilename = "file://" + defaulFilename;
}
StartFileChooser(defaulFilename, RequestCodeKeyFile, false);
}
else
{
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Gone;
_keyfileFilename = null;
}
};
FindViewById(Resource.Id.btn_change_location).Click += (sender, args) =>
{
Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
StartActivityForResult(intent, 0);
};
Button generatePassword = (Button)FindViewById(Resource.Id.generate_button);
generatePassword.Click += (sender, e) =>
{
GeneratePasswordActivity.LaunchWithoutLockCheck(this);
};
FindViewById(Resource.Id.btn_create).Click += (sender, evt) =>
{
CreateDatabase();
};
ImageButton btnTogglePassword = (ImageButton)FindViewById(Resource.Id.toggle_password);
btnTogglePassword.Click += (sender, e) =>
{
_showPassword = !_showPassword;
MakePasswordMaskedOrVisible();
};
}
private void MakePasswordMaskedOrVisible()
{
TextView password = (TextView)FindViewById(Resource.Id.entry_password);
TextView confpassword = (TextView)FindViewById(Resource.Id.entry_confpassword);
if (_showPassword)
{
password.InputType = InputTypes.ClassText | InputTypes.TextVariationVisiblePassword;
confpassword.Visibility = ViewStates.Gone;
}
else
{
password.InputType = InputTypes.ClassText | InputTypes.TextVariationPassword;
confpassword.Visibility = ViewStates.Visible;
}
}
private void CreateDatabase()
{
var keyfileCheckbox = FindViewById<CheckBox>(Resource.Id.use_keyfile);
string password;
if (!TryGetPassword(out password)) return;
// Verify that a password or keyfile is set
if (password.Length == 0 && !keyfileCheckbox.Checked)
{
Toast.MakeText(this, Resource.String.error_nopass, ToastLength.Long).Show();
return;
}
//create the key
CompositeKey newKey = new CompositeKey();
if (String.IsNullOrEmpty(password) == false)
{
newKey.AddUserKey(new KcpPassword(password));
}
if (String.IsNullOrEmpty(_keyfileFilename) == false)
{
try
{
newKey.AddUserKey(new KcpKeyFile(_keyfileFilename));
}
catch (Exception)
{
Toast.MakeText(this, Resource.String.error_adding_keyfile, ToastLength.Long).Show();
return;
}
}
// Create the new database
CreateDb create = new CreateDb(App.Kp2a, this, _ioc, new LaunchGroupActivity(_ioc, this), false, newKey);
ProgressTask createTask = new ProgressTask(
App.Kp2a,
this, create);
createTask.Run();
}
private bool TryGetPassword(out string pass)
{
TextView passView = (TextView)FindViewById(Resource.Id.entry_password);
pass = passView.Text;
if (_showPassword)
return true;
TextView passConfView = (TextView) FindViewById(Resource.Id.entry_confpassword);
String confpass = passConfView.Text;
// Verify that passwords match
if (! pass.Equals(confpass))
{
// Passwords do not match
Toast.MakeText(this, Resource.String.error_pass_match, ToastLength.Long).Show();
return false;
}
return true;
}
protected override void OnRestoreInstanceState(Bundle savedInstanceState)
{
_restoringInstanceState = true;
base.OnRestoreInstanceState(savedInstanceState);
_restoringInstanceState = false;
}
private void StartFileChooser(string defaultPath, int requestCode, bool forSave)
{
#if !EXCLUDE_FILECHOOSER
Kp2aLog.Log("FSA: defaultPath=" + defaultPath);
string fileProviderAuthority = FileChooserFileProvider.TheAuthority;
if (defaultPath.StartsWith("file://"))
{
fileProviderAuthority = "keepass2android.keepass2android.android-filechooser.localfile";
}
Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority,
defaultPath);
if (forSave)
i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true);
StartActivityForResult(i, requestCode);
#endif
}
private void UpdateIocView()
{
int protocolSeparatorPos = _ioc.Path.IndexOf("://", StringComparison.Ordinal);
string protocolId = protocolSeparatorPos < 0 ?
"file" : _ioc.Path.Substring(0, protocolSeparatorPos);
Drawable drawable = App.Kp2a.GetResourceDrawable("ic_storage_" + protocolId);
FindViewById<ImageView>(Resource.Id.filestorage_logo).SetImageDrawable(drawable);
String title = App.Kp2a.GetResourceString("filestoragename_" + protocolId);
FindViewById<TextView>(Resource.Id.filestorage_label).Text = title;
FindViewById<TextView>(Resource.Id.label_filename).Text = protocolSeparatorPos < 0 ?
_ioc.Path :
_ioc.Path.Substring(protocolSeparatorPos + 3);
}
private void SetDefaultIoc()
{
var sdDir = SdDir;
string filename = sdDir + "keepass/keepass.kdbx";
filename = ConvertFilenameToIocPath(filename);
int count = 2;
while (new File(filename).Exists())
{
filename = ConvertFilenameToIocPath(sdDir + "keepass/keepass" + count + ".kdbx");
count++;
}
_ioc = new IOConnectionInfo()
{
Path = filename
};
}
private static string SdDir
{
get
{
string sdDir = Environment.ExternalStorageDirectory.AbsolutePath;
if (!sdDir.EndsWith("/"))
sdDir += "/";
if (!sdDir.StartsWith("file://"))
sdDir = "file://" + sdDir;
return sdDir;
}
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (resultCode == KeePass.ResultOkPasswordGenerator)
{
String generatedPassword = data.GetStringExtra("keepass2android.password.generated_password");
FindViewById<TextView>(Resource.Id.entry_password).Text = generatedPassword;
FindViewById<TextView>(Resource.Id.entry_confpassword).Text = generatedPassword;
}
if (resultCode == KeePass.ExitFileStorageSelectionOk)
{
string protocolId = data.GetStringExtra("protocolId");
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this,
OnActivityResult,
defaultPath =>
{
Util.ShowFilenameDialog(this, OnCreateButton, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
Intents.RequestCodeFileBrowseForOpen);
}
), true, RequestCodeDbFilename, protocolId);
}
if (resultCode == Result.Ok)
{
if (requestCode == RequestCodeKeyFile)
{
string filename = Util.IntentToFilename(data, this);
if (filename != null)
{
_keyfileFilename = ConvertFilenameToIocPath(filename);
FindViewById<TextView>(Resource.Id.keyfile_filename).Text = _keyfileFilename;
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Visible;
}
}
if (requestCode == RequestCodeDbFilename)
{
string filename = Util.IntentToFilename(data, this);
filename = ConvertFilenameToIocPath(filename);
if (filename != null)
{
_ioc = new IOConnectionInfo() {Path = filename};
UpdateIocView();
}
}
}
if (resultCode == (Result)FileStorageResults.FileUsagePrepared)
{
_ioc = new IOConnectionInfo();
PasswordActivity.SetIoConnectionFromIntent(_ioc, data);
UpdateIocView();
}
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
{
IOConnectionInfo ioc = new IOConnectionInfo();
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
StartFileChooser(ioc.Path, RequestCodeDbFilename, true);
}
}
private static string ConvertFilenameToIocPath(string filename)
{
if (filename.StartsWith("file://"))
{
filename = filename.Substring(7);
}
filename = Java.Net.URLDecoder.Decode(filename);
return filename;
}
private void OnCreateButton(object sender, EventArgs evt)
{
Dialog dialog = (Dialog)sender;
String filename = ((EditText)dialog.FindViewById(Resource.Id.file_filename)).Text;
// Make sure file name exists
if (filename.Length == 0)
{
Toast.MakeText(this,
Resource.String.error_filename_required,
ToastLength.Long).Show();
return;
}
IOConnectionInfo ioc = new IOConnectionInfo() { Path = filename };
try
{
App.Kp2a.GetFileStorage(ioc);
}
catch (NoFileStorageFoundException)
{
Toast.MakeText(this,
"Unexpected scheme in "+filename,
ToastLength.Long).Show();
return;
}
if (ioc.IsLocalFile())
{
// Try to create the file
Java.IO.File file = new Java.IO.File(filename);
try
{
Java.IO.File parent = file.ParentFile;
if (parent == null || (parent.Exists() && !parent.IsDirectory))
{
Toast.MakeText(this,
Resource.String.error_invalid_path,
ToastLength.Long).Show();
return;
}
if (!parent.Exists())
{
// Create parent dircetory
if (!parent.Mkdirs())
{
Toast.MakeText(this,
Resource.String.error_could_not_create_parent,
ToastLength.Long).Show();
return;
}
}
System.IO.File.Create(filename);
}
catch (IOException ex)
{
Toast.MakeText(
this,
GetText(Resource.String.error_file_not_create) + " "
+ ex.LocalizedMessage,
ToastLength.Long).Show();
return;
}
_ioc = ioc;
UpdateIocView();
}
}
private class LaunchGroupActivity : FileOnFinish
{
readonly CreateDatabaseActivity _activity;
private readonly IOConnectionInfo _ioc;
public LaunchGroupActivity(IOConnectionInfo ioc, CreateDatabaseActivity activity)
: base(null)
{
_activity = activity;
_ioc = ioc;
}
public override void Run()
{
if (Success)
{
// Update the ongoing notification
_activity.StartService(new Intent(_activity, typeof(OngoingNotificationsService)));
if (PreferenceManager.GetDefaultSharedPreferences(_activity).GetBoolean(_activity.GetString(Resource.String.RememberRecentFiles_key), _activity.Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default)))
{
// Add to recent files
FileDbHelper dbHelper = App.Kp2a.FileDbHelper;
//TODO: getFilename always returns "" -> bug?
dbHelper.CreateFile(_ioc, Filename);
}
GroupActivity.Launch(_activity, new NullTask());
_activity.Finish();
}
else
{
DisplayMessage(_activity);
try
{
App.Kp2a.GetFileStorage(_ioc).Delete(_ioc);
}
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
throw;
}
}
}
}
}
}

View File

@ -45,6 +45,7 @@ namespace keepass2android
public const String KeyRefreshPos = "refresh_pos";
public const String KeyCloseAfterCreate = "close_after_create";
private static Typeface _passwordFont;
public static void Launch(Activity act, PwEntry pw, int pos, AppTask appTask) {
Intent i = new Intent(act, typeof(EntryActivity));
@ -205,7 +206,7 @@ namespace keepass2android
TextView valueView = (TextView)LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
if (value != null)
valueView.Text = value;
valueView.Typeface = Typeface.Monospace;
SetPasswordTypeface(valueView);
if (isProtected)
RegisterProtectedTextView(valueView);
@ -399,6 +400,8 @@ namespace keepass2android
PopulateText(Resource.Id.entry_url, Resource.Id.entry_url_label, Entry.Strings.ReadSafe(PwDefs.UrlField));
PopulateText(Resource.Id.entry_password, Resource.Id.entry_password_label, Entry.Strings.ReadSafe(PwDefs.PasswordField));
RegisterProtectedTextView(FindViewById<TextView>(Resource.Id.entry_password));
SetPasswordTypeface(FindViewById<TextView>(Resource.Id.entry_password));
PopulateText(Resource.Id.entry_created, Resource.Id.entry_created_label, getDateTime(Entry.CreationTime));
PopulateText(Resource.Id.entry_modified, Resource.Id.entry_modified_label, getDateTime(Entry.LastModificationTime));
@ -423,7 +426,14 @@ namespace keepass2android
SetPasswordStyle();
}
private void SetPasswordTypeface(TextView textView)
{
if (_passwordFont == null)
_passwordFont = Typeface.CreateFromAsset(Assets, "DejaVuSansMono.ttf" );
textView.Typeface = _passwordFont;
}
private void PopulateText(int viewId, int headerViewId,int resId) {
View header = FindViewById(headerViewId);
TextView tv = (TextView)FindViewById(viewId);

View File

@ -40,7 +40,7 @@ namespace keepass2android
public const String KeyParent = "parent";
public const int ResultOkIconPicker = (int)Result.FirstUser + 1000;
public const int ResultOkPasswordGenerator = ResultOkIconPicker + 1;
const string IntentContinueWithEditing = "ContinueWithEditing";
@ -594,9 +594,9 @@ namespace keepass2android
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
switch ((int)resultCode)
switch (resultCode)
{
case ResultOkIconPicker:
case (Result)ResultOkIconPicker:
State.SelectedIconId = (PwIcon) data.Extras.GetInt(IconPickerActivity.KeyIconId);
State.SelectedCustomIconId = PwUuid.Zero;
String customIconIdString = data.Extras.GetString(IconPickerActivity.KeyCustomIconId);
@ -607,7 +607,7 @@ namespace keepass2android
Reload();
break;
case ResultOkPasswordGenerator:
case KeePass.ResultOkPasswordGenerator:
String generatedPassword = data.GetStringExtra("keepass2android.password.generated_password");
byte[] password = StrUtil.Utf8.GetBytes(generatedPassword);
@ -618,7 +618,7 @@ namespace keepass2android
State.EntryModified = true;
Reload();
break;
case (int)Result.Ok:
case Result.Ok:
if (requestCode == Intents.RequestCodeFileBrowseForBinary)
{
string filename = Util.IntentToFilename(data, this);
@ -635,7 +635,7 @@ namespace keepass2android
break;
case (int)Result.Canceled:
case Result.Canceled:
Reload();
break;
}

View File

@ -34,6 +34,15 @@ namespace keepass2android
act.StartActivityForResult(i, 0);
}
public static void LaunchWithoutLockCheck(Activity act)
{
Intent i = new Intent(act, typeof(GeneratePasswordActivity));
i.PutExtra(NoLockCheck, true);
act.StartActivityForResult(i, 0);
}
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
@ -69,7 +78,7 @@ namespace keepass2android
Intent intent = new Intent();
intent.PutExtra("keepass2android.password.generated_password", password.Text);
SetResult((Result)EntryEditActivity.ResultOkPasswordGenerator, intent);
SetResult(KeePass.ResultOkPasswordGenerator, intent);
Finish();
};

View File

@ -43,6 +43,7 @@ namespace keepass2android
public const Result ExitReloadDb = Result.FirstUser+6;
public const Result ExitClose = Result.FirstUser + 7;
public const Result ExitFileStorageSelectionOk = Result.FirstUser + 8;
public const Result ResultOkPasswordGenerator = Result.FirstUser + 9;
AppTask _appTask;

View File

@ -15,10 +15,8 @@ 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
@ -28,6 +26,10 @@ namespace keepass2android
/// </summary>
/// Checks in OnResume whether the timeout occured and the database must be locked/closed.
public class LockCloseActivity : LockingActivity {
//the check if the database was locked/closed can be disabled by the caller for activities
//which may be used "outside" the database (e.g. GeneratePassword for creating a master password)
protected const string NoLockCheck = "NO_LOCK_CHECK";
private IOConnectionInfo _ioc;
private BroadcastReceiver _intentReceiver;
@ -37,6 +39,9 @@ namespace keepass2android
base.OnCreate(savedInstanceState);
_ioc = App.Kp2a.GetDb().Ioc;
if (Intent.GetBooleanExtra(NoLockCheck, false))
return;
_intentReceiver = new LockCloseActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
@ -45,7 +50,13 @@ namespace keepass2android
protected override void OnDestroy()
{
UnregisterReceiver(_intentReceiver);
if (Intent.GetBooleanExtra(NoLockCheck, false) == false)
{
UnregisterReceiver(_intentReceiver);
}
base.OnDestroy();
}
@ -55,6 +66,9 @@ namespace keepass2android
{
base.OnResume();
if (Intent.GetBooleanExtra(NoLockCheck, false))
return;
if (TimeoutHelper.CheckShutdown(this, _ioc))
return;

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="23" android:versionName="0.8.6" package="keepass2android.keepass2android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14" />
<application android:label="keepass2android" android:icon="@drawable/ic_launcher">
<activity android:name="com.dropbox.client2.android.AuthActivity" android:launchMode="singleTask" android:configChanges="orientation|keyboard">
<intent-filter>
<!-- Change this to be db- followed by your app key -->
<data android:scheme="db-i8shu7v1hgh7ynt" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/Base" android:name="keepass2android.PasswordActivity">
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.kdbp" />
<data android:pathPattern=".*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\.kdbx" />
<data android:pathPattern=".*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,8 @@
style="@style/BottomBarActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:layout_weight="1">
<TextView
style="?android:actionBarTabTextStyle"

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8" ?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="12dip">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<TextView
android:id="@+id/version_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/divider1"
style="@style/InfoHeader"
android:text="@string/database_location" />
<keepass2android.views.TextWithHelp
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:help_text="@string/help_database_location"
android:text="@string/hint_database_location"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/filestorage_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_storage_file"
android:padding="5dp"
/>
<TextView
android:id="@+id/filestorage_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="Local file (TODO!)"
android:textSize="16dp" >
</TextView>
</LinearLayout>
<TextView
android:id="@+id/label_filename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="/keepass/keepass.kdbx"
android:layout_marginLeft="18dp"
/>
<Button android:id="@+id/btn_change_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_change_location"
style="@style/TextAppearance_SubElement"
/>
<TextView
android:id="@+id/password_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/divider1"
style="@style/InfoHeader"
android:text="@string/master_password" />
<keepass2android.views.TextWithHelp
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:help_text="@string/help_master_password"
android:text="@string/hint_master_password"
/>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/generate_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="@string/ellipsis" />
<ImageButton
android:id="@+id/toggle_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_menu_view"
android:layout_alignTop="@id/generate_button"
android:layout_toLeftOf="@id/generate_button"
/>
<EditText
android:id="@+id/entry_password"
style="@style/TextAppearance_EditEntry_Value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@id/generate_button"
android:layout_toLeftOf="@id/toggle_password"
android:hint="@string/hint_pass"
android:inputType="textPassword"
android:singleLine="true"
android:typeface="monospace" />
<!-- Confirm Password -->
<EditText
android:id="@+id/entry_confpassword"
style="@style/TextAppearance_EditEntry_Value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/entry_password"
android:hint="@string/hint_conf_pass"
android:inputType="textPassword"
android:singleLine="true"
android:typeface="monospace" />
</RelativeLayout>
<TextView
android:id="@+id/keyfile_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/divider1"
style="@style/InfoHeader"
android:text="@string/key_file" />
<keepass2android.views.TextWithHelp
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:help_text="@string/help_key_file"
android:text="@string/hint_key_file"
/>
/>
<CheckBox
android:id="@+id/use_keyfile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/use_key_file" />
<TextView
android:id="@+id/keyfile_filename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance_SubElement"
android:text="/sdcard/bla/blubb.txt"
/>
<Button android:id="@+id/btn_create"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/create_database"
/>
</LinearLayout>
</ScrollView>

View File

@ -12,12 +12,34 @@
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imglogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="12dp"
android:scaleType="fitXY"
android:src="@drawable/ic_keepass2android" />
<TextView
android:id="@+id/label_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Welcome to Keepass2Android!"
android:visibility="visible" />
<keepass2android.view.FileSelectButtons
android:id="@+id/file_select"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
</LinearLayout>
<!-- Small hack because I need to include a list since this is a list activity -->

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<keepass2android.views.Kp2aShortHelpView
android:id="@+id/help"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
style="@style/TextAppearance_Help"
android:textColor="@color/light_gray"
android:layout_alignParentRight="true"
android:background="?android:attr/selectableItemBackground"
/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/help"
style="@style/TextAppearance_SubElement"
/>
</RelativeLayout>

View File

@ -26,5 +26,8 @@
<color name="dark_gray">#303030</color>
<color name="element_being_moved">#a8a8a8</color>
<color name="emphasis">#31b6e7</color>
<color name="emphasis2">#4f7a8a</color>
</resources>

View File

@ -87,8 +87,8 @@
</style>
<style name="BottomBarActionButton" parent="android:style/Widget.Holo.Light.ActionButton">
<item name="android:background">#c8c8c8</item>
</style>
<item name="android:background">?android:attr/selectableItemBackground</item>
</style>
<style name="TextAppearance_EditEntry_Small">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
@ -130,6 +130,26 @@
<item name="android:paddingRight">8dip</item>
<item name="android:layout_marginBottom">-12dip</item>
</style>
<style name="TextAppearance_SubElement">
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
<item name="android:textSize">18sp</item>
<item name="android:textColor">#FF333333</item>
<item name="android:gravity">center_vertical</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:layout_marginRight">12dip</item>
<item name="android:layout_marginLeft">12dip</item>
</style>
<style name="TextAppearance_Help">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
<item name="android:textSize">24sp</item>
<item name="android:padding">12sp</item>
<item name="android:gravity">center_vertical</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:layout_marginRight">12dip</item>
<item name="android:layout_marginLeft">12dip</item>
</style>
<style name="EditEntryButton">

View File

@ -18,6 +18,19 @@
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<attr name="help_text" format="string" />
<declare-styleable name="Kp2aShortHelpView">
<attr name="android:text"/>
<attr name="help_text" />
</declare-styleable>
<declare-styleable name="TextWithHelp">
<attr name="android:text"/>
<attr name="help_text" />
</declare-styleable>
<string name="icon_info">&#xf05a;</string>
<string name="library_name"></string>
<string name="default_file_path">/keepass/keepass.kdbx</string>
<string name="donate_url"><![CDATA[http://philipp.crocoll.net/donate.php?lang=%1$s&app=%2$s]]></string>

View File

@ -153,6 +153,7 @@
<string name="pass_filename">KeePass database filename</string>
<string name="password_title">Enter database password</string>
<string name="progress_create">Creating new database…</string>
<string name="create_database">Create database</string>
<string name="progress_title">Working…</string>
<string name="remember_keyfile_summary">Remembers the location of keyfiles</string>
<string name="remember_keyfile_title">Save keyfile</string>
@ -333,8 +334,24 @@
<string name="filestorage_setup_title">File access initialization</string>
<string name="database_location">Database location</string>
<string name="help_database_location">You can store your database locally on your Android device or in the cloud (non-Offline version only). Keepass2Android makes the database available even if you are offline. As the database is securely encrypted with AES 256 bit encryption, nobody will be able to access your passwords except you. We recommend to select Dropbox: It\'s accessible on all your devices and even provides backups of previous file versions.</string>
<string name="hint_database_location">Select where you want to store the database:</string>
<string name="button_change_location">Change location</string>
<string name="master_password">Master password</string>
<string name="help_master_password">Your database is encrypted with the password you enter here. Choose a strong password in order to keep the database safe! Tip: Make up a sentence or two and use the first letters of the words as password. Include punctuation marks.</string>
<string name="hint_master_password">Select a master password to protect your database:</string>
<string name="key_file">Key file</string>
<string name="help_key_file">A key file is basically a password stored in a file. Key files are typically stronger than master passwords, because the key can be a lot more complicated; however it\'s also harder to keep them secret. If you store your database in the cloud, don\'t store the key file there as well! This would make it completely useless! Important: Do not change the contents of the key file after creating the database!</string>
<string name="hint_key_file">Choose if you want to use a key file in addition to your master password:</string>
<string name="use_key_file">Use key file</string>
<string name="error_adding_keyfile">Error while adding the keyfile!</string>
<string name="ChangeLog_title">Change log</string>
<string name="ChangeLog_0_9">
<b>Version 0.9 preview</b>\n

View File

@ -22,8 +22,10 @@ using Android.App;
using Android.Content;
using Android.Database;
using Android.Provider;
using Android.Views;
using Android.Widget;
using Android.Content.PM;
using KeePassLib.Serialization;
using Uri = Android.Net.Uri;
namespace keepass2android
@ -203,6 +205,52 @@ namespace keepass2android
//Actionbar is available since 11, but the layout has its own "pseudo actionbar" until 13
return ((int)Android.OS.Build.VERSION.SdkInt >= 14) && (activity.ActionBar != null);
}
public static void ShowFilenameDialog(Activity activity, EventHandler onOpen, EventHandler onCreate, bool showBrowseButton,
string defaultFilename, string detailsText, int requestCodeBrowse)
{
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.SetView(activity.LayoutInflater.Inflate(Resource.Layout.file_selection_filename, null));
Dialog dialog = builder.Create();
dialog.Show();
Button openButton = (Button) dialog.FindViewById(Resource.Id.open);
Button createButton = (Button) dialog.FindViewById(Resource.Id.create);
TextView enterFilenameDetails = (TextView) dialog.FindViewById(Resource.Id.label_open_by_filename_details);
openButton.Visibility = onOpen != null ? ViewStates.Visible : ViewStates.Gone;
createButton.Visibility = onCreate != null? ViewStates.Visible : ViewStates.Gone;
// Set the initial value of the filename
EditText editFilename = (EditText) dialog.FindViewById(Resource.Id.file_filename);
editFilename.Text = defaultFilename;
enterFilenameDetails.Text = detailsText;
enterFilenameDetails.Visibility = enterFilenameDetails.Text == "" ? ViewStates.Gone : ViewStates.Visible;
// Open button
if (onOpen != null)
openButton.Click += onOpen;
// Create button
if (onCreate != null)
createButton.Click += onCreate;
Button cancelButton = (Button) dialog.FindViewById(Resource.Id.fnv_cancel);
cancelButton.Click += (sender, e) => dialog.Dismiss();
ImageButton browseButton = (ImageButton) dialog.FindViewById(Resource.Id.browse_button);
if (!showBrowseButton)
{
browseButton.Visibility = ViewStates.Invisible;
}
browseButton.Click += (sender, evt) =>
{
string filename = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text;
Util.ShowBrowseDialog(filename, activity, requestCodeBrowse, onCreate != null);
};
}
}
}

View File

@ -42,6 +42,7 @@ namespace keepass2android
public static class AppNames
{
public const string AppName = "@string/app_name_nonet";
public const int AppNameResource = Resource.String.app_name_nonet;
public const string AppNameShort = "@string/short_app_name_nonet";
public const string AppLauncherTitle = "@string/short_app_name_nonet";
public const string PackagePart = "keepass2android_nonet";
@ -56,6 +57,7 @@ namespace keepass2android
public static class AppNames
{
public const string AppName = "@string/app_name";
public const int AppNameResource = Resource.String.app_name;
public const string AppNameShort = "@string/short_app_name";
public const string AppLauncherTitle = "@string/app_name";
public const string PackagePart = "keepass2android";
@ -377,7 +379,7 @@ namespace keepass2android
}
//TODO: catch!
throw new Exception("Unknown protocol " + iocInfo.Path);
throw new NoFileStorageFoundException("Unknown protocol " + iocInfo.Path);
}
public IEnumerable<IFileStorage> FileStorages

View File

@ -0,0 +1,34 @@
using System;
using System.Runtime.Serialization;
namespace keepass2android
{
[Serializable]
public class NoFileStorageFoundException : Exception
{
//
// For guidelines regarding the creation of new exception types, see
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
// and
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
//
public NoFileStorageFoundException()
{
}
public NoFileStorageFoundException(string message) : base(message)
{
}
public NoFileStorageFoundException(string message, Exception inner) : base(message, inner)
{
}
protected NoFileStorageFoundException(
SerializationInfo info,
StreamingContext context) : base(info, context)
{
}
}
}

View File

@ -66,132 +66,6 @@ namespace keepass2android
public const string NoForwardToPasswordActivity = "NoForwardToPasswordActivity";
void ShowFilenameDialog(bool showOpenButton, bool showCreateButton, bool showBrowseButton, string defaultFilename, string detailsText, int requestCodeBrowse)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetView(LayoutInflater.Inflate(Resource.Layout.file_selection_filename, null));
Dialog dialog = builder.Create();
dialog.Show();
Button openButton = (Button)dialog.FindViewById(Resource.Id.open);
Button createButton = (Button)dialog.FindViewById(Resource.Id.create);
TextView enterFilenameDetails = (TextView)dialog.FindViewById(Resource.Id.label_open_by_filename_details);
openButton.Visibility = showOpenButton ? ViewStates.Visible : ViewStates.Gone;
createButton.Visibility = showCreateButton ? ViewStates.Visible : ViewStates.Gone;
// Set the initial value of the filename
EditText editFilename = (EditText)dialog.FindViewById(Resource.Id.file_filename);
editFilename.Text = defaultFilename;
enterFilenameDetails.Text = detailsText;
enterFilenameDetails.Visibility = enterFilenameDetails.Text == "" ? ViewStates.Gone : ViewStates.Visible;
// Open button
openButton.Click += ( sender, evt) => {
String fileName = ((EditText)dialog.FindViewById(Resource.Id.file_filename)).Text;
IOConnectionInfo ioc = new IOConnectionInfo
{
Path = fileName
};
LaunchPasswordActivityForIoc(ioc);
};
// Create button
createButton.Click += (sender, evt) => {
String filename = ((EditText)dialog.FindViewById(Resource.Id.file_filename)).Text;
//TODO: allow non-local files?
// Make sure file name exists
if (filename.Length == 0)
{
Toast
.MakeText(this,
Resource.String.error_filename_required,
ToastLength.Long).Show();
return;
}
// Try to create the file
Java.IO.File file = new Java.IO.File(filename);
try
{
if (file.Exists())
{
Toast.MakeText(this,
Resource.String.error_database_exists,
ToastLength.Long).Show();
return;
}
Java.IO.File parent = file.ParentFile;
if (parent == null || (parent.Exists() && ! parent.IsDirectory))
{
Toast.MakeText(this,
Resource.String.error_invalid_path,
ToastLength.Long).Show();
return;
}
if (! parent.Exists())
{
// Create parent dircetory
if (! parent.Mkdirs())
{
Toast.MakeText(this,
Resource.String.error_could_not_create_parent,
ToastLength.Long).Show();
return;
}
}
file.CreateNewFile();
} catch (Java.IO.IOException ex)
{
Toast.MakeText(
this,
GetText(Resource.String.error_file_not_create) + " "
+ ex.LocalizedMessage,
ToastLength.Long).Show();
return;
}
// Prep an object to collect a password once the database has been created
CollectPassword collectPassword = new CollectPassword(
new LaunchGroupActivity(IOConnectionInfo.FromPath(filename), this), this);
// Create the new database
CreateDb create = new CreateDb(App.Kp2a, this, IOConnectionInfo.FromPath(filename), collectPassword, true);
ProgressTask createTask = new ProgressTask(
App.Kp2a,
this, create);
createTask.Run();
};
Button cancelButton = (Button)dialog.FindViewById(Resource.Id.fnv_cancel);
cancelButton.Click += (sender, e) => dialog.Dismiss();
ImageButton browseButton = (ImageButton)dialog.FindViewById(Resource.Id.browse_button);
if (!showBrowseButton)
{
browseButton.Visibility = ViewStates.Invisible;
}
browseButton.Click += (sender, evt) => {
string filename = ((EditText)dialog.FindViewById(Resource.Id.file_filename)).Text;
Util.ShowBrowseDialog(filename, this, requestCodeBrowse, showCreateButton);
};
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
@ -248,7 +122,11 @@ namespace keepass2android
//CREATE NEW
Button createNewButton = (Button)FindViewById(Resource.Id.start_create);
EventHandler createNewButtonClick = (sender, e) => ShowFilenameDialog(false, true, true, Android.OS.Environment.ExternalStorageDirectory + GetString(Resource.String.default_file_path), "", Intents.RequestCodeFileBrowseForCreate);
EventHandler createNewButtonClick = (sender, e) =>
{
//ShowFilenameDialog(false, true, true, Android.OS.Environment.ExternalStorageDirectory + GetString(Resource.String.default_file_path), "", Intents.RequestCodeFileBrowseForCreate)
StartActivityForResult(typeof (CreateDatabaseActivity), 0);
};
createNewButton.Click += createNewButtonClick;
/*//CREATE + IMPORT
@ -301,55 +179,8 @@ namespace keepass2android
}
private class LaunchGroupActivity : FileOnFinish {
readonly FileSelectActivity _activity;
private readonly IOConnectionInfo _ioc;
public LaunchGroupActivity(IOConnectionInfo ioc, FileSelectActivity activity): base(null) {
_activity = activity;
_ioc = ioc;
}
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 {
App.Kp2a.GetFileStorage(_ioc).Delete(_ioc);
}
}
}
private class CollectPassword: FileOnFinish {
readonly FileSelectActivity _activity;
readonly FileOnFinish _fileOnFinish;
public CollectPassword(FileOnFinish finish,FileSelectActivity activity):base(finish) {
_activity = activity;
_fileOnFinish = finish;
}
public override void Run() {
SetPasswordDialog password = new SetPasswordDialog(_activity, _fileOnFinish);
password.Show();
}
}
private void FillData()
{
@ -428,8 +259,19 @@ namespace keepass2android
LaunchPasswordActivityForIoc(ioc);
}
private void OnOpenButton(object sender, EventArgs evt)
{
Dialog dialog = (Dialog) sender;
String fileName = ((EditText)dialog.FindViewById(Resource.Id.file_filename)).Text;
IOConnectionInfo ioc = new IOConnectionInfo
{
Path = fileName
};
LaunchPasswordActivityForIoc(ioc);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
@ -460,7 +302,8 @@ namespace keepass2android
OnActivityResult,
defaultPath =>
{
ShowFilenameDialog(true, false, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
Util.ShowFilenameDialog(this, OnOpenButton, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
Intents.RequestCodeFileBrowseForOpen);
}
), false, 0, protocolId);
@ -490,10 +333,7 @@ namespace keepass2android
LaunchPasswordActivityForIoc(ioc);
}
if (requestCode == Intents.RequestCodeFileBrowseForCreate)
{
ShowFilenameDialog(false, true, true, filename, "", Intents.RequestCodeFileBrowseForCreate);
}
}
}
@ -521,7 +361,6 @@ namespace keepass2android
if (defaultPath.StartsWith("file://"))
{
fileProviderAuthority = "keepass2android.keepass2android.android-filechooser.localfile";
defaultPath = Environment.ExternalStorageDirectory + GetString(Resource.String.default_file_path);
}
Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority,
defaultPath);
@ -664,10 +503,6 @@ namespace keepass2android
Android.Database.ICursor cursor = ca.Cursor;
cursor.Requery();
}
}
}

View File

@ -24,7 +24,7 @@
<DebugType>full</DebugType>
<Optimize>False</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
@ -78,6 +78,8 @@
<Reference Include="Mono.Android.Support.v4" />
</ItemGroup>
<ItemGroup>
<Compile Include="app\NoFileStorageFoundException.cs" />
<Compile Include="CreateDatabaseActivity.cs" />
<Compile Include="fileselect\FileChooserFileProvider.cs" />
<Compile Include="fileselect\FileStorageSetupActivity.cs" />
<Compile Include="fileselect\FileStorageSetupInitiatorActivity.cs" />
@ -108,6 +110,7 @@
<Compile Include="views\GroupRootView.cs" />
<Compile Include="PwGroupListAdapter.cs" />
<Compile Include="views\FileStorageView.cs" />
<Compile Include="views\Kp2aShortHelpView.cs" />
<Compile Include="views\PwGroupView.cs" />
<Compile Include="settings\PrefsUtil.cs" />
<Compile Include="views\PwEntryView.cs" />
@ -150,9 +153,13 @@
<Compile Include="Utils\Spr\SprEngine.cs" />
<Compile Include="Utils\Spr\SprEngine.PickChars.cs" />
<Compile Include="Utils\EntryUtil.cs" />
<Compile Include="views\TextWithHelp.cs" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\searchable.xml" />
<AndroidAsset Include="Assets\fontawesome-webfont.ttf" />
<AndroidAsset Include="Assets\DejaVuSansMono.ttf" />
<AndroidAsset Include="Assets\LICENSE_dejavu" />
<None Include="Resources\AboutResources.txt" />
<None Include="Resources\drawable\Thumbs.db">
<Visible>False</Visible>
@ -654,7 +661,7 @@
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
<ItemGroup>
<Folder Include="Assets\" />
<Folder Include="Resources\color\" />
<Folder Include="SupportLib\" />
</ItemGroup>
<ItemGroup>
@ -841,4 +848,12 @@
<ItemGroup>
<AndroidResource Include="Resources\drawable\ic_keepass2android_nonet.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\create_database.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\text_with_help.xml" />
</ItemGroup>
</Project>

View File

@ -64,7 +64,7 @@ namespace keepass2android.view
} else {
tv.Visibility = ViewStates.Invisible;
tv.Visibility = ViewStates.Gone;
}

View File

@ -32,15 +32,10 @@ namespace keepass2android.view
{
public sealed class FileStorageView : ClickView
{
private readonly Activity _activity;
private readonly TextView _textView;
private readonly TextView _textviewDetails;
private int _pos;
private int? _defaultTextColor;
public FileStorageView(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
@ -50,8 +45,6 @@ namespace keepass2android.view
public FileStorageView(Activity activity, string protocolId, int pos)
: base(activity)
{
_activity = activity;
View ev = Inflate(activity, Resource.Layout.entry_list_entry, null);
_textView = (TextView)ev.FindViewById(Resource.Id.entry_text);
_textView.TextSize = PrefsUtil.GetListTextSize(activity);
@ -69,9 +62,6 @@ namespace keepass2android.view
private void PopulateView(View ev, string protocolId, int pos)
{
_pos = pos;
ImageView iv = (ImageView)ev.FindViewById(Resource.Id.entry_icon);
Drawable drawable = App.Kp2a.GetResourceDrawable("ic_storage_" + protocolId);

View File

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Text;
using Android.Text.Method;
using Android.Text.Style;
using Android.Text.Util;
using Android.Util;
using Android.Views;
using Android.Widget;
namespace keepass2android.views
{
public class Kp2aShortHelpView: TextView
{
private string _helpText;
private static Typeface _iconFont;
protected Kp2aShortHelpView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public Kp2aShortHelpView(Context context) : base(context)
{
}
public Kp2aShortHelpView(Context context, IAttributeSet attrs) : base(context, attrs)
{
Initialize(attrs);
}
public Kp2aShortHelpView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
Initialize(attrs);
}
public string HelpText
{
get { return _helpText; }
set { _helpText = value;
UpdateView();
}
}
private void UpdateView()
{
if (!String.IsNullOrEmpty(_helpText))
{
Text = Context.GetString(Resource.String.icon_info);
Clickable = true;
if (_iconFont == null)
_iconFont = Typeface.CreateFromAsset(Context.Assets, "fontawesome-webfont.ttf");
System.Diagnostics.Debug.Assert(_iconFont != null, "_iconFont != null");
SetTypeface(_iconFont, TypefaceStyle.Normal);
//TextFormatted = Html.FromHtml("<a>" + Text + "</a>");
//var spannableString = new SpannableString(Text);
//spannableString.SetSpan(new UnderlineSpan(), 0, Text.Length, SpanTypes.ExclusiveExclusive);
//TextFormatted = spannableString;
MovementMethod = LinkMovementMethod.Instance;
Click += (sender, args) =>
{
new AlertDialog.Builder(Context)
.SetTitle(Context.GetString(AppNames.AppNameResource))
.SetMessage(_helpText)
.SetPositiveButton(Android.Resource.String.Ok, (o, eventArgs) => { })
.Show();
};
Visibility = ViewStates.Visible;
}
else
{
Visibility = ViewStates.Gone;
}
}
void Initialize(IAttributeSet attrs)
{
TypedArray a = Context.ObtainStyledAttributes(
attrs,
Resource.Styleable.Kp2aShortHelpView);
HelpText = a.GetString(Resource.Styleable.Kp2aShortHelpView_help_text);
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
namespace keepass2android.views
{
public class TextWithHelp : RelativeLayout
{
public TextWithHelp(Context context, IAttributeSet attrs) :
base(context, attrs)
{
Initialize(attrs);
}
public TextWithHelp(Context context, IAttributeSet attrs, int defStyle) :
base(context, attrs, defStyle)
{
Initialize(attrs);
}
private void Initialize(IAttributeSet attrs)
{
LayoutInflater inflater = (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);
inflater.Inflate(Resource.Layout.text_with_help, this);
TypedArray a = Context.ObtainStyledAttributes(
attrs,
Resource.Styleable.TextWithHelp);
((Kp2aShortHelpView)FindViewById(Resource.Id.help)).HelpText = a.GetString(Resource.Styleable.TextWithHelp_help_text);
const string xmlns = "http://schemas.android.com/apk/res/android";
((TextView)FindViewById(Resource.Id.text)).Text = Context.GetString(attrs.GetAttributeResourceValue(xmlns, "text",Resource.String.ellipsis));
}
}
}