From 66cd05b9f439da8e2aecb676cc417a607e2b9e25 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sun, 17 Nov 2013 17:01:53 +0100 Subject: [PATCH] first working - but still incomplete - implementation for OTP/Yubikey --- .gitignore | 2 + src/keepass2android/NfcOtpActivity.cs | 63 ++++ src/keepass2android/PasswordActivity.cs | 167 +++++++-- .../Resources/Resource.designer.cs | 331 +++++++++--------- .../Resources/layout-v14/password.xml | 21 +- .../Resources/values/strings.xml | 3 + src/keepass2android/ShareUrlResults.cs | 1 + .../addons/OtpKeyProv/OathHotpKeyProv.cs | 82 ++--- src/keepass2android/intents/Intents.cs | 4 + src/keepass2android/keepass2android.csproj | 1 + 10 files changed, 446 insertions(+), 229 deletions(-) create mode 100644 src/keepass2android/NfcOtpActivity.cs diff --git a/.gitignore b/.gitignore index 8e73fb21..796e6dbc 100644 --- a/.gitignore +++ b/.gitignore @@ -166,3 +166,5 @@ Thumbs.db /src/AppCompatV7Binding/obj/Release /src/java/workspace/DriveTest + +/src/Components diff --git a/src/keepass2android/NfcOtpActivity.cs b/src/keepass2android/NfcOtpActivity.cs new file mode 100644 index 00000000..5e3a48af --- /dev/null +++ b/src/keepass2android/NfcOtpActivity.cs @@ -0,0 +1,63 @@ +using System; +using Android.App; +using Android.Content; +using Android.Content.PM; +using Android.OS; +using Java.Util.Regex; + +namespace keepass2android +{ + [Activity(Label = "@string/app_name", + ConfigurationChanges = ConfigChanges.Orientation | + ConfigChanges.KeyboardHidden, + NoHistory = true, + ExcludeFromRecents = true, + Theme = "@android:style/Theme.Dialog")] + [IntentFilter(new[] { "android.nfc.action.NDEF_DISCOVERED" }, + Label = "@string/app_name", + Categories = new[] { Intent.CategoryDefault }, + DataHost = "my.yubico.com", + DataPathPrefix = "/neo", + DataScheme = "https")] + public class NfcOtpActivity : Activity + { + private String GetOtpFromIntent(Intent intent) + { + String data = intent.DataString; + Matcher matcher = OtpPattern.Matcher(data); + if (matcher.Matches()) + { + String otp = matcher.Group(1); + return otp; + } + return null; + } + + + private static readonly Java.Util.Regex.Pattern OtpPattern = Java.Util.Regex.Pattern.Compile("^https://my\\.yubico\\.com/neo/(.+)$"); + + + + protected override void OnCreate(Bundle bundle) + { + base.OnCreate(bundle); + + Intent i = new Intent(this, typeof (PasswordActivity)); + i.SetAction(Intents.StartWithOtp); + + //things to consider: + // PasswordActivity should be resumed if currently active -> this is why single top is used and why PasswordActivity is started + // If PasswordActivity is not open already, it may be the wrong place to send our OTP to because maybe the user first needs to select + // a file (which might require UI action like entering credentials, all of which is handled in FileSelectActivity) + // FileSelectActivity is not on the back stack, it finishes itself. + // -> PasswordActivity needs to handle this and return to FSA. + + + i.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop); + i.PutExtra(Intents.OtpExtraKey, GetOtpFromIntent(Intent)); + StartActivity(i); + Finish(); + + } + } +} \ No newline at end of file diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index a109f145..99240308 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -25,7 +25,6 @@ using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; -using Java.Lang; using Java.Net; using Android.Preferences; using Java.IO; @@ -33,7 +32,6 @@ using Android.Text; using Android.Content.PM; using KeePassLib.Keys; using KeePassLib.Serialization; -using KeePassLib.Utility; using OtpKeyProv; using keepass2android.Io; using keepass2android.Utils; @@ -47,6 +45,7 @@ namespace keepass2android { [Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden, + LaunchMode = LaunchMode.SingleInstance, Theme="@style/Base")] public class PasswordActivity : LockingActivity { @@ -80,6 +79,9 @@ namespace keepass2android internal AppTask AppTask; private bool _killOnDestroy; private string _password = ""; + //OTPs which should be entered into the OTP fields as soon as these become visible + private readonly List _pendingOtps = new List(); + private const int RequestCodePrepareDbFile = 1000; private const int RequestCodePrepareOtpAuxFile = 1001; @@ -139,8 +141,10 @@ namespace keepass2android Intent i = new Intent(act, typeof(PasswordActivity)); + i.SetFlags(ActivityFlags.ClearTask | ActivityFlags.NewTask); i.PutExtra(KeyFilename, fileName); appTask.ToIntent(i); + act.StartActivityForResult(i, 0); } @@ -155,7 +159,9 @@ namespace keepass2android } Intent i = new Intent(act, typeof(PasswordActivity)); + PutIoConnectionToIntent(ioc, i); + i.SetFlags(ActivityFlags.ClearTask); appTask.ToIntent(i); @@ -259,29 +265,39 @@ namespace keepass2android Toast.MakeText(this, GetString(Resource.String.CouldntLoadOtpAuxFile), ToastLength.Long).Show(); return; } - FindViewById(Resource.Id.init_otp).Visibility = ViewStates.Gone; + FindViewById(Resource.Id.otpInitView).Visibility = ViewStates.Gone; FindViewById(Resource.Id.otpEntry).Visibility = ViewStates.Visible; int c = 0; - foreach (int otpId in _otpTextViewIds) - { - c++; - var otpTextView = FindViewById(otpId); - otpTextView.Text = ""; - otpTextView.Hint = GetString(Resource.String.otp_hint, new Object[] {c}); - otpTextView.SetFilters(new IInputFilter[] {new InputFilterLengthFilter((int)_otpInfo.OtpLength) }); - if (c > _otpInfo.OtpsRequired) + + foreach (int otpId in _otpTextViewIds) { - otpTextView.Visibility = ViewStates.Gone; - } - else - { - otpTextView.TextChanged += (sender, args) => + c++; + var otpTextView = FindViewById(otpId); + if (c <= _pendingOtps.Count) { - UpdateOkButtonState(); - }; + otpTextView.Text = _pendingOtps[c-1]; + } + else + { + otpTextView.Text = ""; + } + otpTextView.Hint = GetString(Resource.String.otp_hint, new Object[] {c}); + otpTextView.SetFilters(new IInputFilter[] {new InputFilterLengthFilter((int)_otpInfo.OtpLength) }); + if (c > _otpInfo.OtpsRequired) + { + otpTextView.Visibility = ViewStates.Gone; + } + else + { + otpTextView.TextChanged += (sender, args) => + { + UpdateOkButtonState(); + }; + } } + _pendingOtps.Clear(); + } - } ).Execute(); } @@ -290,6 +306,8 @@ namespace keepass2android base.OnCreate(savedInstanceState); if (savedInstanceState != null) _showPassword = savedInstanceState.GetBoolean(ShowpasswordKey, false); + + AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); Intent i = Intent; String action = i.Action; @@ -302,6 +320,7 @@ namespace keepass2android if (action != null && action.Equals(ViewIntent)) { + //started from "view" intent (e.g. from file browser) _ioConnection.Path = i.DataString; if (! _ioConnection.Path.Substring(0, 7).Equals("file://")) @@ -333,7 +352,54 @@ namespace keepass2android _keyFileOrProvider = GetKeyFile(_ioConnection.Path); - } else + } + else if ((action != null) && (action.Equals(Intents.StartWithOtp))) + { + //create called after detecting an OTP via NFC + //this means the Activity was not on the back stack before, i.e. no database has been selected + + _ioConnection = null; + + //see if we can get a database from recent: + if (App.Kp2a.FileDbHelper.HasRecentFiles()) + { + ICursor filesCursor = App.Kp2a.FileDbHelper.FetchAllFiles(); + StartManagingCursor(filesCursor); + filesCursor.MoveToFirst(); + IOConnectionInfo ioc = App.Kp2a.FileDbHelper.CursorToIoc(filesCursor); + if (App.Kp2a.GetFileStorage(ioc).RequiresSetup(ioc) == false) + { + IFileStorage fileStorage = App.Kp2a.GetFileStorage(ioc); + + if (!fileStorage.RequiresCredentials(ioc)) + { + //ok, we can use this file + _ioConnection = ioc; + + } + } + } + + if (_ioConnection == null) + { + //We need to go to FileSelectActivity first. + //For security reasons: discard the OTP (otherwise the user might not select a database now and forget + //about the OTP, but it would still be stored in the Intents and later be passed to PasswordActivity again. + + Toast.MakeText(this, GetString(Resource.String.otp_discarded_because_no_db), ToastLength.Long).Show(); + GoToFileSelectActivity(); + Finish(); + return; + } + + //user obviously wants to use OTP: + _keyFileOrProvider = KeyProviderIdOtp; + + //remember the OTP for later use + _pendingOtps.Add(Intent.GetStringExtra(Intents.OtpExtraKey)); + Intent.RemoveExtra(Intents.OtpExtraKey); + } + else { SetIoConnectionFromIntent(_ioConnection, i); _keyFileOrProvider = i.GetStringExtra(KeyKeyfile); @@ -350,7 +416,7 @@ namespace keepass2android App.Kp2a.LockDatabase(false); } - AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); + SetContentView(Resource.Layout.password); PopulateView(); @@ -509,6 +575,10 @@ namespace keepass2android FindViewById(Resource.Id.otpView).Visibility = KeyProviderType == KeyProviders.Otp ? ViewStates.Visible : ViewStates.Gone; + if (KeyProviderType == KeyProviders.Otp) + { + FindViewById(Resource.Id.otps_pending).Visibility = _pendingOtps.Count > 0 ? ViewStates.Visible : ViewStates.Gone; + } UpdateOkButtonState(); } @@ -715,6 +785,61 @@ namespace keepass2android base.OnSaveInstanceState(outState); AppTask.ToBundle(outState); outState.PutBoolean(ShowpasswordKey, _showPassword); + //TODO: + // * save OTP state + + //more OTP TODO: + // * NfcOtp: Ask for close when db open + // * Caching of aux file + // * -> implement IFileStorage in JavaFileStorage based on ListFiles + } + + protected override void OnNewIntent(Intent intent) + { + base.OnNewIntent(intent); + + //this method is called from the NfcOtpActivity's startActivity() if the activity is already running + //note: it's not called in other cases because OnNewIntent requires the activity to be on top already + //which is never the case when started from another activity (in the same task). + //NfcOtpActivity sets the ClearTop flag to get OnNewIntent called. + if ((intent != null) && (intent.HasExtra(Intents.OtpExtraKey))) + { + string otp = intent.GetStringExtra(Intents.OtpExtraKey); + + if (_otpInfo == null) + { + //Entering OTPs not yet initialized: + _pendingOtps.Add(otp); + UpdateKeyProviderUiState(); + } + else + { + //Entering OTPs is initialized. Write OTP into first empty field: + bool foundEmptyField = false; + foreach (int otpId in _otpTextViewIds) + { + EditText otpEdit = FindViewById(otpId); + if ((otpEdit.Visibility == ViewStates.Visible) && String.IsNullOrEmpty(otpEdit.Text)) + { + otpEdit.Text = otp; + foundEmptyField = true; + break; + } + } + //did we find a field? + if (!foundEmptyField) + { + Toast.MakeText(this, GetString(Resource.String.otp_discarded_no_space), ToastLength.Long).Show(); + } + } + + Spinner passwordModeSpinner = FindViewById(Resource.Id.password_mode_spinner); + if (passwordModeSpinner.SelectedItemPosition != (int) KeyProviders.Otp) + { + passwordModeSpinner.SetSelection((int)KeyProviders.Otp); + } + } + } protected override void OnResume() diff --git a/src/keepass2android/Resources/Resource.designer.cs b/src/keepass2android/Resources/Resource.designer.cs index 0e7a0efb..8f1cd9d2 100644 --- a/src/keepass2android/Resources/Resource.designer.cs +++ b/src/keepass2android/Resources/Resource.designer.cs @@ -738,17 +738,17 @@ namespace keepass2android // aapt resource value: 0x7f07008c public const int IconGridView = 2131165324; + // aapt resource value: 0x7f0700a8 + public const int QuickUnlock_button = 2131165352; + + // aapt resource value: 0x7f0700a9 + public const int QuickUnlock_buttonLock = 2131165353; + // aapt resource value: 0x7f0700a6 - public const int QuickUnlock_button = 2131165350; + public const int QuickUnlock_label = 2131165350; // aapt resource value: 0x7f0700a7 - public const int QuickUnlock_buttonLock = 2131165351; - - // aapt resource value: 0x7f0700a4 - public const int QuickUnlock_label = 2131165348; - - // aapt resource value: 0x7f0700a5 - public const int QuickUnlock_password = 2131165349; + public const int QuickUnlock_password = 2131165351; // aapt resource value: 0x7f07006d public const int RelativeLayout = 2131165293; @@ -771,8 +771,8 @@ namespace keepass2android // aapt resource value: 0x7f07007f public const int add_group = 2131165311; - // aapt resource value: 0x7f0700ba - public const int add_url_entry = 2131165370; + // aapt resource value: 0x7f0700bc + public const int add_url_entry = 2131165372; // aapt resource value: 0x7f07003c public const int advanced_container = 2131165244; @@ -822,38 +822,38 @@ namespace keepass2android // aapt resource value: 0x7f070082 public const int cancel_insert_element = 2131165314; - // aapt resource value: 0x7f0700b7 - public const int cbCaseSensitive = 2131165367; + // aapt resource value: 0x7f0700b9 + public const int cbCaseSensitive = 2131165369; - // aapt resource value: 0x7f0700b8 - public const int cbExcludeExpiredEntries = 2131165368; - - // aapt resource value: 0x7f0700ad - public const int cbRegEx = 2131165357; - - // aapt resource value: 0x7f0700b6 - public const int cbSearchInGroupName = 2131165366; - - // aapt resource value: 0x7f0700b3 - public const int cbSearchInNotes = 2131165363; - - // aapt resource value: 0x7f0700b4 - public const int cbSearchInOtherStrings = 2131165364; - - // aapt resource value: 0x7f0700b2 - public const int cbSearchInPassword = 2131165362; - - // aapt resource value: 0x7f0700b5 - public const int cbSearchInTags = 2131165365; + // aapt resource value: 0x7f0700ba + public const int cbExcludeExpiredEntries = 2131165370; // aapt resource value: 0x7f0700af - public const int cbSearchInTitle = 2131165359; + public const int cbRegEx = 2131165359; - // aapt resource value: 0x7f0700b0 - public const int cbSearchInUrl = 2131165360; + // aapt resource value: 0x7f0700b8 + public const int cbSearchInGroupName = 2131165368; + + // aapt resource value: 0x7f0700b5 + public const int cbSearchInNotes = 2131165365; + + // aapt resource value: 0x7f0700b6 + public const int cbSearchInOtherStrings = 2131165366; + + // aapt resource value: 0x7f0700b4 + public const int cbSearchInPassword = 2131165364; + + // aapt resource value: 0x7f0700b7 + public const int cbSearchInTags = 2131165367; // aapt resource value: 0x7f0700b1 - public const int cbSearchInUsername = 2131165361; + public const int cbSearchInTitle = 2131165361; + + // aapt resource value: 0x7f0700b2 + public const int cbSearchInUrl = 2131165362; + + // aapt resource value: 0x7f0700b3 + public const int cbSearchInUsername = 2131165363; // aapt resource value: 0x7f07007d public const int cb_brackets = 2131165309; @@ -885,14 +885,14 @@ namespace keepass2android // aapt resource value: 0x7f070062 public const int create = 2131165282; - // aapt resource value: 0x7f0700c2 - public const int cred_password = 2131165378; + // aapt resource value: 0x7f0700c4 + public const int cred_password = 2131165380; + + // aapt resource value: 0x7f0700c5 + public const int cred_remember_mode = 2131165381; // aapt resource value: 0x7f0700c3 - public const int cred_remember_mode = 2131165379; - - // aapt resource value: 0x7f0700c1 - public const int cred_username = 2131165377; + public const int cred_username = 2131165379; // aapt resource value: 0x7f07002c public const int delete_extra = 2131165228; @@ -1095,8 +1095,8 @@ namespace keepass2android // aapt resource value: 0x7f070088 public const int group_text = 2131165320; - // aapt resource value: 0x7f0700bf - public const int help = 2131165375; + // aapt resource value: 0x7f0700c1 + public const int help = 2131165377; // aapt resource value: 0x7f070004 public const int hybrid = 2131165188; @@ -1119,8 +1119,8 @@ namespace keepass2android // aapt resource value: 0x7f070026 public const int imgoktfest = 2131165222; - // aapt resource value: 0x7f07009a - public const int init_otp = 2131165338; + // aapt resource value: 0x7f07009b + public const int init_otp = 2131165339; // aapt resource value: 0x7f070081 public const int insert_element = 2131165313; @@ -1158,62 +1158,62 @@ namespace keepass2android // aapt resource value: 0x7f070070 public const int length_label = 2131165296; - // aapt resource value: 0x7f0700ac - public const int linearLayout1 = 2131165356; - - // aapt resource value: 0x7f0700cd - public const int menu_about = 2131165389; - - // aapt resource value: 0x7f0700cc - public const int menu_app_settings = 2131165388; - - // aapt resource value: 0x7f0700cb - public const int menu_cancel_edit = 2131165387; - - // aapt resource value: 0x7f0700d3 - public const int menu_change_db = 2131165395; + // aapt resource value: 0x7f0700ae + public const int linearLayout1 = 2131165358; // aapt resource value: 0x7f0700cf - public const int menu_change_master_key = 2131165391; - - // aapt resource value: 0x7f0700c4 - public const int menu_donate = 2131165380; - - // aapt resource value: 0x7f0700c6 - public const int menu_goto_url = 2131165382; - - // aapt resource value: 0x7f0700c7 - public const int menu_lock = 2131165383; - - // aapt resource value: 0x7f0700c9 - public const int menu_rate = 2131165385; + public const int menu_about = 2131165391; // aapt resource value: 0x7f0700ce - public const int menu_search = 2131165390; + public const int menu_app_settings = 2131165390; - // aapt resource value: 0x7f0700d2 - public const int menu_search_advanced = 2131165394; + // aapt resource value: 0x7f0700cd + public const int menu_cancel_edit = 2131165389; + + // aapt resource value: 0x7f0700d5 + public const int menu_change_db = 2131165397; // aapt resource value: 0x7f0700d1 - public const int menu_sort = 2131165393; + public const int menu_change_master_key = 2131165393; + + // aapt resource value: 0x7f0700c6 + public const int menu_donate = 2131165382; // aapt resource value: 0x7f0700c8 - public const int menu_suggest_improvements = 2131165384; + public const int menu_goto_url = 2131165384; + + // aapt resource value: 0x7f0700c9 + public const int menu_lock = 2131165385; + + // aapt resource value: 0x7f0700cb + public const int menu_rate = 2131165387; // aapt resource value: 0x7f0700d0 - public const int menu_sync = 2131165392; + public const int menu_search = 2131165392; - // aapt resource value: 0x7f0700c5 - public const int menu_toggle_pass = 2131165381; + // aapt resource value: 0x7f0700d4 + public const int menu_search_advanced = 2131165396; + + // aapt resource value: 0x7f0700d3 + public const int menu_sort = 2131165395; // aapt resource value: 0x7f0700ca - public const int menu_translate = 2131165386; + public const int menu_suggest_improvements = 2131165386; + + // aapt resource value: 0x7f0700d2 + public const int menu_sync = 2131165394; + + // aapt resource value: 0x7f0700c7 + public const int menu_toggle_pass = 2131165383; + + // aapt resource value: 0x7f0700cc + public const int menu_translate = 2131165388; // aapt resource value: 0x7f070028 public const int no_donate = 2131165224; - // aapt resource value: 0x7f0700bb - public const int no_results = 2131165371; + // aapt resource value: 0x7f0700bd + public const int no_results = 2131165373; // aapt resource value: 0x7f070000 public const int none = 2131165184; @@ -1230,35 +1230,41 @@ namespace keepass2android // aapt resource value: 0x7f070061 public const int open = 2131165281; - // aapt resource value: 0x7f07009d - public const int otp1 = 2131165341; - - // aapt resource value: 0x7f07009e - public const int otp2 = 2131165342; - // aapt resource value: 0x7f07009f - public const int otp3 = 2131165343; + public const int otp1 = 2131165343; // aapt resource value: 0x7f0700a0 - public const int otp4 = 2131165344; + public const int otp2 = 2131165344; // aapt resource value: 0x7f0700a1 - public const int otp5 = 2131165345; + public const int otp3 = 2131165345; // aapt resource value: 0x7f0700a2 - public const int otp6 = 2131165346; + public const int otp4 = 2131165346; - // aapt resource value: 0x7f07009b - public const int otpEntry = 2131165339; + // aapt resource value: 0x7f0700a3 + public const int otp5 = 2131165347; + + // aapt resource value: 0x7f0700a4 + public const int otp6 = 2131165348; + + // aapt resource value: 0x7f07009d + public const int otpEntry = 2131165341; + + // aapt resource value: 0x7f07009a + public const int otpInitView = 2131165338; // aapt resource value: 0x7f070099 public const int otpView = 2131165337; - // aapt resource value: 0x7f07009c - public const int otp_expl = 2131165340; + // aapt resource value: 0x7f07009e + public const int otp_expl = 2131165342; - // aapt resource value: 0x7f0700bd - public const int pass_conf_password = 2131165373; + // aapt resource value: 0x7f07009c + public const int otps_pending = 2131165340; + + // aapt resource value: 0x7f0700bf + public const int pass_conf_password = 2131165375; // aapt resource value: 0x7f070092 public const int pass_keyfile = 2131165330; @@ -1266,8 +1272,8 @@ namespace keepass2android // aapt resource value: 0x7f070093 public const int pass_ok = 2131165331; - // aapt resource value: 0x7f0700bc - public const int pass_password = 2131165372; + // aapt resource value: 0x7f0700be + public const int pass_password = 2131165374; // aapt resource value: 0x7f07006e public const int password = 2131165294; @@ -1290,8 +1296,8 @@ namespace keepass2android // aapt resource value: 0x7f07002b public const int protection = 2131165227; - // aapt resource value: 0x7f0700a3 - public const int qu_filename = 2131165347; + // aapt resource value: 0x7f0700a5 + public const int qu_filename = 2131165349; // aapt resource value: 0x7f070023 public const int rounds = 2131165219; @@ -1302,29 +1308,29 @@ namespace keepass2android // aapt resource value: 0x7f070002 public const int satellite = 2131165186; + // aapt resource value: 0x7f0700ad + public const int scrollView1 = 2131165357; + + // aapt resource value: 0x7f0700ac + public const int searchEditText = 2131165356; + // aapt resource value: 0x7f0700ab - public const int scrollView1 = 2131165355; + public const int search_button = 2131165355; + + // aapt resource value: 0x7f0700b0 + public const int search_in_label = 2131165360; // aapt resource value: 0x7f0700aa - public const int searchEditText = 2131165354; + public const int search_label = 2131165354; - // aapt resource value: 0x7f0700a9 - public const int search_button = 2131165353; - - // aapt resource value: 0x7f0700ae - public const int search_in_label = 2131165358; - - // aapt resource value: 0x7f0700a8 - public const int search_label = 2131165352; - - // aapt resource value: 0x7f0700b9 - public const int select_other_entry = 2131165369; + // aapt resource value: 0x7f0700bb + public const int select_other_entry = 2131165371; // aapt resource value: 0x7f07005c public const int start_create = 2131165276; - // aapt resource value: 0x7f0700be - public const int start_create_import = 2131165374; + // aapt resource value: 0x7f0700c0 + public const int start_create_import = 2131165376; // aapt resource value: 0x7f07005a public const int start_open_file = 2131165274; @@ -1335,8 +1341,8 @@ namespace keepass2android // aapt resource value: 0x7f070003 public const int terrain = 2131165187; - // aapt resource value: 0x7f0700c0 - public const int text = 2131165376; + // aapt resource value: 0x7f0700c2 + public const int text = 2131165378; // aapt resource value: 0x7f070069 public const int textView = 2131165289; @@ -1608,47 +1614,47 @@ namespace keepass2android // aapt resource value: 0x7f080170 public const int CannotMoveGroupHere = 2131231088; + // aapt resource value: 0x7f0801a1 + public const int ChangeLog = 2131231137; + + // aapt resource value: 0x7f0801a0 + public const int ChangeLog_0_7 = 2131231136; + // aapt resource value: 0x7f08019e - public const int ChangeLog = 2131231134; + public const int ChangeLog_0_8 = 2131231134; // aapt resource value: 0x7f08019d - public const int ChangeLog_0_7 = 2131231133; - - // aapt resource value: 0x7f08019b - public const int ChangeLog_0_8 = 2131231131; - - // aapt resource value: 0x7f08019a - public const int ChangeLog_0_8_1 = 2131231130; - - // aapt resource value: 0x7f080199 - public const int ChangeLog_0_8_2 = 2131231129; - - // aapt resource value: 0x7f080198 - public const int ChangeLog_0_8_3 = 2131231128; - - // aapt resource value: 0x7f080197 - public const int ChangeLog_0_8_4 = 2131231127; - - // aapt resource value: 0x7f080196 - public const int ChangeLog_0_8_5 = 2131231126; - - // aapt resource value: 0x7f080195 - public const int ChangeLog_0_8_6 = 2131231125; - - // aapt resource value: 0x7f080194 - public const int ChangeLog_0_9 = 2131231124; - - // aapt resource value: 0x7f080193 - public const int ChangeLog_0_9_1 = 2131231123; - - // aapt resource value: 0x7f080192 - public const int ChangeLog_0_9_2 = 2131231122; + public const int ChangeLog_0_8_1 = 2131231133; // aapt resource value: 0x7f08019c - public const int ChangeLog_keptDonate = 2131231132; + public const int ChangeLog_0_8_2 = 2131231132; - // aapt resource value: 0x7f080191 - public const int ChangeLog_title = 2131231121; + // aapt resource value: 0x7f08019b + public const int ChangeLog_0_8_3 = 2131231131; + + // aapt resource value: 0x7f08019a + public const int ChangeLog_0_8_4 = 2131231130; + + // aapt resource value: 0x7f080199 + public const int ChangeLog_0_8_5 = 2131231129; + + // aapt resource value: 0x7f080198 + public const int ChangeLog_0_8_6 = 2131231128; + + // aapt resource value: 0x7f080197 + public const int ChangeLog_0_9 = 2131231127; + + // aapt resource value: 0x7f080196 + public const int ChangeLog_0_9_1 = 2131231126; + + // aapt resource value: 0x7f080195 + public const int ChangeLog_0_9_2 = 2131231125; + + // aapt resource value: 0x7f08019f + public const int ChangeLog_keptDonate = 2131231135; + + // aapt resource value: 0x7f080194 + public const int ChangeLog_title = 2131231124; // aapt resource value: 0x7f080040 public const int CheckForFileChangesOnSave_key = 2131230784; @@ -2484,8 +2490,8 @@ namespace keepass2android // aapt resource value: 0x7f0800b2 public const int list_size_title = 2131230898; - // aapt resource value: 0x7f080190 - public const int loading = 2131231120; + // aapt resource value: 0x7f080193 + public const int loading = 2131231123; // aapt resource value: 0x7f0800b4 public const int loading_database = 2131230900; @@ -2607,12 +2613,21 @@ namespace keepass2android // aapt resource value: 0x7f0800d3 public const int open_recent = 2131230931; + // aapt resource value: 0x7f080190 + public const int otp_discarded_because_no_db = 2131231120; + + // aapt resource value: 0x7f080191 + public const int otp_discarded_no_space = 2131231121; + // aapt resource value: 0x7f08018d public const int otp_explanation = 2131231117; // aapt resource value: 0x7f08018e public const int otp_hint = 2131231118; + // aapt resource value: 0x7f080192 + public const int otps_pending = 2131231122; + // aapt resource value: 0x7f0800d6 public const int pass_filename = 2131230934; diff --git a/src/keepass2android/Resources/layout-v14/password.xml b/src/keepass2android/Resources/layout-v14/password.xml index 869e5264..c4ec4aac 100644 --- a/src/keepass2android/Resources/layout-v14/password.xml +++ b/src/keepass2android/Resources/layout-v14/password.xml @@ -104,11 +104,22 @@ android:layout_height="wrap_content" android:orientation="vertical" > -