context: /*
  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 <http://www.gnu.org/licenses/>.
    */
  
  using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Linq;
  using System.Threading;
  using Android.App;
  using Android.Content;
  using Android.OS;
  using Android.Runtime;
  using Android.Text;
  using Android.Views;
  using Android.Widget;
  using Android.Preferences;
  using Android.Text.Method;
  using System.Globalization;
  using System.IO;
  using System.Net;
  using Android.Content.PM;
  using Android.Webkit;
  using Android.Graphics;
  using keepass2android.EntryActivityClasses;
  using KeePassLib;
  using KeePassLib.Security;
  using KeePassLib.Utility;
  using Keepass2android.Pluginsdk;
  using keepass2android.Io;
  using KeePass.DataExchange;
  using KeePass.Util.Spr;
  using KeePassLib.Interfaces;
  using KeePassLib.Serialization;
  using PluginTOTP;
  using File = Java.IO.File;
  using Uri = Android.Net.Uri;
  
  namespace keepass2android
  {
      public class ExportBinaryProcessManager : FileSaveProcessManager
      {
          private readonly string _binaryToSave;
  
          public ExportBinaryProcessManager(int requestCode, Activity activity, string key) : base(requestCode, activity)
          {
              _binaryToSave = key;
          }
  
          public ExportBinaryProcessManager(int requestCode, EntryActivity activity, Bundle savedInstanceState) : base(requestCode, activity)
          {
              _binaryToSave = savedInstanceState.GetString("BinaryToSave", null);
          }
  
          protected override void SaveFile(IOConnectionInfo ioc)
          {
              var task = new EntryActivity.WriteBinaryTask(_activity, App.Kp2a, new ActionOnFinish(_activity, (success, message, activity) =>
                  {
                      if (!success)
                          Toast.MakeText(activity, message, ToastLength.Long).Show();
                  }
              ), ((EntryActivity)_activity).Entry.Binaries.Get(_binaryToSave), ioc);
              ProgressTask pt = new ProgressTask(App.Kp2a, _activity, task);
              pt.Run();
  
          }
  
          public override void OnSaveInstanceState(Bundle outState)
          {
              outState.PutString("BinaryToSave", _binaryToSave);
          }
          
  
      }
  
  
  	[Activity (Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden,
          Theme = "@style/MyTheme_ActionBar")]
  	public class EntryActivity : LockCloseActivity 
  	{
  		public const String KeyEntry = "entry";
          public const String KeyRefreshPos = "refresh_pos";
          public const String KeyEntryHistoryIndex = "entry_history_index";
  		public const String KeyActivateKeyboard = "activate_keyboard";
  		public const String KeyGroupFullPath = "groupfullpath_key";
  
  	    public const int requestCodeBinaryFilename = 42376;
          public const int requestCodeSelFileStorageForWriteAttachment = 42377;
  	    
  
  
          public static void Launch(Activity act, PwEntry pw, int pos, AppTask appTask, ActivityFlags? flags = null, int historyIndex=-1)
  		{
  			Intent i = new Intent(act, typeof(EntryActivity));
  
              var db = App.Kp2a.FindDatabaseForElement(pw);
  			i.PutExtra(KeyEntry, new ElementAndDatabaseId(db, pw).FullId);
  			i.PutExtra(KeyRefreshPos, pos);
              i.PutExtra(KeyEntryHistoryIndex, historyIndex);
  
  		    if (App.Kp2a.CurrentDb != db)
  		    {
  		        App.Kp2a.CurrentDb = db;
  		    }
  
  			if (flags != null)
  				i.SetFlags((ActivityFlags) flags);
  
  			appTask.ToIntent(i);
  			if (flags != null && (((ActivityFlags) flags) | ActivityFlags.ForwardResult) == ActivityFlags.ForwardResult)
  				act.StartActivity(i);
  			else
  				act.StartActivityForResult(i, 0);
  		}
  
  		public EntryActivity (IntPtr javaReference, JniHandleOwnership transfer)
  			: base(javaReference, transfer)
  		{
  			
  		}
  
  		public EntryActivity()
  		{
  			_activityDesign = new ActivityDesign(this);
  		}
  
  		//this is the entry we display. Note that it might be an element from a History list in case _historyIndex >= 0
  	    public PwEntry Entry;
  		//if _historyIndex >=0, _historyParentEntry stores the PwEntry which contains the history entry "Entry"
          private PwEntry _historyParentEntry;
  
  		private PasswordFont _passwordFont = new PasswordFont();
  
  		internal Dictionary<TextView /*the "ProtectedField" of the ProtectedTextviewGroup*/, bool> _showPassword = new Dictionary<TextView, bool>();
  		private int _pos;
  
          private AppTask _appTask;
          private AppTask AppTask
          {
              get { return _appTask; }
              set
              {
                  _appTask = value;
                  Kp2aLog.LogTask(value, MyDebugName);
              }
          }
  
  		struct ProtectedTextviewGroup
  	    {
  	        public TextView ProtectedField;
  	        public TextView VisibleProtectedField;
          }
  
  		private List<ProtectedTextviewGroup> _protectedTextViews;
  		private IMenu _menu;
  
  		private readonly Dictionary<string, List<IPopupMenuItem>> _popupMenuItems =
  			new Dictionary<string, List<IPopupMenuItem>>();
  
  		private readonly Dictionary<string, IStringView> _stringViews = new Dictionary<string, IStringView>();
  		private readonly List<PluginMenuOption> _pendingMenuOptions = new List<PluginMenuOption>();
  		
  		//make sure _timer doesn't go out of scope:
  		private Timer _timer;
  		private PluginActionReceiver _pluginActionReceiver;
  		private PluginFieldReceiver _pluginFieldReceiver;
  		private ActivityDesign _activityDesign;
  	    
  
  
  	    protected void SetEntryView()
  		{
  			SetContentView(Resource.Layout.entry_view);
  		}
  
  		protected void SetupEditButtons() {
  			View edit =  FindViewById(Resource.Id.entry_edit);
  			if (App.Kp2a.CurrentDb.CanWrite && _historyIndex < 0)
  			{
  				edit.Visibility = ViewStates.Visible;
  				edit.Click += (sender, e) =>
  				{
  					EntryEditActivity.Launch(this, Entry, AppTask);
  				};	
  			}
  			else
  			{
  				edit.Visibility = ViewStates.Gone;
  			}
  			
  		}
  
  		
  		private class PluginActionReceiver : BroadcastReceiver
  		{
  			private readonly EntryActivity _activity;
  
  			public PluginActionReceiver(EntryActivity activity)
  			{
  				_activity = activity;
  			}
  
  			public override void OnReceive(Context context, Intent intent)
  			{
  				var pluginPackage = intent.GetStringExtra(Strings.ExtraSender);
  				if (new PluginDatabase(context).IsValidAccessToken(pluginPackage,
  				                                                   intent.GetStringExtra(Strings.ExtraAccessToken),
  				                                                   Strings.ScopeCurrentEntry))
  				{
  					if (intent.GetStringExtra(Strings.ExtraEntryId) != _activity.Entry.Uuid.ToHexString())
  					{
  						Kp2aLog.Log("received action for wrong entry " + intent.GetStringExtra(Strings.ExtraEntryId));
  						return;
  					}
  					_activity.AddPluginAction(pluginPackage,
  					                          intent.GetStringExtra(Strings.ExtraFieldId),
  											  intent.GetStringExtra(Strings.ExtraActionId),
  					                          intent.GetStringExtra(Strings.ExtraActionDisplayText),
  					                          intent.GetIntExtra(Strings.ExtraActionIconResId, -1),
  					                          intent.GetBundleExtra(Strings.ExtraActionData));
  				}
  				else
  				{
  					Kp2aLog.Log("received invalid request. Plugin not authorized.");
  				}
  			}
  		}
  
  		private class PluginFieldReceiver : BroadcastReceiver
  		{
  			private readonly EntryActivity _activity;
  
  			public PluginFieldReceiver(EntryActivity activity)
  			{
  				_activity = activity;
  			}
  
  			public override void OnReceive(Context context, Intent intent)
  			{
  				if (intent.GetStringExtra(Strings.ExtraEntryId) != _activity.Entry.Uuid.ToHexString())
  				{
  					Kp2aLog.Log("received field for wrong entry " + intent.GetStringExtra(Strings.ExtraEntryId));
  					return;
  				}
  				if (!new PluginDatabase(context).IsValidAccessToken(intent.GetStringExtra(Strings.ExtraSender),
  				                                                    intent.GetStringExtra(Strings.ExtraAccessToken),
  				                                                    Strings.ScopeCurrentEntry))
  				{
  					Kp2aLog.Log("received field with invalid access token from " + intent.GetStringExtra(Strings.ExtraSender));
  					return;
  				}
  				string key = intent.GetStringExtra(Strings.ExtraFieldId);
  				string value = intent.GetStringExtra(Strings.ExtraFieldValue);
  				bool isProtected = intent.GetBooleanExtra(Strings.ExtraFieldProtected, false);
  				_activity.SetPluginField(key, value, isProtected);
  			}
  		}
  
  		private void SetPluginField(string key, string value, bool isProtected)
  		{
  			//update or add the string view:
  			IStringView existingField;
  			if (_stringViews.TryGetValue(key, out existingField))
  			{
  				existingField.Text = value;
  			}
  			else
  			{
  				ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings);
  				var view = CreateExtraSection(key, value, isProtected);
  				extraGroup.AddView(view.View);
  			}
  
  			//update the Entry output in the App database and notify the CopyToClipboard service
  
  		    if (App.Kp2a.LastOpenedEntry != null)
  		    {
  		        App.Kp2a.LastOpenedEntry.OutputStrings.Set(key, new ProtectedString(isProtected, value));
  		        Intent updateKeyboardIntent = new Intent(this, typeof(CopyToClipboardService));
  		        updateKeyboardIntent.SetAction(Intents.UpdateKeyboard);
  		        updateKeyboardIntent.PutExtra(KeyEntry, new ElementAndDatabaseId(App.Kp2a.CurrentDb, Entry).FullId);
  		        StartService(updateKeyboardIntent);
  
  		        //notify plugins
  		        NotifyPluginsOnModification(Strings.PrefixString + key);
  		    }
  		}
  
  		private void AddPluginAction(string pluginPackage, string fieldId, string popupItemId, string displayText, int iconId, Bundle bundleExtra)
  		{
  			if (fieldId != null)
  			{
  				try
  				{
  					if (!_popupMenuItems.ContainsKey(fieldId))
  					{
  						Kp2aLog.Log("Did not find field with key " + fieldId);
  						return;
  					}
  					//create a new popup item for the plugin action:
  					var newPopup = new PluginPopupMenuItem(this, pluginPackage, fieldId, popupItemId, displayText, iconId, bundleExtra);
  					//see if we already have a popup item for this field with the same item id
  					var popupsForField = _popupMenuItems[fieldId];
  					var popupItemPos = popupsForField.FindIndex(0,
  															item =>
  															(item is PluginPopupMenuItem) &&
  															((PluginPopupMenuItem)item).PopupItemId == popupItemId);
  
  					//replace existing or add
  					if (popupItemPos >= 0)
  					{
  						popupsForField[popupItemPos] = newPopup;
  					}
  					else
  					{
  						popupsForField.Add(newPopup);
  					}
  				}
  				catch (Exception e)
  				{
  					Kp2aLog.LogUnexpectedError(e);
  				}
  				
  			}
  			else
  			{
  				//we need to add an option to the  menu.
  				//As it is not sure that OnCreateOptionsMenu was called yet, we cannot access _menu without a check:
  
  				Intent i = new Intent(Strings.ActionEntryActionSelected);
  				i.SetPackage(pluginPackage);
  				i.PutExtra(Strings.ExtraActionData, bundleExtra);
  				i.PutExtra(Strings.ExtraSender, PackageName);
  				PluginHost.AddEntryToIntent(i, App.Kp2a.LastOpenedEntry);
  
  				var menuOption = new PluginMenuOption()
  					{
  						DisplayText = displayText,
  						Icon = PackageManager.GetResourcesForApplication(pluginPackage).GetDrawable(iconId),
  						Intent = i
  					};
  
  				if (_menu != null)
  				{
  					AddMenuOption(menuOption);
  				}
  				else
  				{
  					lock (_pendingMenuOptions)
  					{
  						_pendingMenuOptions.Add(menuOption);
  					}
  
  				}
  
  
  			}
  		}
  
  		private void AddMenuOption(PluginMenuOption menuOption)
  		{
  			var menuItem = _menu.Add(menuOption.DisplayText);
  			menuItem.SetIcon(menuOption.Icon);
  			menuItem.SetIntent(menuOption.Intent);
  		}
  
  		
  
  
  		protected override void OnCreate(Bundle savedInstanceState)
  		{
  		    if (savedInstanceState != null)
  		    {
  		        _exportBinaryProcessManager =
  		            new ExportBinaryProcessManager(requestCodeSelFileStorageForWriteAttachment, this, savedInstanceState);
  
  		    }
  
  		    ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
  
  			long usageCount = prefs.GetLong(GetString(Resource.String.UsageCount_key), 0);
  
  			ISharedPreferencesEditor edit = prefs.Edit();
  			edit.PutLong(GetString(Resource.String.UsageCount_key), usageCount + 1);
  			edit.Commit();
  
  			_showPasswordDefault =
  				!prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));
              _showTotpDefault =
                  !prefs.GetBoolean(GetString(Resource.String.masktotp_key), Resources.GetBoolean(Resource.Boolean.masktotp_default));
  
  			RequestWindowFeature(WindowFeatures.IndeterminateProgress);
  			
  			_activityDesign.ApplyTheme(); 
  			base.OnCreate(savedInstanceState);
  			
  
  			
  
  			SetEntryView();
  
  			Database db = App.Kp2a.CurrentDb;
  			// Likely the app has been killed exit the activity 
  			if (db == null || (App.Kp2a.QuickLocked))
  			{
  				Finish();
  				return;
  			}
  
  			SetResult(KeePass.ExitNormal);
  
  			Intent i = Intent;
              ElementAndDatabaseId dbAndElementId = new ElementAndDatabaseId(i.GetStringExtra(KeyEntry));
  			PwUuid uuid = new PwUuid(MemUtil.HexStringToByteArray(dbAndElementId.ElementIdString));
  			_pos = i.GetIntExtra(KeyRefreshPos, -1);
              _historyIndex = i.GetIntExtra(KeyEntryHistoryIndex, -1);
  
  			AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
  
  			Entry = db.EntriesById[uuid];
  
              if (_historyIndex >= 0 && _historyIndex < Entry.History.UCount)
              {
                  _historyParentEntry = Entry;
                  Entry = Entry.History.Skip(_historyIndex).First();
                  FindViewById<Button>(Resource.Id.btn_restore_history).Click += (sender, args) =>
                  {
                      RestoreFromHistory();
                      SaveHistoryChangeAndFinish();
                  };
                  FindViewById<Button>(Resource.Id.btn_remove_history).Click += (sender, args) =>
                  {
                      RemoveFromHistory();
                      SaveHistoryChangeAndFinish();
  				};
  
  
              }
              else
              {
                  // Update last access time.
                  Entry.Touch(false);
                  FindViewById<Button>(Resource.Id.btn_restore_history).Visibility = ViewStates.Gone;
                  FindViewById<Button>(Resource.Id.btn_remove_history).Visibility = ViewStates.Gone;
  			}
  
              // Refresh Menu contents in case onCreateMenuOptions was called before Entry was set
  			ActivityCompat.InvalidateOptionsMenu(this);
  
  			
  
  			if (PwDefs.IsTanEntry(Entry) 
                  && prefs.GetBoolean(GetString(Resource.String.TanExpiresOnUse_key), Resources.GetBoolean(Resource.Boolean.TanExpiresOnUse_default)) 
                  && ((Entry.Expires == false) || Entry.ExpiryTime > DateTime.Now))
  			{
  				PwEntry backupEntry = Entry.CloneDeep();
  				Entry.ExpiryTime = DateTime.Now;
  				Entry.Expires = true;
  				Entry.Touch(true);
  				RequiresRefresh();
  				UpdateEntry update = new UpdateEntry(this, App.Kp2a, backupEntry, Entry, null);
  				ProgressTask pt = new ProgressTask(App.Kp2a, this, update);
  				pt.Run();
  			}
  			FillData();
  
  			SetupEditButtons();
  			
  			App.Kp2a.LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2a.CurrentDb);
  
  			_pluginActionReceiver = new PluginActionReceiver(this);
  			RegisterReceiver(_pluginActionReceiver, new IntentFilter(Strings.ActionAddEntryAction));
  			_pluginFieldReceiver = new PluginFieldReceiver(this);
  			RegisterReceiver(_pluginFieldReceiver, new IntentFilter(Strings.ActionSetEntryField));
  
  			new Thread(NotifyPluginsOnOpen).Start();
  
  			//the rest of the things to do depends on the current app task:
  			AppTask.CompleteOnCreateEntryActivity(this);
  		}
  
          private void RemoveFromHistory()
          {
              _historyParentEntry.History.RemoveAt((uint)_historyIndex);
              _historyParentEntry.Touch(true, false);
  		}
  
          private void RestoreFromHistory()
          {
              var db = App.Kp2a.FindDatabaseForElement(_historyParentEntry);
  			_historyParentEntry.RestoreFromBackup((uint)_historyIndex, db.KpDatabase);
              _historyParentEntry.Touch(true, false);
  		}
  
          private void SaveHistoryChangeAndFinish()
          {
              PwGroup parent = _historyParentEntry.ParentGroup;
              if (parent != null)
              {
                  // Mark parent group dirty (title might have changed etc.)
                  App.Kp2a.DirtyGroups.Add(parent);
              }
  
  			var saveTask = new SaveDb(this, App.Kp2a, App.Kp2a.FindDatabaseForElement(Entry), new ActionOnFinish(this, (success, message, activity) =>
              {
                  activity.SetResult(KeePass.ExitRefresh);
                  activity.Finish();
              }));
  
              ProgressTask pt = new ProgressTask(App.Kp2a, this, saveTask);
              pt.Run();
  		}
  
          private void NotifyPluginsOnOpen()
  		{
  			Intent i = new Intent(Strings.ActionOpenEntry);
  			i.PutExtra(Strings.ExtraSender, PackageName);
  			AddEntryToIntent(i);
  
  			foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
  			{
  				i.SetPackage(plugin);
  				SendBroadcast(i);
  			}
  
  			new Kp2aTotp().OnOpenEntry();
  
  		}
  		private void NotifyPluginsOnModification(string fieldId)
  		{
  			Intent i = new Intent(Strings.ActionEntryOutputModified);
  			i.PutExtra(Strings.ExtraSender, PackageName);
  			i.PutExtra(Strings.ExtraFieldId, fieldId);
  			AddEntryToIntent(i);
  
  
  			foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
  			{
  				i.SetPackage(plugin);
  				SendBroadcast(i);
  			}
  		}
  
  		
  
  		internal void StartNotificationsService(bool activateKeyboard)
  		{
  			Intent showNotIntent = new Intent(this, typeof (CopyToClipboardService));
  			showNotIntent.SetAction(Intents.ShowNotification);
  			showNotIntent.PutExtra(KeyEntry, new ElementAndDatabaseId(App.Kp2a.CurrentDb, Entry).FullId);
  			AppTask.PopulatePasswordAccessServiceIntent(showNotIntent);
  			showNotIntent.PutExtra(KeyActivateKeyboard, activateKeyboard);
  
  			StartService(showNotIntent);
  		}
  
  
  		private String getDateTime(DateTime dt)
  		{
  			return dt.ToLocalTime().ToString("g", CultureInfo.CurrentUICulture);
  		}
  
  		private String concatTags(List<string> tags)
  		{
  			StringBuilder sb = new StringBuilder();
  			foreach (string tag in tags)
  			{
  				sb.Append(tag);
  				sb.Append(", ");
  			}
  			if (tags.Count > 0)
  				sb.Remove(sb.Length - 2, 2);
  			return sb.ToString();
  		}
  
  		private void PopulateExtraStrings()
  		{
  			ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings);
  		    bool hasExtras = false;
  			EditModeBase editMode = new DefaultEdit();
  			if (KpEntryTemplatedEdit.IsTemplated(App.Kp2a.CurrentDb, this.Entry))
  				editMode = new KpEntryTemplatedEdit(App.Kp2a.CurrentDb, this.Entry);
  			foreach (var key in  editMode.SortExtraFieldKeys(Entry.Strings.GetKeys().Where(key=> !PwDefs.IsStandardField(key))))
  			{
  				if (editMode.IsVisible(key))
  				{
  					hasExtras = true;
  					var value = Entry.Strings.Get(key);
  					var stringView = CreateExtraSection(key, value.ReadString(), value.IsProtected);
  					extraGroup.AddView(stringView.View);
  				}
  			}
              FindViewById(Resource.Id.extra_strings_container).Visibility = hasExtras ? ViewStates.Visible : ViewStates.Gone;
  		}
  
  		private ExtraStringView CreateExtraSection(string key, string value, bool isProtected)
  		{
  			LinearLayout layout = new LinearLayout(this, null) {Orientation = Orientation.Vertical};
  			LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent,
  			                                                                       ViewGroup.LayoutParams.WrapContent);
  
  			layout.LayoutParameters = layoutParams;
  			View viewInflated = LayoutInflater.Inflate(Resource.Layout.entry_extrastring_title, null);
  			TextView keyView = viewInflated.FindViewById<TextView>(Resource.Id.entry_title);
  			if (key != null)
  				keyView.Text = key;
  
  			layout.AddView(viewInflated);
  			RelativeLayout valueViewContainer =
  				(RelativeLayout) LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
  			var valueView = valueViewContainer.FindViewById<TextView>(Resource.Id.entry_extra);
  		    var valueViewVisible = valueViewContainer.FindViewById<TextView>(Resource.Id.entry_extra_visible);
  		    if (value != null)
  		    {
  		        valueView.Text = value;
                  valueViewVisible.Text = value;
  
              }
  		    SetPasswordTypeface(valueViewVisible);
  		    if (isProtected)
  		    {
  		        RegisterProtectedTextView(key, valueView, valueViewVisible);
                  
  		    }
  		    else
  		    {
  		        valueView.Visibility = ViewStates.Gone;
  		    }
  
  			layout.AddView(valueViewContainer);
  			var stringView = new ExtraStringView(layout, valueView, valueViewVisible, keyView);
  
  			_stringViews.Add(key, stringView);
  			RegisterTextPopup(valueViewContainer, valueViewContainer.FindViewById(Resource.Id.extra_vdots), key, isProtected);
  
  			return stringView;
  
  		}
  
  
  
  		private List<IPopupMenuItem> RegisterPopup(string popupKey, View clickView, View anchorView)
  		{
  			clickView.Click += (sender, args) =>
  				{
  					ShowPopup(anchorView, popupKey);
  				};
  			_popupMenuItems[popupKey] = new List<IPopupMenuItem>();
  			return _popupMenuItems[popupKey];
  		}
  
  	    internal Uri WriteBinaryToFile(string key, bool writeToCacheDirectory)
  	    {
  	        ProtectedBinary pb = Entry.Binaries.Get(key);
  	        System.Diagnostics.Debug.Assert(pb != null);
  	        if (pb == null)
  	            throw new ArgumentException();
  
  
  	        ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
  	        
  	        if (writeToCacheDirectory)
  	        {
  	            string binaryDirectory = CacheDir.Path + File.Separator + AttachmentContentProvider.AttachmentCacheSubDir;
  
  	            string filepart = key;
  	            Java.Lang.String javaFilename = new Java.Lang.String(filepart);
  	            filepart = javaFilename.ReplaceAll("[^a-zA-Z0-9.-]", "_");
  
  	            var targetFile = new File(binaryDirectory, filepart);
  
  	            File parent = targetFile.ParentFile;
  
  	            if (parent == null || (parent.Exists() && !parent.IsDirectory))
  	            {
  	                Toast.MakeText(this,
  	                    Resource.String.error_invalid_path,
  	                    ToastLength.Long).Show();
  	                return null;
  	            }
  
  	            if (!parent.Exists())
  	            {
  	                // Create parent directory
  	                if (!parent.Mkdirs())
  	                {
  	                    Toast.MakeText(this,
  	                        Resource.String.error_could_not_create_parent,
  	                        ToastLength.Long).Show();
  	                    return null;
  
  	                }
  	            }
  	            string filename = targetFile.AbsolutePath;
  
  	            byte[] pbData = pb.ReadData();
  	            try
  	            {
  	                System.IO.File.WriteAllBytes(filename, pbData);
  	            }
  	            catch (Exception exWrite)
  	            {
  	                Toast.MakeText(this,
  	                    GetString(Resource.String.SaveAttachment_Failed, new Java.Lang.Object[] {filename})
  	                    + exWrite.Message, ToastLength.Long).Show();
  	                return null;
  	            }
  	            finally
  	            {
  	                MemUtil.ZeroByteArray(pbData);
  	            }
  	            Toast.MakeText(this,
  	                GetString(Resource.String.SaveAttachment_doneMessage, new Java.Lang.Object[] {filename}),
  	                ToastLength.Short).Show();
  	            return Uri.Parse("content://" + AttachmentContentProvider.Authority + "/"
  	                             + filename);
  	        }
  	        else
  	        {
  	            _exportBinaryProcessManager =
  	                new ExportBinaryProcessManager(requestCodeSelFileStorageForWriteAttachment, this, key);
                  _exportBinaryProcessManager.StartProcess();
  	            return null;
  	        }
  	    
      	    
  		}
  
  		internal void OpenBinaryFile(Android.Net.Uri uri)
  		{
  
  
  			String theMimeType = GetMimeType(uri.Path);
  			if (theMimeType != null)
  			{
  
  				Intent theIntent = new Intent(Intent.ActionView);
  				theIntent.AddFlags(ActivityFlags.NewTask | ActivityFlags.ExcludeFromRecents);
  				theIntent.SetDataAndType(uri, theMimeType);
  				try
  				{
  					StartActivity(theIntent);
  				}
  				catch (ActivityNotFoundException)
  				{
  					//ignore
  					Toast.MakeText(this, "Couldn't open file", ToastLength.Short).Show();
  				}
  			}
  
  		}
  
  
  
  		private void RegisterProtectedTextView(string fieldKey, TextView protectedTextView, TextView visibleTextView)
  		{
              if (!_showPassword.ContainsKey(protectedTextView))
              {
                  _showPassword[protectedTextView] = fieldKey == UpdateTotpTimerTask.TotpKey ? _showTotpDefault : _showPasswordDefault;
              }
  		    var protectedTextviewGroup = new ProtectedTextviewGroup { ProtectedField = protectedTextView, VisibleProtectedField = visibleTextView};
  		    _protectedTextViews.Add(protectedTextviewGroup);
              SetPasswordStyle(protectedTextviewGroup);
  		}
  
  
  		private void PopulateBinaries()
  		{
  			ViewGroup binariesGroup = (ViewGroup) FindViewById(Resource.Id.binaries);
  			foreach (KeyValuePair<string, ProtectedBinary> pair in Entry.Binaries)
  			{
  				String key = pair.Key;
  
  
  				RelativeLayout valueViewContainer =
  					(RelativeLayout) LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
  				var valueView = valueViewContainer.FindViewById<TextView>(Resource.Id.entry_extra);
  				if (key != null)
  					valueView.Text = key;
  
  				string popupKey = Strings.PrefixBinary + key;
  
  				var itemList = RegisterPopup(popupKey, valueViewContainer, valueViewContainer.FindViewById(Resource.Id.extra_vdots));
  				itemList.Add(new WriteBinaryToFilePopupItem(key, this));
  				itemList.Add(new OpenBinaryPopupItem(key, this));
  				itemList.Add(new ViewImagePopupItem(key, this));
  
  
  
  
  				binariesGroup.AddView(valueViewContainer);
  				/*
  				Button binaryButton = new Button(this);
  				RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
  				binaryButton.Text = key;
  				binaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuSave),null, null, null);
  				binaryButton.Click += (sender, e) => 
  				{
  					Button btnSender = (Button)(sender);
  
  					AlertDialog.Builder builder = new AlertDialog.Builder(this);
  					builder.SetTitle(GetString(Resource.String.SaveAttachmentDialog_title));
  					
  					builder.SetMessage(GetString(Resource.String.SaveAttachmentDialog_text));
  					
  					builder.SetPositiveButton(GetString(Resource.String.SaveAttachmentDialog_save), (dlgSender, dlgEvt) => 
  					                                                                                                                    {
  							
  						});
  					
  					builder.SetNegativeButton(GetString(Resource.String.SaveAttachmentDialog_open), (dlgSender, dlgEvt) => 
  					                                                                                                                   {
  							
  						});
  
  					Dialog dialog = builder.Create();
  					dialog.Show();
  
  
  				};
  				binariesGroup.AddView(binaryButton,layoutParams);
  				*/
  
  			}
  			FindViewById(Resource.Id.entry_binaries_label).Visibility = Entry.Binaries.Any() ? ViewStates.Visible : ViewStates.Gone;
  		}
  
  		// url = file path or whatever suitable URL you want.
  		public static String GetMimeType(String url)
  		{
  			String type = null;
  			String extension = MimeTypeMap.GetFileExtensionFromUrl(url);
  			if (extension != null)
  			{
  				MimeTypeMap mime = MimeTypeMap.Singleton;
  				type = mime.GetMimeTypeFromExtension(extension.ToLowerInvariant());
  			}
  			return type;
  		}
  
  		public override void OnBackPressed()
  		{
  			base.OnBackPressed();
  			//OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
  		}
  
  		protected void FillData()
  		{
  
gt: 			_protectedTextViews = new List<ProtectedTextviewGroup>();