/* 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 3 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 . */ using System; using System.Collections.Generic; using System.Linq; using Android.App; using Android.Content; using Android.OS; using Android.Provider; using Android.Views; using Android.Widget; using Android.Preferences; using Java.IO; using KeePassLib.Utility; using KeePassLib; using Android.Text; using KeePassLib.Security; using Android.Content.PM; using System.IO; using System.Globalization; using Android.Util; using File = System.IO.File; using Uri = Android.Net.Uri; namespace keepass2android { [Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_ActionBar")] public class EntryEditActivity : LockCloseActivity { public const String KeyEntry = "entry"; public const String KeyParent = "parent"; public const int ResultOkIconPicker = (int)Result.FirstUser + 1000; const string IntentContinueWithEditing = "ContinueWithEditing"; EntryEditActivityState State { get { return App.Kp2a.EntryEditActivityState; } } public static void Launch(Activity act, PwEntry pw, AppTask appTask) { Intent i = new Intent(act, typeof(EntryEditActivity)); i.PutExtra(KeyEntry, pw.Uuid.ToHexString()); appTask.ToIntent(i); act.StartActivityForResult(i, 0); } public static void Launch(Activity act, PwGroup pw, AppTask appTask) { Intent i = new Intent(act, typeof(EntryEditActivity)); PwGroup parent = pw; i.PutExtra(KeyParent, parent.Uuid.ToHexString()); appTask.ToIntent(i); act.StartActivityForResult(i, 0); } bool _closeForReload; AppTask _appTask; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); if (LastNonConfigurationInstance != null) { //bug in Mono for Android or whatever: after config change the extra fields are wrong // -> reload: Reload(); return; } _appTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); SetContentView(Resource.Layout.entry_edit); _closeForReload = false; // Likely the app has been killed exit the activity if (!App.Kp2a.DatabaseIsUnlocked) { Finish(); return; } if (Intent.GetBooleanExtra(IntentContinueWithEditing, false)) { //property "State" will return the state } 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)); Intent i = Intent; String uuidBytes = i.GetStringExtra(KeyEntry); PwUuid entryId = PwUuid.Zero; if (uuidBytes != null) entryId = new PwUuid(MemUtil.HexStringToByteArray(uuidBytes)); State.ParentGroup = null; if (entryId.Equals(PwUuid.Zero)) { String groupId = i.GetStringExtra(KeyParent); State.ParentGroup = db.KpDatabase.RootGroup.FindGroup(new PwUuid(MemUtil.HexStringToByteArray(groupId)), true); State.EntryInDatabase = new PwEntry(true, true); State.EntryInDatabase.Strings.Set(PwDefs.UserNameField, new ProtectedString( db.KpDatabase.MemoryProtection.ProtectUserName, db.KpDatabase.DefaultUserName)); /*KPDesktop * ProtectedString psAutoGen; PwGenerator.Generate(out psAutoGen, Program.Config.PasswordGenerator.AutoGeneratedPasswordsProfile, null, Program.PwGeneratorPool); psAutoGen = psAutoGen.WithProtection(pwDb.MemoryProtection.ProtectPassword); pwe.Strings.Set(PwDefs.PasswordField, psAutoGen); int nExpireDays = Program.Config.Defaults.NewEntryExpiresInDays; if(nExpireDays >= 0) { pwe.Expires = true; pwe.ExpiryTime = DateTime.Now.AddDays(nExpireDays); }*/ if ((State.ParentGroup.IconId != PwIcon.Folder) && (State.ParentGroup.IconId != PwIcon.FolderOpen) && (State.ParentGroup.IconId != PwIcon.FolderPackage)) { State.EntryInDatabase.IconId = State.ParentGroup.IconId; // Inherit icon from group } else State.EntryInDatabase.IconId = PwIcon.Key; State.EntryInDatabase.CustomIconUuid = State.ParentGroup.CustomIconUuid; /* * KPDesktop if(strDefaultSeq.Length == 0) { PwGroup pg = m_pwEntry.ParentGroup; if(pg != null) { strDefaultSeq = pg.GetAutoTypeSequenceInherited(); if(strDefaultSeq.Length == 0) { if(PwDefs.IsTanEntry(m_pwEntry)) strDefaultSeq = PwDefs.DefaultAutoTypeSequenceTan; else strDefaultSeq = PwDefs.DefaultAutoTypeSequence; } } }*/ _appTask.PrepareNewEntry(State.EntryInDatabase); State.IsNew = true; State.EntryModified = true; } else { System.Diagnostics.Debug.Assert(entryId != null); State.EntryInDatabase = db.Entries [entryId]; State.IsNew = false; } State.Entry = State.EntryInDatabase.CloneDeep(); } if (!State.EntryModified) SetResult(KeePass.ExitNormal); else SetResult(KeePass.ExitRefreshTitle); FillData(); View scrollView = FindViewById(Resource.Id.entry_scroll); scrollView.ScrollBarStyle = ScrollbarStyles.InsideInset; ImageButton iconButton = (ImageButton)FindViewById(Resource.Id.icon_button); if (State.SelectedIcon) { App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iconButton, Resources, App.Kp2a.GetDb().KpDatabase, (PwIcon)State.SelectedIconId, State.SelectedCustomIconId, false); } iconButton.Click += (sender, evt) => { UpdateEntryFromUi(State.Entry); IconPickerActivity.Launch(this); }; // Generate password button FindViewById(Resource.Id.generate_button).Click += (sender, e) => { UpdateEntryFromUi(State.Entry); GeneratePasswordActivity.Launch(this); }; // Save button //SupportActionBar.SetCustomView(Resource.Layout.SaveButton); if (State.IsNew) { SupportActionBar.Title = GetString(Resource.String.add_entry); } else { SupportActionBar.Title = GetString(Resource.String.edit_entry); } SupportActionBar.SetDisplayHomeAsUpEnabled(true); SupportActionBar.SetHomeButtonEnabled(true); // Respect mask password setting MakePasswordVisibleOrHidden(); ImageButton btnTogglePassword = (ImageButton)FindViewById(Resource.Id.toggle_password); btnTogglePassword.Click += (sender, e) => { State.ShowPassword = !State.ShowPassword; MakePasswordVisibleOrHidden(); }; Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop; Android.Graphics.Color color = new Android.Graphics.Color (189,189,189); btnTogglePassword.SetColorFilter (color, mMode); Button addButton = (Button) FindViewById(Resource.Id.add_advanced); addButton.Visibility = ViewStates.Visible; addButton.Click += (sender, e) => { LinearLayout container = (LinearLayout) FindViewById(Resource.Id.advanced_container); KeyValuePair pair = new KeyValuePair("" , new ProtectedString(true, "")); View ees = CreateExtraStringView(pair); container.AddView(ees); State.EntryModified = true; TextView keyView = (TextView) ees.FindViewById(Resource.Id.title); keyView.RequestFocus(); }; SetAddExtraStringEnabled(); ((CheckBox)FindViewById(Resource.Id.entry_expires_checkbox)).CheckedChange += (sender, e) => { State.Entry.Expires = e.IsChecked; if (e.IsChecked) { if (State.Entry.ExpiryTime < DateTime.Now) State.Entry.ExpiryTime = DateTime.Now; } UpdateExpires(); State.EntryModified = true; }; } private void SetAddExtraStringEnabled() { if (!App.Kp2a.GetDb().DatabaseFormat.CanHaveCustomFields) ((Button)FindViewById(Resource.Id.add_advanced)).Visibility = ViewStates.Gone; } private void MakePasswordVisibleOrHidden() { TextView password = (TextView) FindViewById(Resource.Id.entry_password); TextView confpassword = (TextView) FindViewById(Resource.Id.entry_confpassword); if (State.ShowPassword) { password.InputType = InputTypes.ClassText | InputTypes.TextVariationVisiblePassword; confpassword.Visibility = ViewStates.Gone; } else { password.InputType = InputTypes.ClassText | InputTypes.TextVariationPassword; confpassword.Visibility = ViewStates.Visible; } } void SaveEntry() { Database db = App.Kp2a.GetDb(); EntryEditActivity act = this; if (!ValidateBeforeSaving()) return; PwEntry initialEntry = State.EntryInDatabase.CloneDeep(); PwEntry newEntry = State.EntryInDatabase; //Clone history and re-assign: newEntry.History = newEntry.History.CloneDeep(); //Based on KeePass Desktop bool bCreateBackup = (!State.IsNew); if(bCreateBackup) newEntry.CreateBackup(null); if (State.SelectedIcon) { newEntry.IconId = State.SelectedIconId; newEntry.CustomIconUuid = State.SelectedCustomIconId; } //else the State.EntryInDatabase.Icon /* KPDesktop if(m_cbCustomForegroundColor.Checked) newEntry.ForegroundColor = m_clrForeground; else newEntry.ForegroundColor = Color.Empty; if(m_cbCustomBackgroundColor.Checked) newEntry.BackgroundColor = m_clrBackground; else newEntry.BackgroundColor = Color.Empty; */ UpdateEntryFromUi(newEntry); newEntry.Binaries = State.Entry.Binaries; newEntry.Expires = State.Entry.Expires; if (newEntry.Expires) { newEntry.ExpiryTime = State.Entry.ExpiryTime; } newEntry.Touch(true, false); // Touch *after* backup StrUtil.NormalizeNewLines(newEntry.Strings, true); bool bUndoBackup = false; PwCompareOptions cmpOpt = (PwCompareOptions.NullEmptyEquivStd | PwCompareOptions.IgnoreTimes); if(bCreateBackup) cmpOpt |= PwCompareOptions.IgnoreLastBackup; if(newEntry.EqualsEntry(initialEntry, cmpOpt, MemProtCmpMode.CustomOnly)) { // No modifications at all => restore last mod time and undo backup newEntry.LastModificationTime = initialEntry.LastModificationTime; bUndoBackup = bCreateBackup; } else if(bCreateBackup) { // If only history items have been modified (deleted) => undo // backup, but without restoring the last mod time PwCompareOptions cmpOptNh = (cmpOpt | PwCompareOptions.IgnoreHistory); if(newEntry.EqualsEntry(initialEntry, cmpOptNh, MemProtCmpMode.CustomOnly)) bUndoBackup = true; } if(bUndoBackup) newEntry.History.RemoveAt(newEntry.History.UCount - 1); newEntry.MaintainBackups(db.KpDatabase); //if ( newEntry.Strings.ReadSafe (PwDefs.TitleField).Equals(State.Entry.Strings.ReadSafe (PwDefs.TitleField)) ) { // SetResult(KeePass.EXIT_REFRESH); //} else { //it's safer to always update the title as we might add further information in the title like expiry etc. SetResult(KeePass.ExitRefreshTitle); //} RunnableOnFinish runnable; ActionOnFinish closeOrShowError = new ActionOnFinish((success, message) => { if (success) { Finish(); } else { OnFinish.DisplayMessage(this, message); } }); ActionOnFinish afterAddEntry = new ActionOnFinish((success, message) => { if (success) _appTask.AfterAddNewEntry(this, newEntry); },closeOrShowError); if ( State.IsNew ) { runnable = AddEntry.GetInstance(this, App.Kp2a, newEntry, State.ParentGroup, afterAddEntry); } else { runnable = new UpdateEntry(this, App.Kp2a, initialEntry, newEntry, closeOrShowError); } ProgressTask pt = new ProgressTask(App.Kp2a, act, runnable); pt.Run(); } void UpdateEntryFromUi(PwEntry entry) { Database db = App.Kp2a.GetDb(); EntryEditActivity act = this; entry.Strings.Set(PwDefs.TitleField, new ProtectedString(db.KpDatabase.MemoryProtection.ProtectTitle, Util.GetEditText(act, Resource.Id.entry_title))); entry.Strings.Set(PwDefs.UserNameField, new ProtectedString(db.KpDatabase.MemoryProtection.ProtectUserName, Util.GetEditText(act, Resource.Id.entry_user_name))); String pass = Util.GetEditText(act, Resource.Id.entry_password); byte[] password = StrUtil.Utf8.GetBytes(pass); entry.Strings.Set(PwDefs.PasswordField, new ProtectedString(db.KpDatabase.MemoryProtection.ProtectPassword, password)); MemUtil.ZeroByteArray(password); entry.Strings.Set(PwDefs.UrlField, new ProtectedString(db.KpDatabase.MemoryProtection.ProtectUrl, Util.GetEditText(act, Resource.Id.entry_url))); entry.Strings.Set(PwDefs.NotesField, new ProtectedString(db.KpDatabase.MemoryProtection.ProtectNotes, Util.GetEditText(act, Resource.Id.entry_comment))); // Validate expiry date DateTime newExpiry = new DateTime(); if ((State.Entry.Expires) && (!DateTime.TryParse( Util.GetEditText(this,Resource.Id.entry_expires), out newExpiry))) { //ignore here } else { State.Entry.ExpiryTime = newExpiry; } // Delete all non standard strings var keys = entry.Strings.GetKeys(); foreach (String key in keys) if (PwDefs.IsStandardField(key) == false) entry.Strings.Remove(key); LinearLayout container = (LinearLayout) FindViewById(Resource.Id.advanced_container); for (int index = 0; index < container.ChildCount; index++) { View view = container.GetChildAt(index); TextView keyView = (TextView)view.FindViewById(Resource.Id.title); String key = keyView.Text; if (String.IsNullOrEmpty(key)) continue; TextView valueView = (TextView)view.FindViewById(Resource.Id.value); String value = valueView.Text; bool protect = ((CheckBox) view.FindViewById(Resource.Id.protection)).Checked; entry.Strings.Set(key, new ProtectedString(protect, value)); } entry.OverrideUrl = Util.GetEditText(this,Resource.Id.entry_override_url); List vNewTags = StrUtil.StringToTags(Util.GetEditText(this,Resource.Id.entry_tags)); entry.Tags.Clear(); foreach(string strTag in vNewTags) entry.AddTag(strTag); /*KPDesktop m_atConfig.Enabled = m_cbAutoTypeEnabled.Checked; m_atConfig.ObfuscationOptions = (m_cbAutoTypeObfuscation.Checked ? AutoTypeObfuscationOptions.UseClipboard : AutoTypeObfuscationOptions.None); SaveDefaultSeq(); newEntry.AutoType = m_atConfig; */ } public String GetFileName(Uri uri) { String result = null; if (uri.Scheme.Equals("content")) { var cursor = ContentResolver.Query(uri, null, null, null, null); try { if (cursor != null && cursor.MoveToFirst()) { result = cursor.GetString(cursor.GetColumnIndex(OpenableColumns.DisplayName)); } } finally { if (cursor != null) cursor.Close(); } } if (result == null) { result = uri.Path; int cut = result.LastIndexOf('/'); if (cut != -1) { result = result.Substring(cut + 1); } cut = result.LastIndexOf('?'); if (cut != -1) { result = result.Substring(0, cut); } } return result; } void AddBinaryOrAsk(Uri filename) { string strItem = GetFileName(filename); if (String.IsNullOrEmpty(strItem)) strItem = "attachment.bin"; if(State.Entry.Binaries.Get(strItem) != null) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.SetTitle(GetString(Resource.String.AskOverwriteBinary_title)); builder.SetMessage(GetString(Resource.String.AskOverwriteBinary)); builder.SetPositiveButton(GetString(Resource.String.AskOverwriteBinary_yes), (dlgSender, dlgEvt) => { AddBinary(filename, true); }); builder.SetNegativeButton(GetString(Resource.String.AskOverwriteBinary_no), (dlgSender, dlgEvt) => { AddBinary(filename, false); }); builder.SetNeutralButton(GetString(Android.Resource.String.Cancel), (dlgSender, dlgEvt) => {}); Dialog dialog = builder.Create(); dialog.Show(); } else AddBinary(filename, true); } void AddBinary(Uri filename, bool overwrite) { string strItem = GetFileName(filename); if (!overwrite) { string strFileName = UrlUtil.StripExtension(strItem); string strExtension = "." + UrlUtil.GetExtension(strItem); int nTry = 0; while(true) { string strNewName = strFileName + nTry.ToString(CultureInfo.InvariantCulture) + strExtension; if(State.Entry.Binaries.Get(strNewName) == null) { strItem = strNewName; break; } ++nTry; } } try { byte[] vBytes = null; try { //Android standard way to read the contents (content or file scheme) vBytes = ReadFully(ContentResolver.OpenInputStream(filename)); } catch (Exception) { //if standard way fails, try to read as a file vBytes = File.ReadAllBytes(filename.Path); } ProtectedBinary pb = new ProtectedBinary(false, vBytes); State.Entry.Binaries.Set(strItem, pb); } catch(Exception exAttach) { Toast.MakeText(this, GetString(Resource.String.AttachFailed)+" "+exAttach.Message, ToastLength.Long).Show(); } State.EntryModified = true; PopulateBinaries(); } public static byte[] ReadFully(Stream input) { byte[] buffer = new byte[16 * 1024]; using (MemoryStream ms = new MemoryStream()) { int read; while ((read = input.Read(buffer, 0, buffer.Length)) > 0) { ms.Write(buffer, 0, read); } return ms.ToArray(); } } protected override void OnSaveInstanceState(Bundle outState) { base.OnSaveInstanceState(outState); _appTask.ToBundle(outState); } public override void OnBackPressed() { if (State.EntryModified == false) { base.OnBackPressed(); } else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.SetTitle(GetString(Resource.String.AskDiscardChanges_title)); builder.SetMessage(GetString(Resource.String.AskDiscardChanges)); builder.SetPositiveButton(GetString(Android.Resource.String.Yes), (dlgSender, dlgEvt) => { Finish(); }); builder.SetNegativeButton(GetString(Android.Resource.String.No), (dlgSender, dlgEvt) => { }); Dialog dialog = builder.Create(); dialog.Show(); } } public void Reload() { //this reload ìs necessary to overcome a strange problem with the extra string fields which get lost //somehow after re-creating the activity. Maybe a Mono for Android bug? Intent intent = Intent; intent.PutExtra(IntentContinueWithEditing, true); //OverridePendingTransition(0, 0); intent.AddFlags(ActivityFlags.NoAnimation | ActivityFlags.ForwardResult); _closeForReload = true; SetResult(KeePass.ExitRefreshTitle); //probably the entry will be modified -> let the EditActivity refresh to be safe Finish(); //OverridePendingTransition(0, 0); StartActivity(intent); } protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { switch (resultCode) { case (Result)ResultOkIconPicker: State.SelectedIconId = (PwIcon) data.Extras.GetInt(IconPickerActivity.KeyIconId,(int)PwIcon.Key); State.SelectedCustomIconId = PwUuid.Zero; String customIconIdString = data.Extras.GetString(IconPickerActivity.KeyCustomIconId); if (!String.IsNullOrEmpty(customIconIdString)) State.SelectedCustomIconId = new PwUuid(MemUtil.HexStringToByteArray(customIconIdString)); State.SelectedIcon = true; State.EntryModified = true; Reload(); break; case KeePass.ResultOkPasswordGenerator: String generatedPassword = data.GetStringExtra("keepass2android.password.generated_password"); byte[] password = StrUtil.Utf8.GetBytes(generatedPassword); State.Entry.Strings.Set(PwDefs.PasswordField, new ProtectedString(App.Kp2a.GetDb().KpDatabase.MemoryProtection.ProtectPassword, password)); MemUtil.ZeroByteArray(password); State.EntryModified = true; Reload(); break; case Result.Ok: if (requestCode == Intents.RequestCodeFileBrowseForBinary) { Uri uri = data.Data; if (data.Data == null) { string s = Util.GetFilenameFromInternalFileChooser(data, this); if (s == null) { Toast.MakeText(this, "No URI retrieved.", ToastLength.Short).Show(); return; } uri = Uri.Parse(s); } AddBinaryOrAsk(uri); } Reload(); break; case Result.Canceled: Reload(); break; } } void PopulateBinaries() { ViewGroup binariesGroup = (ViewGroup)FindViewById(Resource.Id.binaries); binariesGroup.RemoveAllViews(); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent); foreach (KeyValuePair pair in State.Entry.Binaries.OrderBy(p => p.Key) ) { String key = pair.Key; String label = key; if ((String.IsNullOrEmpty(label) || (!App.Kp2a.GetDb().DatabaseFormat.SupportsAttachmentKeys))) { label = ""; } //Button binaryButton = new Button(this, null, Resource.Style.EditEntryButton) {Text = label}; Button binaryButton = (Button)LayoutInflater.Inflate(Resource.Layout.EntryEditButtonDelete, null); binaryButton.Text = label; //binaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuDelete),null, null, null); binaryButton.Click += (sender, e) => { State.EntryModified = true; State.Entry.Binaries.Remove(key); PopulateBinaries(); }; binariesGroup.AddView(binaryButton,layoutParams); } //Button addBinaryButton = new Button(this, null, Resource.Style.EditEntryButton ) {Text = GetString(Resource.String.add_binary)}; //addBinaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuAdd) , null, null, null); Button addBinaryButton = (Button)LayoutInflater.Inflate(Resource.Layout.EntryEditButtonAdd, null); addBinaryButton.Text = GetString(Resource.String.add_binary); addBinaryButton.Enabled = true; if (!App.Kp2a.GetDb().DatabaseFormat.CanHaveMultipleAttachments) addBinaryButton.Enabled = !State.Entry.Binaries.Any(); addBinaryButton.Click += (sender, e) => { Util.ShowBrowseDialog(this, Intents.RequestCodeFileBrowseForBinary, false, false); }; binariesGroup.AddView(addBinaryButton,layoutParams); var binariesLabel = FindViewById(Resource.Id.entry_binaries_label); if (binariesLabel != null) binariesLabel.Visibility = State.Entry.Binaries.UCount > 0 ? ViewStates.Visible : ViewStates.Gone; } public override bool OnPrepareOptionsMenu(IMenu menu) { Util.PrepareDonateOptionMenu(menu, this); return base.OnPrepareOptionsMenu(menu); } public override bool OnCreateOptionsMenu(IMenu menu) { base.OnCreateOptionsMenu(menu); MenuInflater inflater = MenuInflater; inflater.Inflate(Resource.Menu.entry_edit, menu); return true; } public override bool OnOptionsItemSelected(IMenuItem item) { switch ( item.ItemId ) { case Resource.Id.menu_save: SaveEntry(); return true; case Resource.Id.menu_cancel: Finish(); return true; case Android.Resource.Id.Home: OnBackPressed(); return true; default: return base.OnOptionsItemSelected(item); } } void UpdateExpires() { if (State.Entry.Expires) { PopulateText(Resource.Id.entry_expires, getDateTime(State.Entry.ExpiryTime)); } else { PopulateText(Resource.Id.entry_expires, GetString(Resource.String.never)); } ((CheckBox)FindViewById(Resource.Id.entry_expires_checkbox)).Checked = State.Entry.Expires; FindViewById(Resource.Id.entry_expires).Enabled = State.Entry.Expires; } /* * TODO required?? * * public override Java.Lang.Object OnRetainNonConfigurationInstance() { UpdateEntryFromUi(State.Entry); return this; }*/ RelativeLayout CreateExtraStringView(KeyValuePair pair) { RelativeLayout ees = (RelativeLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section, null); ((TextView)ees.FindViewById(Resource.Id.title)).Text = pair.Key; ((TextView)ees.FindViewById(Resource.Id.title)).TextChanged += (sender, e) => State.EntryModified = true; ((TextView)ees.FindViewById(Resource.Id.value)).Text = pair.Value.ReadString(); ((TextView)ees.FindViewById(Resource.Id.value)).TextChanged += (sender, e) => State.EntryModified = true; ((CheckBox)ees.FindViewById(Resource.Id.protection)).Checked = pair.Value.IsProtected; //ees.FindViewById(Resource.Id.edit_extra).Click += (sender, e) => DeleteAdvancedString((View)sender); ees.FindViewById(Resource.Id.edit_extra).Click += (sender, e) => EditAdvancedString((View)sender); return ees; } private void EditAdvancedString(View sender) { AlertDialog.Builder builder = new AlertDialog.Builder(this); View dlgView = LayoutInflater.Inflate(Resource.Layout.edit_extra_string_dialog, null); builder.SetView(dlgView); builder.SetNegativeButton(Android.Resource.String.Cancel, (o, args) => { }); builder.SetPositiveButton(Android.Resource.String.Ok, (o, args) => { CopyFieldFromExtraDialog(sender, o, Resource.Id.title); CopyFieldFromExtraDialog(sender, o, Resource.Id.value); CopyCheckboxFromExtraDialog(sender, o, Resource.Id.protection); }); Dialog dialog = builder.Create(); //setup delete button: var deleteButton = dlgView.FindViewById