/*
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;
using System.Collections.Generic;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Views;

using KeePassLib;
using Android.Preferences;
using KeePassLib.Interfaces;
using KeePassLib.Utility;
using keepass2android.Io;
using keepass2android.database.edit;
using keepass2android.view;
using Android.Graphics.Drawables;
using Android.Provider;
using Android.Views.Autofill;
using Object = Java.Lang.Object;
using Android.Text;
using keepass2android.search;
using keepass2android;
using KeeTrayTOTP.Libraries;
using AndroidX.AppCompat.Widget;
using Google.Android.Material.Dialog;
using SearchView = AndroidX.AppCompat.Widget.SearchView;

namespace keepass2android
{
    public abstract class GroupBaseActivity : LockCloseActivity
    {
        public const String KeyEntry = "entry";
        public const String KeyMode = "mode";

        private float _currentListTextSize;

        public const int RequestCodeActivateRealSearch = 12366;

        static readonly Dictionary<int /*resource id*/, int /*prio*/> bottomBarElementsPriority = new Dictionary<int, int>()
        {
            { Resource.Id.cancel_insert_element, 20 },
            { Resource.Id.insert_element, 20 },
            //only use the same id if elements can be shown simultaneously!
            { Resource.Id.dbreadonly_infotext, 14 },
            { Resource.Id.child_db_infotext, 13 },
            { Resource.Id.fingerprint_infotext, 12 },
            { Resource.Id.autofill_infotext, 11 },
            { Resource.Id.notification_permission_infotext, 10 },
            { Resource.Id.notification_info_android8_infotext, 9 },
            { Resource.Id.infotext, 8 },
            { Resource.Id.select_other_entry, 20},
            { Resource.Id.add_url_entry, 20},
        };

        private readonly HashSet<int /*resource id*/> showableBottomBarElements = new HashSet<int>();

        private ActivityDesign _design;

        public virtual void LaunchActivityForEntry(PwEntry pwEntry, int pos)
        {
            EntryActivity.Launch(this, pwEntry, pos, AppTask);
        }

        protected GroupBaseActivity()
        {
            _design = new ActivityDesign(this);
        }

        protected GroupBaseActivity(IntPtr javaReference, JniHandleOwnership transfer)
            : base(javaReference, transfer)
        {

        }

        protected override void OnSaveInstanceState(Bundle outState)
        {
            base.OnSaveInstanceState(outState);
            AppTask.ToBundle(outState);
        }

        public virtual void SetupNormalButtons()
        {
            SetNormalButtonVisibility(AddGroupEnabled, AddEntryEnabled);
        }


        protected virtual bool AddGroupEnabled
        {
            get { return false; }
        }
        protected virtual bool AddEntryEnabled
        {
            get { return false; }
        }

        public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry)
        {
            if (FindViewById(Resource.Id.fabCancelAddNew) != null)
            {
                FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
                FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone;
                FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone;

                FindViewById(Resource.Id.fabAddNew).Visibility = (showAddGroup || showAddEntry) ? ViewStates.Visible : ViewStates.Gone;
                FindViewById(Resource.Id.fabSearch).Visibility = (showAddGroup || showAddEntry) ? ViewStates.Visible : ViewStates.Gone;
                FindViewById(Resource.Id.fabTotpOverview).Visibility = CanShowTotpFab() ? ViewStates.Visible : ViewStates.Gone;
            }

            UpdateBottomBarElementVisibility(Resource.Id.insert_element, false);
            UpdateBottomBarElementVisibility(Resource.Id.cancel_insert_element, false);

        }

        void UpdateBottomBarVisibility()
        {
            var bottomBar = FindViewById<RelativeLayout>(Resource.Id.bottom_bar);
            //check for null because the "empty" layouts may not have all views
            int highestPrio = -1;
            HashSet<int> highestPrioElements = new HashSet<int>();
            if (bottomBar != null)
            {
                for (int i = 0; i < bottomBar.ChildCount; i++)
                {
                    int id = bottomBar.GetChildAt(i).Id;
                    if (!showableBottomBarElements.Contains(id))
                        continue;
                    int myPrio = bottomBarElementsPriority[id];

                    if (!highestPrioElements.Any() || highestPrio < myPrio)
                    {
                        highestPrioElements.Clear();
                        highestPrio = myPrio;
                    }
                    if (highestPrio == myPrio)
                    {
                        highestPrioElements.Add(id);
                    }
                }

                bottomBar.Visibility = highestPrioElements.Any() ? ViewStates.Visible : ViewStates.Gone;

                for (int i = 0; i < bottomBar.ChildCount; i++)
                {
                    int id = bottomBar.GetChildAt(i).Id;
                    bottomBar.GetChildAt(i).Visibility = highestPrioElements.Contains(id) ? ViewStates.Visible : ViewStates.Gone;
                }

                if (FindViewById(Resource.Id.divider2) != null)
                    FindViewById(Resource.Id.divider2).Visibility = highestPrioElements.Any() ? ViewStates.Visible : ViewStates.Gone;
            }
        }


        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);

            AppTask a = null;
            if (AppTask.TryGetFromActivityResult(data, ref a))
            {
                AppTask = a;
                //make sure the app task is passed to the calling activity
                this.AppTask.SetActivityResult(this, KeePass.ExitNormal);

            }

            if (RequestCodeActivateRealSearch == requestCode)
            {
                hasCalledOtherActivity = true;
                SetSearchItemVisibility();
                ActivateSearchView();
            }

            if ((GroupEditActivity.RequestCodeGroupEdit == requestCode) && (resultCode == Result.Ok))
            {
                String groupName = data.Extras.GetString(GroupEditActivity.KeyName);
                int groupIconId = data.Extras.GetInt(GroupEditActivity.KeyIconId);
                PwUuid groupCustomIconId =
                    new PwUuid(MemUtil.HexStringToByteArray(data.Extras.GetString(GroupEditActivity.KeyCustomIconId)));
                String strGroupUuid = data.Extras.GetString(GroupEditActivity.KeyGroupUuid);
                GroupBaseActivity act = this;
                Handler handler = new Handler();
                RunnableOnFinish task;
                if (strGroupUuid == null)
                {
                    task = AddGroup.GetInstance(this, App.Kp2a, groupName, groupIconId, groupCustomIconId, Group, new RefreshTask(handler, this), false);
                }
                else
                {
                    PwUuid groupUuid = new PwUuid(MemUtil.HexStringToByteArray(strGroupUuid));
                    task = new EditGroup(this, App.Kp2a, groupName, (PwIcon)groupIconId, groupCustomIconId, App.Kp2a.FindGroup(groupUuid),
                                         new RefreshTask(handler, this));
                }
                ProgressTask pt = new ProgressTask(App.Kp2a, act, task);
                pt.Run();
            }

            if (resultCode == KeePass.ExitCloseAfterTaskComplete)
            {
                AppTask.SetActivityResult(this, KeePass.ExitCloseAfterTaskComplete);
                Finish();
            }

            if (resultCode == KeePass.ExitLoadAnotherDb)
            {
                AppTask.SetActivityResult(this, KeePass.ExitLoadAnotherDb);
                Finish();
            }

            if (resultCode == KeePass.ExitReloadDb)
            {
                AppTask.SetActivityResult(this, KeePass.ExitReloadDb);
                Finish();
            }

        }

        private ISharedPreferences _prefs;

        protected PwGroup Group;

        private AppTask _appTask;
        public AppTask AppTask
        {
            get { return _appTask; }
            set { _appTask = value;
                Kp2aLog.LogTask(value, MyDebugName);
            }
        }
        

        private String strCachedGroupUuid = null;
        private IMenuItem _offlineItem;
        private IMenuItem _onlineItem;
        private IMenuItem _syncItem;
        private SearchView searchView;


        public String UuidGroup
        {
            get
            {
                if (strCachedGroupUuid == null)
                {
                    strCachedGroupUuid = MemUtil.ByteArrayToHexString(Group.Uuid.UuidBytes);
                }
                return strCachedGroupUuid;
            }
        }

        private bool hasCalledOtherActivity = false;
        private IMenuItem searchItem;
        private IMenuItem searchItemDummy;
        private bool isPaused;

        protected override void OnResume()
        {
            base.OnResume();
            if (IsFinishing)
            {
                //can happen e.g. after theme change
                return;
            }
            if (PrefsUtil.GetListTextSize(this) != _currentListTextSize)
            {
               Recreate();
               return;
            }
            AppTask.StartInGroupActivity(this);
            AppTask.SetupGroupBaseActivityButtons(this);

            UpdateDbReadOnlyInfo();
            UpdateChildDbInfo();
            UpdateFingerprintInfo();
            UpdateAutofillInfo();
            UpdateAndroid8NotificationInfo();
            UpdatePostNotificationsPermissionInfo();
            UpdateInfotexts();

            RefreshIfDirty();

            SetSearchItemVisibility();

            isPaused = false;
            System.Threading.Tasks.Task.Run(UpdateTotpCountdown);
        }
        private async System.Threading.Tasks.Task UpdateTotpCountdown()
        {
            
            while (!isPaused )
            {
                RunOnUiThread(() =>
                {
                    var listView = FragmentManager?.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment)
                        ?.ListView;
                    if (listView != null)
                    {
                        int count = listView.Count;
                        for (int i = 0; i < count; i++)
                        {
                            var item = listView.GetChildAt(i);
                            if (item is PwEntryView)
                            {
                                var entryView = (PwEntryView)item;
                                entryView.UpdateTotp();

                            }
                        }
                    }
                });


                await System.Threading.Tasks.Task.Delay(1000);
            }
        }

        private void UpdateInfotexts()
        {

            string lastInfoText;
            if (IsTimeForInfotext(out lastInfoText) && (FindViewById<TextView>(Resource.Id.info_head) != null))
            {

                FingerprintUnlockMode um;
                Enum.TryParse(_prefs.GetString(Database.GetFingerprintModePrefKey(App.Kp2a.CurrentDb.Ioc), ""), out um);
                bool isFingerprintEnabled = (um == FingerprintUnlockMode.FullUnlock);

                string masterKeyKey = "MasterKey" + isFingerprintEnabled;
                string emergencyKey = "Emergency";
                string backupKey = "Backup";

                List<string> applicableInfoTextKeys = new List<string> { masterKeyKey };

                if (App.Kp2a.GetFileStorage(App.Kp2a.CurrentDb.Ioc).UserShouldBackup)
                {
                    applicableInfoTextKeys.Add(backupKey);
                }
                if (App.Kp2a.CurrentDb.EntriesById.Count > 15)
                {
                    applicableInfoTextKeys.Add(emergencyKey);
                }

                List<string> enabledInfoTextKeys = new List<string>();
                foreach (string key in applicableInfoTextKeys)
                {
                    if (!InfoTextWasDisabled(key))
                        enabledInfoTextKeys.Add(key);
                }

                if (enabledInfoTextKeys.Any())
                {
                    string infoTextKey = "", infoHead = "", infoMain = "", infoNote = "";

                    if (enabledInfoTextKeys.Count > 1)
                    {
                        foreach (string key in enabledInfoTextKeys)
                            if (key == lastInfoText)
                            {
                                enabledInfoTextKeys.Remove(key);
                                break;
                            }
                        infoTextKey = enabledInfoTextKeys[new Random().Next(enabledInfoTextKeys.Count)];
                    }

                    if (infoTextKey == masterKeyKey)
                    {
                        infoHead = GetString(Resource.String.masterkey_infotext_head);
                        infoMain = GetString(Resource.String.masterkey_infotext_main);
                        if (isFingerprintEnabled)
                            infoNote = GetString(Resource.String.masterkey_infotext_fingerprint_note);
                    }
                    else if (infoTextKey == emergencyKey)
                    {
                        infoHead = GetString(Resource.String.emergency_infotext_head);
                        infoMain = GetString(Resource.String.emergency_infotext_main);
                    }
                    else if (infoTextKey == backupKey)
                    {
                        infoHead = GetString(Resource.String.backup_infotext_head);
                        infoMain = GetString(Resource.String.backup_infotext_main);
                        infoNote = GetString(Resource.String.backup_infotext_note, GetString(Resource.String.menu_app_settings), GetString(Resource.String.menu_db_settings), GetString(Resource.String.export_prefs));
                    }



                    FindViewById<TextView>(Resource.Id.info_head).Text = infoHead;
                    FindViewById<TextView>(Resource.Id.info_main).Text = infoMain;
                    var additionalInfoText = FindViewById<TextView>(Resource.Id.info_additional);
                    additionalInfoText.Text = infoNote;
                    additionalInfoText.Visibility = string.IsNullOrEmpty(infoNote) ? ViewStates.Gone : ViewStates.Visible;

                    if (infoTextKey != "")
                    {

                        RegisterInfoTextDisplay(infoTextKey);
                        FindViewById(Resource.Id.info_ok).Click += (sender, args) =>
                        {
                            UpdateBottomBarElementVisibility(Resource.Id.infotext, false);
                        };
                        FindViewById(Resource.Id.info_dont_show_again).Click += (sender, args) =>
                        {
                            UpdateBottomBarElementVisibility(Resource.Id.infotext, false);
                            DisableInfoTextDisplay(infoTextKey);
                        };

                        UpdateBottomBarElementVisibility(Resource.Id.infotext, true);
                    }

                }



            }
        }

        protected override void OnStop()
        {
            base.OnStop();
            hasCalledOtherActivity = false;
        }


        protected override void OnPause()
        {
            base.OnPause();
            isPaused = true;
        }


        private void UpdatePostNotificationsPermissionInfo(bool hideForever=false)
        {
            const string prefsKey = "DidShowNotificationPermissionInfo";

            bool canShowNotificationInfo = ((int)Build.VERSION.SdkInt >= 33) 
                                           && (!_prefs.GetBoolean(prefsKey, false)
                                               && (CheckSelfPermission(Android.Manifest.Permission.PostNotifications) !=
                                                   Permission.Granted)
                                               && (ShouldShowRequestPermissionRationale(Android.Manifest.Permission.PostNotifications) //this menthod returns false if we haven't asked yet or if the user has denied permission too often
                                                   || !PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean("RequestedPostNotificationsPermission", false))//use a preference to tell the difference between "haven't asked yet" and "have asked too often"
                                           );
            if ((canShowNotificationInfo) && hideForever)
            {
                _prefs.Edit().PutBoolean(prefsKey, true).Commit();
                canShowNotificationInfo = false;
            }
            if (canShowNotificationInfo)
            {
                RegisterInfoTextDisplay("NotificationPermissionInfo"); //this ensures that we don't show the general info texts too soon
            }
            UpdateBottomBarElementVisibility(Resource.Id.notification_permission_infotext, canShowNotificationInfo);

        }

        private void UpdateAndroid8NotificationInfo(bool hideForever = false)
        {
            const string prefsKey = "DidShowAndroid8NotificationInfo";
            
            bool canShowNotificationInfo = (Build.VERSION.SdkInt >= BuildVersionCodes.O) && (!_prefs.GetBoolean(prefsKey, false));
            if ((canShowNotificationInfo) && hideForever)
            {
                _prefs.Edit().PutBoolean(prefsKey, true).Commit();
                canShowNotificationInfo = false;
            }
            if (canShowNotificationInfo)
            {
                RegisterInfoTextDisplay("Android8Notification"); //this ensures that we don't show the general info texts too soon
            }
            UpdateBottomBarElementVisibility(Resource.Id.notification_info_android8_infotext, canShowNotificationInfo);
            

        }

        public override bool OnSearchRequested()
        {
            Intent i = new Intent(this, typeof(SearchActivity));
            AppTask.ToIntent(i);
            StartActivityForResult(i, 0);
            return true;
        }

        public void RefreshIfDirty()
        {
            if (App.Kp2a.DirtyGroups.Contains(Group))
            {
                App.Kp2a.DirtyGroups.Remove(Group);
                ListAdapter.NotifyDataSetChanged();

            }
        }

        public BaseAdapter ListAdapter
        {
            get { return (BaseAdapter)FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter; }
        }

        public virtual bool IsSearchResult
        {
            get { return false; }
        }

        protected override void OnCreate(Bundle savedInstanceState)
        {
            _design.ApplyTheme();
            _currentListTextSize = PrefsUtil.GetListTextSize(this);
            base.OnCreate(savedInstanceState);
            
            Android.Util.Log.Debug("KP2A", "Creating GBA");

            AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);

            // Likely the app has been killed exit the activity 
            if (App.Kp2a.CurrentDb== null)
            {
                Finish();
                return;
            }

            _prefs = PreferenceManager.GetDefaultSharedPreferences(this);


            SetContentView(ContentResourceId);
            

            if (FindViewById(Resource.Id.enable_autofill) != null)
            {
                FindViewById(Resource.Id.enable_autofill).Click += (sender, args) =>
                {
                    var intent = new Intent(Settings.ActionRequestSetAutofillService);
                    intent.SetData(Android.Net.Uri.Parse("package:" + PackageName));
		            try
		            {
		                StartActivity(intent);
		            }
		            catch (ActivityNotFoundException e)
		            {
                        //this exception was reported by many Huawei users
		                Kp2aLog.LogUnexpectedError(e);
		                new MaterialAlertDialogBuilder(this)
		                    .SetTitle(Resource.String.autofill_enable)
		                    .SetMessage(Resource.String.autofill_enable_failed)
		                    .SetPositiveButton(Resource.String.Ok, (o, eventArgs) => { })
		                    .Show();
		                const string autofillservicewasenabled = "AutofillServiceWasEnabled";
		                _prefs.Edit().PutBoolean(autofillservicewasenabled, true).Commit();
		                UpdateBottomBarElementVisibility(Resource.Id.autofill_infotext, false);
                    }
                };
            }

            if (FindViewById(Resource.Id.info_dont_show_fingerprint_again) != null)
            {
                FindViewById(Resource.Id.info_dont_show_fingerprint_again).Click += (sender, args) =>
                {
                    _prefs.Edit().PutBoolean(fingerprintinfohidden_prefskey, true).Commit();
                    UpdateFingerprintInfo();
                };
            }


            if (FindViewById(Resource.Id.hide_fingerprint_info) != null)
            {
                FindViewById(Resource.Id.hide_fingerprint_info).Click += (sender, args) =>
                {
                    _prefs.Edit().PutBoolean(fingerprintinfohidden_prefskey + App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, true).Commit(); 
                    UpdateFingerprintInfo();
                };
            }

            if (FindViewById(Resource.Id.enable_fingerprint) != null)
            {
                FindViewById(Resource.Id.enable_fingerprint).Click += (sender, args) =>
                {
                    StartActivity(typeof(BiometricSetupActivity));
                };
            }

            if (FindViewById(Resource.Id.info_dont_show_autofill_again) != null)
            {
                FindViewById(Resource.Id.info_dont_show_autofill_again).Click += (sender, args) =>
                {
                    _prefs.Edit().PutBoolean(autofillservicewasenabled_prefskey, true).Commit();
                    UpdateAutofillInfo();
                };
            }

            if (FindViewById(Resource.Id.configure_child_db) != null)
            {
                FindViewById(Resource.Id.configure_child_db).Click += (sender, args) =>
                {
                    StartActivity(typeof(ConfigureChildDatabasesActivity));
                };
            }

            if (FindViewById(Resource.Id.info_dont_show_child_db_again) != null)
            {
                FindViewById(Resource.Id.info_dont_show_child_db_again).Click += (sender, args) =>
                {
                    _prefs.Edit().PutBoolean(childdb_ignore_prefskey + App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, true).Commit();
                    UpdateChildDbInfo();
                };
            }


            if (FindViewById(Resource.Id.info_dont_show_dbreadonly_again) != null)
            {
                FindViewById(Resource.Id.info_dont_show_dbreadonly_again).Click += (sender, args) =>
                {
                    _prefs.Edit().PutBoolean(dbreadonly_ignore_prefskey + App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, true).Commit();
                    UpdateDbReadOnlyInfo();
                };
            }

            if (FindViewById(Resource.Id.fabSearch) != null)
            {
                FindViewById(Resource.Id.fabSearch).Click += (sender, args) =>
                {
                    if (searchView?.Iconified != false)
                        ActivateSearchView();
                    else
                        searchView.Iconified = true;
                };
            }

            if (FindViewById(Resource.Id.fabTotpOverview) != null)
            {
                FindViewById(Resource.Id.fabTotpOverview).Click += (sender, args) =>
                {
                    SearchTotpResults.Launch(this, this.AppTask);
                };
            }

            if (FindViewById(Resource.Id.fabCancelAddNew) != null)
            {
                FindViewById(Resource.Id.fabAddNew).Click += (sender, args) =>
                {
                    FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Visible;
                    FindViewById(Resource.Id.fabAddNewGroup).Visibility = AddGroupEnabled ? ViewStates.Visible : ViewStates.Gone;
                    FindViewById(Resource.Id.fabAddNewEntry).Visibility = AddEntryEnabled ? ViewStates.Visible : ViewStates.Gone;
                    FindViewById<Google.Android.Material.FloatingActionButton.ExtendedFloatingActionButton>(Resource.Id
                        .fabAddNewEntry).Shrink();
                    FindViewById<Google.Android.Material.FloatingActionButton.ExtendedFloatingActionButton>(Resource.Id
                        .fabAddNewGroup).Extended = false;
                    FindViewById<Google.Android.Material.FloatingActionButton.ExtendedFloatingActionButton>(Resource.Id
                        .fabAddNewGroup).Extend();
                    FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Gone;
                    FindViewById(Resource.Id.fabSearch).Visibility = ViewStates.Gone;
                    FindViewById(Resource.Id.fabTotpOverview).Visibility = ViewStates.Gone;
                };

                FindViewById(Resource.Id.fabCancelAddNew).Click += (sender, args) =>
                {
                    FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
                    FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone;
                    FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone;
                    FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Visible;
                    FindViewById(Resource.Id.fabSearch).Visibility = ViewStates.Visible;
                    FindViewById(Resource.Id.fabTotpOverview).Visibility = CanShowTotpFab() ? ViewStates.Visible : ViewStates.Gone;
                };


            }


            if (FindViewById(Resource.Id.cancel_insert_element) != null)
            {
                FindViewById(Resource.Id.cancel_insert_element).Click += (sender, args) => StopMovingElements();
                FindViewById(Resource.Id.insert_element).Click += (sender, args) => InsertElements();
                Util.MoveBottomBarButtons(Resource.Id.cancel_insert_element, Resource.Id.insert_element, Resource.Id.bottom_bar, this);
            }

            if (FindViewById(Resource.Id.show_autofill_info) != null)
            {
                FindViewById(Resource.Id.show_autofill_info).Click += (sender, args) => Util.GotoUrl(this, "https://philippc.github.io/keepass2android/OreoAutoFill.html");
                Util.MoveBottomBarButtons(Resource.Id.show_autofill_info, Resource.Id.enable_autofill, Resource.Id.autofill_buttons, this);
            }

            if (FindViewById(Resource.Id.configure_notification_channels) != null)
            {
                FindViewById(Resource.Id.configure_notification_channels).Click += (sender, args) =>
                {
                    Intent intent = new Intent(Settings.ActionChannelNotificationSettings);
                    intent.PutExtra(Settings.ExtraChannelId, App.NotificationChannelIdQuicklocked);
                    intent.PutExtra(Settings.ExtraAppPackage, PackageName);
                    try
                    {
                        StartActivity(intent);
                    }
                    catch (Exception e)
                    {
                        new MaterialAlertDialogBuilder(this)
                            .SetTitle("Unexpected error")
                            .SetMessage(
                                "Opening the settings failed. Please report this to crocoapps@gmail.com including information about your device vendor and OS. Please try to configure the notifications by long pressing a KP2A notification. Details: " + e.ToString())
                            .Show();
                    }
                    UpdateAndroid8NotificationInfo(true);
                };
                FindViewById(Resource.Id.ignore_notification_channel).Click += (sender, args) =>
                {
                    UpdateAndroid8NotificationInfo(true);
                };

            }

            if (FindViewById(Resource.Id.post_notification_button_allow) != null)
            {
                FindViewById(Resource.Id.post_notification_button_allow).Click += (sender, args) =>
                {
                    //remember that we did ask for permission at least once:
                    var edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
                    edit.PutBoolean("RequestedPostNotificationsPermission", true);
                    edit.Commit();

                    AndroidX.Core.App.ActivityCompat.RequestPermissions(this, new[] { Android.Manifest.Permission.PostNotifications }, 0);
                    UpdatePostNotificationsPermissionInfo(true);
                };
                FindViewById(Resource.Id.post_notification_button_dont_show_again).Click += (sender, args) =>
                {
                    UpdatePostNotificationsPermissionInfo(true);
                };

            }







            SetResult(KeePass.ExitNormal);

            



        }

        protected virtual bool CanShowTotpFab()
        {
            return App.Kp2a.CurrentDb.HasTotpEntries && Group == App.Kp2a.CurrentDb.Root;
        }

        private bool IsTimeForInfotext(out string lastInfoText)
        {
            DateTime lastDisplayTime = new DateTime(_prefs.GetLong("LastInfoTextTime", 0));
            lastInfoText = _prefs.GetString("LastInfoTextKey", "");
#if DEBUG
            return DateTime.UtcNow - lastDisplayTime > TimeSpan.FromSeconds(10);
#else
            return DateTime.UtcNow - lastDisplayTime > TimeSpan.FromDays(3);
#endif
        }

        private void DisableInfoTextDisplay(string infoTextKey)
        {
            _prefs
                .Edit()
                .PutBoolean("InfoTextDisabled_" + infoTextKey, true)
                .Commit();

        }

        private void RegisterInfoTextDisplay(string infoTextKey)
        {
            _prefs
                .Edit()
                .PutLong("LastInfoTextTime", DateTime.UtcNow.Ticks)
                .PutString("LastInfoTextKey", infoTextKey)
                .Commit();

        }

        private bool InfoTextWasDisabled(string infoTextKey)
        {
            return _prefs.GetBoolean("InfoTextDisabled_" + infoTextKey, false);
        }

        const string dbreadonly_ignore_prefskey = "dbreadonly_ignore_prefskey";
        const string childdb_ignore_prefskey = "childdb_ignore_prefskey";
        const string autofillservicewasenabled_prefskey = "AutofillServiceWasEnabled";
        const string fingerprintinfohidden_prefskey = "fingerprintinfohidden_prefskey";

        private void UpdateAutofillInfo()
        {
            bool canShowAutofillInfo = false;

            if (!((Android.OS.Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.O) ||
                  !((AutofillManager)GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager))))
                      .IsAutofillSupported))
            {
                if (!((AutofillManager)GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager))))
                    .HasEnabledAutofillServices)
                {
                    if (!_prefs.GetBoolean(autofillservicewasenabled_prefskey, false))
                        canShowAutofillInfo = true;
                }
                else
                {
                    _prefs.Edit().PutBoolean(autofillservicewasenabled_prefskey, true).Commit();

                }
            }
            if (canShowAutofillInfo)
            {
                RegisterInfoTextDisplay("AutofillSuggestion"); //this ensures that we don't show the general info texts too soon
                
            }
            UpdateBottomBarElementVisibility(Resource.Id.autofill_infotext, canShowAutofillInfo);
        }

        private void UpdateFingerprintInfo()
        {
            bool canShowFingerprintInfo = false;

            bool disabledForDatabase = _prefs.GetBoolean(fingerprintinfohidden_prefskey + App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, false);
            bool disabledForAll = _prefs.GetBoolean(fingerprintinfohidden_prefskey, false);
            if (!disabledForAll && !disabledForDatabase && !App.Kp2a.IsChildDatabase(App.Kp2a.CurrentDb))
            {

                BiometricModule biometricModule = new BiometricModule(this);
                if (biometricModule.IsAvailable)
                {
                    FingerprintUnlockMode um;
                    Enum.TryParse(_prefs.GetString(Database.GetFingerprintModePrefKey(App.Kp2a.CurrentDb.Ioc), ""), out um);
                    canShowFingerprintInfo = um == FingerprintUnlockMode.Disabled;
                }
            }


            if (canShowFingerprintInfo)
            {
                RegisterInfoTextDisplay("FingerprintSuggestion"); //this ensures that we don't show the general info texts too soon

            }
            UpdateBottomBarElementVisibility(Resource.Id.fingerprint_infotext, canShowFingerprintInfo);
        }

        private void UpdateChildDbInfo()
        {
            bool canShow = Group == App.Kp2a.CurrentDb.Root
                && KeeAutoExecExt.GetAutoExecItems(App.Kp2a.CurrentDb.KpDatabase).Any(item =>
            {
                bool isexplicit;
                KeeAutoExecExt.IsDeviceEnabled(item, KeeAutoExecExt.ThisDeviceId, out isexplicit);
                return !isexplicit;
            });

            bool disabledForDatabase = _prefs.GetBoolean(childdb_ignore_prefskey+ App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, false);

            if (canShow && !disabledForDatabase)
            {
                RegisterInfoTextDisplay("ChildDb"); //this ensures that we don't show the general info texts too soon

            }
            UpdateBottomBarElementVisibility(Resource.Id.child_db_infotext, canShow && !disabledForDatabase);
        }

        private void UpdateDbReadOnlyInfo()
        {
            bool disabledForDatabase = _prefs.GetBoolean(dbreadonly_ignore_prefskey + App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, false);

            bool canShow = false;

            if (!disabledForDatabase)
            {
                var ioc = App.Kp2a.CurrentDb.Ioc;
                OptionalOut<UiStringKey> reason = new OptionalOut<UiStringKey>();

                if (App.Kp2a.GetFileStorage(ioc).IsReadOnly(ioc, reason))
                {
                    canShow = true;
                    RegisterInfoTextDisplay(
                        "DbReadOnly"); //this ensures that we don't show the general info texts too soon

                    FindViewById<TextView>(Resource.Id.dbreadonly_infotext_text).Text =
                    (GetString(Resource.String.FileReadOnlyMessagePre) + " " +
                     App.Kp2a.GetResourceString(reason.Result));
                }
            }
            UpdateBottomBarElementVisibility(Resource.Id.dbreadonly_infotext, canShow);
        }


        protected void UpdateBottomBarElementVisibility(int resourceId, bool canShow)
        {
            if (canShow)
                showableBottomBarElements.Add(resourceId);
            else
                showableBottomBarElements.Remove(resourceId);
            UpdateBottomBarVisibility();
        }

        protected virtual int ContentResourceId
        {
            get { return Resource.Layout.group; }
        }

        private void InsertElements()
        {
            MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask;
            IEnumerable<IStructureItem> elementsToMove = moveElementsTask.Uuids.Select(uuid => App.Kp2a.FindStructureItem(uuid));



            var moveElement = new MoveElements(elementsToMove.ToList(), Group, this, App.Kp2a, new ActionOnFinish(this,
                (success, message, activity) =>
                {
                    ((GroupBaseActivity)activity)?.StopMovingElements();
                    if (!String.IsNullOrEmpty(message))
                        Toast.MakeText(activity, message, ToastLength.Long).Show();
                }));
            var progressTask = new ProgressTask(App.Kp2a, this, moveElement);
            progressTask.Run();

        }



        protected void SetGroupTitle()
        {
            String name = Group.Name;
            String titleText;
            bool clickable = (Group != null) && (Group.IsVirtual == false) && ((Group.ParentGroup != null) || App.Kp2a.OpenDatabases.Count() > 1);
            if (!String.IsNullOrEmpty(name))
            {
                titleText = name;
            }
            else
            {
                titleText = GetText(Resource.String.root);
            }

            SupportActionBar.Title = titleText;
            if (clickable)
            {
                SupportActionBar.SetHomeButtonEnabled(true);
                SupportActionBar.SetDisplayHomeAsUpEnabled(true);
                SupportActionBar.SetDisplayShowHomeEnabled(true);
            }

        }


        protected void SetGroupIcon()
        {
            if (Group != null)
            {
                SupportActionBar.SetDisplayShowHomeEnabled(true);
            }
        }

        class SuggestionListener : Java.Lang.Object, AndroidX.AppCompat.Widget.SearchView.IOnSuggestionListener
        {
            private readonly AndroidX.CursorAdapter.Widget.CursorAdapter _suggestionsAdapter;
            private readonly GroupBaseActivity _activity;
            private readonly IMenuItem _searchItem;


            public SuggestionListener(AndroidX.CursorAdapter.Widget.CursorAdapter suggestionsAdapter, GroupBaseActivity activity, IMenuItem searchItem)
            {
                _suggestionsAdapter = suggestionsAdapter;
                _activity = activity;
                _searchItem = searchItem;
            }

            public bool OnSuggestionClick(int position)
            {
                var cursor = _suggestionsAdapter.Cursor;
                cursor.MoveToPosition(position);
                
                ElementAndDatabaseId fullId = new ElementAndDatabaseId(cursor.GetString(cursor.GetColumnIndexOrThrow(SearchManager.SuggestColumnIntentDataId)));
                var entryId = fullId.ElementId;
                EntryActivity.Launch(_activity, App.Kp2a.GetDatabase(fullId.DatabaseId).EntriesById[entryId], -1, _activity.AppTask);
                return true;
            }

            public bool OnSuggestionSelect(int position)
            {
                return false;
            }
        }

        class OnQueryTextListener : Java.Lang.Object, SearchView.IOnQueryTextListener
        {
            private readonly GroupBaseActivity _activity;

            public OnQueryTextListener(GroupBaseActivity activity)
            {
                _activity = activity;
            }

            public bool OnQueryTextChange(string newText)
            {
                return false;
            }

            public bool OnQueryTextSubmit(string query)
            {
                if (String.IsNullOrEmpty(query))
                    return false; //let the default happen

                Intent searchIntent = new Intent(_activity, typeof(search.SearchResults));
                searchIntent.SetAction(Intent.ActionSearch); //currently not necessary to set because SearchResults doesn't care, but let's be as close to the default as possible
                searchIntent.PutExtra(SearchManager.Query, query);
                //forward appTask:
                _activity.AppTask.ToIntent(searchIntent);

                _activity.StartActivityForResult(searchIntent, 0);

                return true;
            }
        }

        public override bool OnCreateOptionsMenu(IMenu menu)
        {

            MenuInflater inflater = MenuInflater;
            inflater.Inflate(Resource.Menu.group, menu);
            var searchManager = (SearchManager)GetSystemService(Context.SearchService);

            /*This is the start of a pretty hacky workaround to avoid a crash on Samsung devices with Android 9.
             * The crash stacktrace is pretty unspecific (see https://stackoverflow.com/questions/54530604/app-crash-but-no-app-specific-code-in-stack-trace)
             * It points to InputMethodService.java which seems to be modified by Samsung. Hard to tell what's going on.
             * The problem only occurs, if our own keyboard is activated.
             * Users found that the crash does not appear if another activity was launched and closed before activating search view.
             * That's what we do as a workaround: We display another search menu option in case a crash would occur. When that search option is clicked,
             * we launch an activity which immediately finished. In the activity result, we can activate the search view safely.
             * If anybody reading this has a better idea, please let me know :-)
             */
            searchItem = menu.FindItem(Resource.Id.menu_search);
            searchItemDummy = menu.FindItem(Resource.Id.menu_search_dummy);
            SetSearchItemVisibility();


            var view = searchItem.ActionView;

            searchView = view.JavaCast<SearchView>();

            searchView.SetSearchableInfo(searchManager.GetSearchableInfo(ComponentName));
            searchView.SetOnSuggestionListener(new SuggestionListener(searchView.SuggestionsAdapter, this, searchItem));
            searchView.SetOnQueryTextListener(new OnQueryTextListener(this));
            
            if (_prefs.GetBoolean("ActivateSearchView", false) && AppTask.CanActivateSearchViewOnStart)
            {

                //need to use PostDelayed, otherwise the menu_lock item completely disappears
                searchView.PostDelayed(ActivateSearchView, 500);
            }

            ActionBar.LayoutParams lparams = new ActionBar.LayoutParams(ActionBar.LayoutParams.MatchParent,
                ActionBar.LayoutParams.MatchParent);
            searchView.LayoutParameters = lparams;


            _syncItem = menu.FindItem(Resource.Id.menu_sync);


            _offlineItem = menu.FindItem(Resource.Id.menu_work_offline);
            _onlineItem = menu.FindItem(Resource.Id.menu_work_online);

            UpdateOfflineModeMenu();


            return base.OnCreateOptionsMenu(menu);

        }

        private void SetSearchItemVisibility()
        {
            if ((searchItem == null) || (searchItemDummy == null))
                return;
            if (Build.Manufacturer.ToLowerInvariant() == "samsung" && ((int) Build.VERSION.SdkInt >= 28) && (IsKp2aKeyboardActive()) && !hasCalledOtherActivity)
            {
                searchItem.SetVisible(false);
                searchItemDummy.SetVisible(true);
            }
            else
            {
                searchItem.SetVisible(true);
                searchItemDummy.SetVisible(false);
            }
        }

        private string Kp2aInputMethodName
        {
            get { return PackageName + "/keepass2android.softkeyboard.KP2AKeyboard"; }
        }
        private bool IsKp2aKeyboardActive()
        {
            string currentIme = Android.Provider.Settings.Secure.GetString(
                ContentResolver,
                Android.Provider.Settings.Secure.DefaultInputMethod);
            
            return Kp2aInputMethodName == currentIme;

        }

        private void ActivateSearchView()
        {
            
                searchView.Iconified = false;
                AppTask.CanActivateSearchViewOnStart = false;
                
            
        }

        private void UpdateOfflineModeMenu()
        {
            try
            {
                if (_syncItem != null)
                {
                    if (((App.Kp2a.OpenDatabases.Count() == 1)  || (EntriesBelongToCurrentDatabaseOnly))
                        && App.Kp2a.CurrentDb.Ioc.IsLocalFile())
                        _syncItem.SetVisible(false);
                    else
                        _syncItem.SetVisible(!App.Kp2a.OfflineMode);
                }

                if (((App.Kp2a.OpenDatabases.Count() == 1) || (EntriesBelongToCurrentDatabaseOnly))
                    &&  (App.Kp2a.GetFileStorage(App.Kp2a.CurrentDb.Ioc) is IOfflineSwitchable))
                {
                    _offlineItem?.SetVisible(App.Kp2a.OfflineMode == false);
                    _onlineItem?.SetVisible(App.Kp2a.OfflineMode);
                }
                else
                {
                    _offlineItem?.SetVisible(false);
                    _onlineItem?.SetVisible(false);
                }
            }
            catch (Exception e)
            {
                Kp2aLog.LogUnexpectedError(new Exception("Cannot UpdateOfflineModeMenu " + (App.Kp2a == null) + " " + ((App.Kp2a == null) || (App.Kp2a.CurrentDb== null)) + " " + (((App.Kp2a == null) || (App.Kp2a.CurrentDb== null) || (App.Kp2a.CurrentDb.Ioc == null)) + " " + (_syncItem != null) + " " + (_offlineItem != null) + " " + (_onlineItem != null))));
            }

        }

        public abstract bool EntriesBelongToCurrentDatabaseOnly { get; }

        public abstract ElementAndDatabaseId FullGroupId { get; }

        public virtual bool MayPreviewTotp
        {
            get
            {
                return !PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(GetString(Resource.String.masktotp_key),
                    Resources.GetBoolean(Resource.Boolean.masktotp_default));
            }
        }


        public override bool OnPrepareOptionsMenu(IMenu menu)
        {
            if (!base.OnPrepareOptionsMenu(menu))
            {
                return false;
            }

            Util.PrepareDonateOptionMenu(menu, this);


            return true;
        }

        public override bool OnOptionsItemSelected(IMenuItem item)
        {
            switch (item.ItemId)
            {
                case Resource.Id.menu_donate:
                    return Util.GotoDonateUrl(this);
                case Resource.Id.menu_lock:
                    App.Kp2a.Lock();
                    Finish();
                    return true;
                case Resource.Id.menu_search_dummy:
                    StartActivityForResult(typeof(CloseImmediatelyActivity), RequestCodeActivateRealSearch);
                    OverridePendingTransition(0, 0);
                    hasCalledOtherActivity = true;
                    //TODO transition?

                    return true;
                case Resource.Id.menu_search:
                case Resource.Id.menu_search_advanced:
                    OnSearchRequested();
                    return true;

                case Resource.Id.menu_app_settings:
                    DatabaseSettingsActivity.Launch(this);
                    return true;

                case Resource.Id.menu_sync:
                    new SyncUtil(this).SynchronizeDatabase(() => { });
                    return true;

                case Resource.Id.menu_work_offline:
                    App.Kp2a.OfflineMode = App.Kp2a.OfflineModePreference = true;
                    UpdateOfflineModeMenu();
                    return true;

                case Resource.Id.menu_work_online:
                    App.Kp2a.OfflineMode = App.Kp2a.OfflineModePreference = false;
                    UpdateOfflineModeMenu();
                    new SyncUtil(this).SynchronizeDatabase(() => { });
                    return true;
                case Resource.Id.menu_open_other_db:
                    AppTask.SetActivityResult(this, KeePass.ExitLoadAnotherDb);
                    Finish();
                    return true;
                case Resource.Id.menu_sort:
                    ChangeSort();
                    return true;
                case Android.Resource.Id.Home:
                    //Currently the action bar only displays the home button when we come from a previous activity.
                    //So we can simply Finish. See this page for information on how to do this in more general (future?) cases:
                    //http://developer.android.com/training/implementing-navigation/ancestral.html
                    AppTask.SetActivityResult(this, KeePass.ExitNormal);
                    Finish();
                    //OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);

                    return true;
            }

            return base.OnOptionsItemSelected(item);
        }

    

        public override void OnBackPressed()
        {
            AppTask.SetActivityResult(this, KeePass.ExitNormal);
            base.OnBackPressed();
        }

        private void ChangeSort()
        {
            var sortOrderManager = new GroupViewSortOrderManager(this);
            IEnumerable<string> sortOptions = sortOrderManager.SortOrders.Select(
                o => GetString(o.ResourceId)
                );

            int selectedBefore = sortOrderManager.GetCurrentSortOrderIndex();

            new MaterialAlertDialogBuilder(this)
                .SetSingleChoiceItems(sortOptions.ToArray(), selectedBefore, (sender, args) =>
                    {
                        int selectedAfter = args.Which;

                        sortOrderManager.SetNewSortOrder(selectedAfter);
                        // Refresh menu titles
                        ActivityCompat.InvalidateOptionsMenu(this);

                        // Mark all groups as dirty now to refresh them on load
                        
                        App.Kp2a.MarkAllGroupsAsDirty();
                        // We'll manually refresh this group so we can remove it
                        App.Kp2a.DirtyGroups.Remove(Group);

                        // Tell the adapter to refresh it's list

                        BaseAdapter adapter = (BaseAdapter)ListAdapter;
                        adapter.NotifyDataSetChanged();


                    })
                    .SetPositiveButton(Android.Resource.String.Ok, (sender, args) => ((Dialog)sender).Dismiss())
                    .Show();




        }

        public class RefreshTask : OnFinish
        {
            public RefreshTask(Handler handler, GroupBaseActivity act)
                : base(act, handler)
            {
            }

            public override void Run()
            {
                if (Success)
                {
                    ((GroupBaseActivity)ActiveActivity)?.RefreshIfDirty();
                }
                else
                {
                    DisplayMessage(ActiveActivity);
                }
            }
        }
        public class AfterDeleteGroup : OnFinish
        {
            public AfterDeleteGroup(Handler handler, GroupBaseActivity act)
                : base(act, handler)
            {
            }


            public override void Run()
            {
                if (Success)
                {
                    ((GroupBaseActivity)ActiveActivity)?.RefreshIfDirty();
                }
                else
                {
                    Handler.Post(() =>
                    {
                        Toast.MakeText(ActiveActivity ?? LocaleManager.LocalizedAppContext, "Unrecoverable error: " + Message, ToastLength.Long).Show();
                    });

                    App.Kp2a.Lock(false);
                }
            }

        }

        public bool IsBeingMoved(PwUuid uuid)
        {
            MoveElementsTask moveElementsTask = AppTask as MoveElementsTask;
            if (moveElementsTask != null)
            {
                if (moveElementsTask.Uuids.Any(uuidMoved => uuidMoved.Equals(uuid)))
                    return true;
            }
            return false;
        }

        public void StartTask(AppTask task)
        {
            AppTask = task;
            task.StartInGroupActivity(this);
        }


        public void StartMovingElements()
        {

            ShowInsertElementsButtons();
            BaseAdapter adapter = (BaseAdapter)ListAdapter;
            adapter.NotifyDataSetChanged();
        }

        public void ShowInsertElementsButtons()
        {
            FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
            FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone;
            FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone;
            FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Gone;
            FindViewById(Resource.Id.fabSearch).Visibility = ViewStates.Gone;
            FindViewById(Resource.Id.fabTotpOverview).Visibility = ViewStates.Gone;

            UpdateBottomBarElementVisibility(Resource.Id.insert_element, true);
            UpdateBottomBarElementVisibility(Resource.Id.cancel_insert_element, true);

        }

        public void StopMovingElements()
        {
            try
            {
                MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask;
                foreach (var uuid in moveElementsTask.Uuids)
                {
                    IStructureItem elementToMove = App.Kp2a.FindStructureItem(uuid);
                    if (elementToMove.ParentGroup != Group)
                        App.Kp2a.DirtyGroups.Add(elementToMove.ParentGroup);
                }
            }
            catch (Exception e)
            {
                //don't crash if adding to dirty fails but log the exception:
                Kp2aLog.LogUnexpectedError(e);
            }

            AppTask = new NullTask();
            AppTask.SetupGroupBaseActivityButtons(this);
            BaseAdapter adapter = (BaseAdapter)ListAdapter;
            adapter.NotifyDataSetChanged();
        }


        public void EditGroup(PwGroup pwGroup)
        {
            GroupEditActivity.Launch(this, pwGroup.ParentGroup, pwGroup);
        }
    }

    public class GroupListFragment : ListFragment, AbsListView.IMultiChoiceModeListener
    {
        private ActionMode _mode;
        private int _statusBarColor;

        public override void OnActivityCreated(Bundle savedInstanceState)
        {
            base.OnActivityCreated(savedInstanceState);

            ListView.SetMultiChoiceModeListener(this);
            if (App.Kp2a.OpenDatabases.Any(db => db.CanWrite))
            {
                ListView.ChoiceMode = ChoiceMode.MultipleModal;

                ListView.ItemLongClick += delegate(object sender, AdapterView.ItemLongClickEventArgs args)
                {
                    ListView.SetItemChecked(args.Position, true);
                };

            }
            else
            {
                ListView.ChoiceMode = ChoiceMode.None;
            }

            ListView.ItemClick += (sender, args) => ((GroupListItemView)args.View).OnClick();
            

            StyleListView();

        }

        protected void StyleListView()
        {
            ListView lv = ListView;
            lv.ScrollBarStyle = ScrollbarStyles.InsideInset;
            lv.TextFilterEnabled = true;

            lv.Divider = null;
        }

        public bool OnActionItemClicked(ActionMode mode, IMenuItem item)
        {
            var listView = FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListView;
            var checkedItemPositions = listView.CheckedItemPositions;

            List<IStructureItem> checkedItems = new List<IStructureItem>();
            for (int i = 0; i < checkedItemPositions.Size(); i++)
            {
                if (checkedItemPositions.ValueAt(i))
                {
                    checkedItems.Add(((PwGroupListAdapter)ListAdapter).GetItemAtPosition(checkedItemPositions.KeyAt(i)));
                }
            }

            //shouldn't happen, just in case...
            if (!checkedItems.Any())
            {
                return false;
            }
            Handler handler = new Handler();
            switch (item.ItemId)
            {

                case Resource.Id.menu_delete:
                    DeleteMultipleItems((GroupBaseActivity)Activity, checkedItems, new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a);
                    break;
                case Resource.Id.menu_move:
                    var navMove = new NavigateToFolderAndLaunchMoveElementTask(App.Kp2a.CurrentDb, checkedItems.First().ParentGroup, checkedItems.Select(i => i.Uuid).ToList(), ((GroupBaseActivity)Activity).IsSearchResult);
                    ((GroupBaseActivity)Activity).StartTask(navMove);
                    break;
                case Resource.Id.menu_copy:

                    var copyTask = new CopyEntry((GroupBaseActivity)Activity, App.Kp2a, (PwEntry)checkedItems.First(),
                        new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a.CurrentDb);

                    ProgressTask pt = new ProgressTask(App.Kp2a, Activity, copyTask);
                    pt.Run();
                    break;

                case Resource.Id.menu_navigate:
                    NavigateToFolder navNavigate = new NavigateToFolder(App.Kp2a.CurrentDb, checkedItems.First().ParentGroup, true);
                    ((GroupBaseActivity)Activity).StartTask(navNavigate);
                    break;
                case Resource.Id.menu_edit:
                    GroupEditActivity.Launch(Activity, checkedItems.First().ParentGroup, (PwGroup)checkedItems.First());
                    break;
                default:
                    return false;


            }
            listView.ClearChoices();
            ((BaseAdapter)ListAdapter).NotifyDataSetChanged();
            if (_mode != null)
                mode.Finish();

            return true;
        }

        public bool OnCreateActionMode(ActionMode mode, IMenu menu)
        {
            MenuInflater inflater = Activity.MenuInflater;
            inflater.Inflate(Resource.Menu.group_entriesselected, menu);
            //mode.Title = "Select Items";
            Android.Util.Log.Debug("KP2A", "Create action mode" + mode);
            ((PwGroupListAdapter)ListView.Adapter).InActionMode = true;
            ((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged();
            _mode = mode;
            if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
            {
                _statusBarColor = Activity.Window.StatusBarColor;
                Activity.Window.SetStatusBarColor(Activity.Resources.GetColor(Resource.Color.md_theme_secondary));
            }
            return true;
        }

        public void OnDestroyActionMode(ActionMode mode)
        {
            Android.Util.Log.Debug("KP2A", "Destroy action mode" + mode);
            ((PwGroupListAdapter)ListView.Adapter).InActionMode = false;
            ((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged();
            _mode = null;
            if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
            {
                Activity.Window.SetStatusBarColor(new Android.Graphics.Color(_statusBarColor));
            }
        }

        public bool OnPrepareActionMode(ActionMode mode, IMenu menu)
        {
            Android.Util.Log.Debug("KP2A", "Prepare action mode" + mode);
            ((PwGroupListAdapter)ListView.Adapter).InActionMode = mode != null;
            ((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged();
            UpdateMenuItemVisibilities(mode);
            return true;
        }

        public void OnItemCheckedStateChanged(ActionMode mode, int position, long id, bool @checked)
        {
            UpdateMenuItemVisibilities(mode);
        }

        private void UpdateMenuItemVisibilities(ActionMode mode)
        {
            var menuItem = mode.Menu.FindItem(Resource.Id.menu_edit);
            if (menuItem != null)
            {
                menuItem.SetVisible(IsOnlyOneGroupChecked());
            }

            menuItem = mode.Menu.FindItem(Resource.Id.menu_navigate);
            if (menuItem != null)
            {
                menuItem.SetVisible(((GroupBaseActivity) Activity).IsSearchResult && IsOnlyOneItemChecked());
            }

            menuItem = mode.Menu.FindItem(Resource.Id.menu_copy);
            if (menuItem != null)
            {
                menuItem.SetVisible(IsOnlyOneEntryChecked());
            }
        }

        private bool IsOnlyOneGroupChecked()
        {
            var checkedItems = ListView.CheckedItemPositions;
            bool hadCheckedGroup = false;
            if (checkedItems != null)
            {
                for (int i = 0; i < checkedItems.Size(); i++)
                {
                    if (checkedItems.ValueAt(i))
                    {
                        if (hadCheckedGroup)
                        {
                            return false;
                        }

                        if (((PwGroupListAdapter)ListAdapter).IsGroupAtPosition(checkedItems.KeyAt(i)))
                        {
                            hadCheckedGroup = true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                }
            }
            return hadCheckedGroup;
        }

        private bool IsOnlyOneItemChecked()
        {
            var checkedItems = ListView.CheckedItemPositions;
            bool hadCheckedItem = false;
            if (checkedItems != null)
            {
                for (int i = 0; i < checkedItems.Size(); i++)
                {
                    if (checkedItems.ValueAt(i))
                    {
                        if (hadCheckedItem)
                        {
                            return false;
                        }

                        hadCheckedItem = true;
                    }
                }
            }
            return hadCheckedItem;
        }

        private bool IsOnlyOneEntryChecked()
        {
            return IsOnlyOneItemChecked() && !IsOnlyOneGroupChecked();
        }


        public void  DeleteMultipleItems(GroupBaseActivity activity, List<IStructureItem> checkedItems, OnFinish onFinish, Kp2aApp app)
        {
            if (checkedItems.Any() == false)
                return;
            //sort checkedItems by database
            List<KeyValuePair<Database, List<IStructureItem>>> itemsForDatabases =
                new List<KeyValuePair<Database, List<IStructureItem>>>();
            foreach (var item in checkedItems)
            {
                var db = app.FindDatabaseForElement(item);
                if (db != null)
                {
                    bool foundDatabase = false;
                    foreach (var listEntry in itemsForDatabases)
                    {
                        if (listEntry.Key == db)
                        {
                            foundDatabase = true;
                            listEntry.Value.Add(item);
                            break;
                        }
                    }
                    if (!foundDatabase)
                    {
                        itemsForDatabases.Add(new KeyValuePair<Database, List<IStructureItem>>(db, new List<IStructureItem> { item }));
                    }
                }

            }

            int dbIndex = 0;
            Action<bool, string, Activity> action = null;
            action = (success, message, activeActivity) =>
            {
                if (success)
                {
                    dbIndex++;
                    if (dbIndex == itemsForDatabases.Count)
                    {
                        onFinish.SetResult(true);
                        onFinish.Run();
                        return;
                    }
                    new DeleteMultipleItemsFromOneDatabase(activity, itemsForDatabases[dbIndex].Key,
                        itemsForDatabases[dbIndex].Value, new ActionOnFinish(activeActivity, (b, s, activity1) => action(b, s, activity1)), app)
                        .Start();
                }
                else
                {
                    onFinish.SetResult(false, message, true, null);
                }
            };

            new DeleteMultipleItemsFromOneDatabase(activity, itemsForDatabases[dbIndex].Key,
                itemsForDatabases[dbIndex].Value, new ActionOnFinish(activity, (b, s, activity1) => action(b, s, activity1)), app)
                .Start();
        }

    }
}

