package com.quaap.launchtime;

/*
 * Copyright (C) 2017   Tom Kliethermes
 *
 * This file is part of LaunchTime and is 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.
 *
 * This program 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.
 */

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;


import android.content.pm.PackageManager;

import android.content.pm.ResolveInfo;

import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;

import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;

import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;

import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;

import android.view.animation.Animation;

import android.view.animation.ScaleAnimation;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;

import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.GridLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;

import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.quaap.launchtime.apps.AppLauncher;
import com.quaap.launchtime.apps.Badger;
import com.quaap.launchtime.apps.DefaultApps;
import com.quaap.launchtime.apps.LaunchApp;
import com.quaap.launchtime.components.Theme;
import com.quaap.launchtime.ui.InteractiveScrollView;
import com.quaap.launchtime.ui.ActionMenu;
import com.quaap.launchtime.components.Categories;
import com.quaap.launchtime.components.ExceptionHandler;
import com.quaap.launchtime.components.IconsHandler;
import com.quaap.launchtime.ui.MsgBox;
import com.quaap.launchtime.db.DB;
import com.quaap.launchtime.ui.QuickRow;
import com.quaap.launchtime.ui.SearchBox;
import com.quaap.launchtime.ui.Style;
import com.quaap.launchtime.ui.Style.AnimateDirection;
import com.quaap.launchtime.widgets.Widget;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import static android.view.Gravity.LEFT;

public class MainActivity extends Activity implements
        View.OnLongClickListener, SharedPreferences.OnSharedPreferenceChangeListener,
        Badger.BadgerCountChangeListener, Widget.WidgetChangedListener {

    // Things are getting very messy.  That's what happens when you figure it out as you go along.
    //TODO: everything needs a major refactor.


    private static final int UNINSTALL_RESULT = 3454;

    private LinearLayout mIconsArea;
    private LinearLayout mIconSheetTopFrame;
    private InteractiveScrollView mIconSheetScroller;
    private ViewGroup mIconSheetBottomFrame;
    private ViewGroup mIconSheetHolder;
    private Map<String, GridLayout> mIconSheets;

    private GridLayout mIconSheet;

    private InteractiveScrollView mCategoriesScroller;
    private Map<String, TextView> mCategoryTabs;
    private Map<View, String> mRevCategoryMap;
    private View mCategoryTabsWrap;
    private volatile String mCategory;
    private ImageView mShowButtons;
    private ImageView mHideButtons;
    private ImageView mShowCats;


    private TextView mSortCategoryButton;
    private TextView mAddCategoryButton;
    private TextView mRenameCategoryButton;
    private TextView mEditWidgetsButton;
    private ImageView mOpenPrefsButton;
    private ImageView mOpenPrefs2Button;


    private LinearLayout mCategoriesLayout;
    private TextView mRemoveAppText;
    private FrameLayout mRemoveDropzone;
    private FrameLayout mLinkDropzone;
    private FrameLayout mLinkDropzonePeek;
    private PackageManager mPackageMan;
    private AppLauncher mBeingDragged;
    private volatile ViewGroup mDragDropSource;
    private SharedPreferences mPrefs;
    private View mBeingUninstalled;
    private Widget mWidgetHelper;

    private Animation itemClickedAnim;

    private String mCategoryJustCreated;

    private int mColumns = 3;


    public Point mScreenDim;

    public SharedPreferences mAppPreferences;


    private final Map<AppLauncher,ViewGroup> mAppLauncherViews = Collections.synchronizedMap(new HashMap<AppLauncher,ViewGroup>());

    private boolean mChildLock;
    private boolean mChildLockSetup;

    private boolean mDumbMode;

    private LaunchApp mLaunchApp;

    private QuickRow mQuickRow;

    private ProgressBar mProgressBar;


    //private DB db();

    private SearchBox mSearchBox;
    private TextView mCategoryLabel;

    private Style mStyle;

    private AddIconHandler iconHandler;


    private static final String TAG = "LaunchTime";

    private static String latestCategory;

    private int mAnimationDuration = 150;

    private ActionMenu mActionMenu;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");

        if (GlobState.enableCrashReporter && !BuildConfig.DEBUG) Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this));

        setContentView(R.layout.activity_main);

        ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.hide();
        }


        //Setup some of our globals utils

        iconHandler = new AddIconHandler(this);

        mPackageMan = getApplicationContext().getPackageManager();
        mAppPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

        mPrefs = getSharedPreferences("default", MODE_PRIVATE);

        mWidgetHelper = GlobState.getWidgetHelper(this);
        mWidgetHelper.addWidgetChangedListener(this);

        mQuickRow = new QuickRow(mMainDragListener, this);

        mScreenDim = getScreenDimensions();


        mActionMenu = new ActionMenu(this);

        mStyle = GlobState.getStyle(this);

        //Load resources and init the form members
        initUI();

        mSearchBox = new SearchBox(this, mIconSheetScroller);

        mLaunchApp = new LaunchApp(this);

        mInitCalled = false;
        mInitCalling = false;
    }

    private StartupTask mStartupTask;
    private boolean mInitCalled = false;
    private boolean mInitCalling = false;

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
//        prefsUpdate = 0;

        //in case the task didn't complete
        int startat = 0;
        if (mStartupTask!=null && mStartupTask.isCancelled() && !mStartupTask.isCompleted()) {
            startat = mStartupTask.mCompletedStage;
            mInitCalled = false;
            mInitCalling = false;
        }


        if (mInitCalling) return;
        if (mInitCalled) {
            iconHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                   myResume();
                }
            },100);
        } else {
            mStartupTask = new StartupTask(this, true, startat);
            mStartupTask.executeOnExecutor(GlobState.getExecutor(this));
            //mStartupTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }

    }


    private void myResume() {
        //Check how long we've been gone
        long pausetime = mPrefs.getLong("pausetime", -1);
        int homesetting = Integer.parseInt(getPreference(R.string.pref_key_return_home, "9999999"));


        //We go "home" if it's been longer than the timeout
        boolean skiphome = false;
        if (pausetime>-1 && System.currentTimeMillis() - pausetime > homesetting*1000 && !mChildLock) {
            mCategory = getTopCategory();
            skiphome = true;
            mQuickRow.scrollToStart();
            mCategoriesScroller.smoothScrollTo(0, 0);
        } else if (mCategory==null) {
            mCategory = mPrefs.getString("category", getTopCategory());
        }

        // If the category has been deleted, pick a known-good category
        if (mCategory==null || db().getCategoryDisplay(mCategory)==null) {
            mCategory = Categories.CAT_TALK;
        }
        switchCategory(mCategory);

        if (!skiphome) {
            //move the page to the right scroll position
            iconHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mIconSheetScroller.scrollTo(0, mPrefs.getInt("scrollpos" + mCategory, 0));
                    scrollToCategoryTab();
                    showButtonBar(false, true);

                }
            }, 100);
        }

        //rerun our query if needed
        if (mCategory.equals(Categories.CAT_SEARCH)) {
            mSearchBox.refreshSearch(false);
        }

        hideRemoveDropzone();

        hideCatsIfAutoHide(true);
        showButtonBar(false, true);
        //lock things up if it was in toddler mode
        checkChildLock();

//        PopupWindow pw = new PopupWindow(this);
//        int color = Color.parseColor("#CCCCCCCC");
//        ColorChooser cc = new ColorChooser(this, color);
//        cc.setColor(color);
//        pw.setContentView(cc);
//        pw.showAsDropDown(mIconSheetScroller);

    }



    private long mPauseTime = 0;

    @SuppressLint("ApplySharedPref")
    @Override
    protected void onPause() {
        Log.d(TAG, "onPause");
        mBackPressedSessionCount=0;
        prefsUpdate = 0;
        checkChildLock();

//        try {
//            if (mStartupTask!=null && !mStartupTask.isCancelled()) {
//                mStartupTask.cancel(true);
//                //mStartupTask = null;
//            }
//        } catch (Throwable t) {
//            Log.e(TAG, t.getMessage(), t);
//        }

        //save a few items
        mPrefs.edit()
                .putInt("scrollpos" + mCategory, mIconSheetScroller.getScrollY())
                .putString("category", mCategory)
                .putLong("pausetime", System.currentTimeMillis())
                .commit();

        //close our search cursor, if needed
        mSearchBox.closeSeachAdapter();
        mPauseTime = System.currentTimeMillis();



        super.onPause();
    }

    private static class StartupTask extends AsyncTask<Void,Integer,List<AppLauncher>> {

        private final WeakReference<MainActivity> mMain;
        private final boolean mShowProgress;

        final private int mStartAtStage;
        int mCompletedStage = 0;
        final int mStages = 6;

        StartupTask(MainActivity main, boolean showProgress, int startAtStage) {
            mMain = new WeakReference<>(main);
            mShowProgress = showProgress;
            main.mInitCalling = true;
            mStartAtStage = startAtStage;

        }

        boolean isCompleted() {
            return mCompletedStage == mStages;
        }

        @Override
        protected List<AppLauncher> doInBackground(Void... voids) {
            final MainActivity main = mMain.get();
            if (main == null) return null;
            try {

                if (main.mInitCalled) return null;

                if (mShowProgress) {
                    main.showProgressBar(100);
                    main.incProgressBar(1);
                    Thread.yield();
                }

                if (mStartAtStage<1) {
                    stage1(main);
                    mCompletedStage = 1;
                }

                if (mStartAtStage<2) {
                    stage2(main);
                    mCompletedStage = 2;
                }

                return main.processActivities(mShowProgress);
            } catch (Throwable t) {
                Log.e(TAG, t.getMessage(), t);
            } finally {
                main.mInitCalled = true;
            }
            return null;
        }

        private void stage1(MainActivity main) {
            DB db = GlobState.getGlobState(main).getDB();
            String key = "current";
            int lastvalue = main.mPrefs.getInt(key,0);

            if (db.isFirstRun()) {
                main.mAppPreferences.edit()
                        .putBoolean(main.getString(R.string.pref_key_show_action_menus), Build.VERSION.SDK_INT >= 25)
                        .putBoolean(main.getString(R.string.pref_key_show_action_extra), Build.VERSION.SDK_INT >= 25)
                        .apply();
                lastvalue = 0;
            }

            if (lastvalue < 830) {
                main.mPrefs.edit().putInt(key,830).apply();

                if (main.getPreference(R.string.pref_key_animate_duration, "150").equals("250")) {
                    main.mAppPreferences.edit().putString(main.getString(R.string.pref_key_animate_duration), "150").apply();
                }

                WindowManager wm = ((WindowManager) main.getSystemService(Context.WINDOW_SERVICE));

                int orientationPref = Integer.parseInt(main.getPreference(R.string.pref_key_orientation, "-1"));
                if (orientationPref==-1 && wm!=null) {

                    Display display = wm.getDefaultDisplay();
                    int rotation = display.getRotation();
                    int orientation = main.getResources().getConfiguration().orientation;


                    switch (rotation) {
                        case Surface.ROTATION_180:
                        case Surface.ROTATION_0:
                            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                                orientationPref = 3;
                            } else {
                                orientationPref = 2;
                            }
                            break;
                        case Surface.ROTATION_270:
                        case Surface.ROTATION_90:
                        default:
                            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                                orientationPref = 3;
                            } else {
                                orientationPref = 2;
                            }

                            break;
                    }


                    main.mAppPreferences.edit().putString(main.getString(R.string.pref_key_orientation), orientationPref + "").apply();
                }

                main.mWidgetHelper.delete();
            }
        }

        private void stage2(MainActivity main) {
            if (mShowProgress) main.incProgressBar(1);

            main.init(mShowProgress);
            Thread.yield();
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            final MainActivity main = mMain.get();
            if (main == null) return;
            main.mInitCalling = false;
            main.hideProgressBar();
        }

        @Override
        protected void onPostExecute(List<AppLauncher> launchers) {
            final MainActivity main = mMain.get();
            if (main == null) return;

            mCompletedStage = 3;


            try {

                if (launchers != null) {
                    if (mShowProgress) main.showProgressBar(5);

                    if (mStartAtStage<4) {
                        main.mQuickRow.processQuickApps(launchers);
                        main.mQuickRow.repopulate();

                        //main.db().setAppCategoryOrder(main.mRevCategoryMap.get(main.mQuickRow.getGridLayout()), main.mQuickRow.getGridLayout());
                        mCompletedStage = 4;
                    }

//                    if (mShowProgress) main.incProgressBar(1);
//
//                    if (main.mCategory.equals(Categories.CAT_SEARCH)) {
//                        main.populateRecentApps();
//                    } else {
//                        main.repopulateIconSheet(main.mCategory);
//                    }
                    if (mShowProgress) main.incProgressBar(1);

                    main.firstRunPostApps();
                    mCompletedStage = 5;

                    if (mShowProgress) main.incProgressBar(1);
                }

                if (mShowProgress) main.incProgressBar(1);

                main.iconHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        main.myResume();
                    }
                },100);
                mCompletedStage = 6;
                if (mShowProgress) main.incProgressBar(1);
                main.mStartupTask = null;

            } catch (Throwable t) {
                Log.e(TAG, t.getMessage(), t);
            } finally {
                try {
                    main.mInitCalling = false;
                    main.mStartupTask = null;
                    main.hideProgressBar();
                } catch (Throwable t) {
                    Log.e(TAG, t.getMessage(), t);
                }
            }
        }
    }


    private void init(boolean progress) {

        if (progress) setProgressBarMax(10);

        mCategory = mPrefs.getString("category", getTopCategory());
        latestCategory = mCategory;

        GlobState.getBadger(this).setBadgerCountChangeListener(this);

        mAppPreferences.registerOnSharedPreferenceChangeListener(this);

        if (progress) incProgressBar(1);

        readPrefs();
        //create the grids for each existing category.
        if (progress) incProgressBar(1);

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mCategoriesLayout.removeAllViews();
            }
        });
        for (final String category : db().getCategories()) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    createIconSheet(category, db().isFirstRun()?-1:-2);
                }
            });
            Thread.yield();
        }

        if (progress) incProgressBar(1);
        if (!db().isFirstRun()) {
            //Make sure the displayed icons load first
            //Load the quickrow icons first
            for (ComponentName actvname : db().getAppCategoryOrder(QuickRow.QUICK_ROW_CAT)) {
                if (db().isAppInstalled(actvname)) {
                    AppLauncher app = db().getApp(actvname);
                    if (app != null) {
                        app.loadAppIconAsync(this);
                    }
                }
            }
            if (progress) incProgressBar(1);
            //Load the selected category icons
            for (ComponentName actvname : db().getAppCategoryOrder(mCategory)) {
                if (db().isAppInstalled(actvname)) {
                    AppLauncher app = db().getApp(actvname);
                    if (app != null) {
                        app.loadAppIconAsync(this);
                    }
                }
            }
            if (progress) incProgressBar(1);
        }
    }


    private List<AppLauncher> processActivities(boolean showProgress) {
        final List<AppLauncher> launchers = new ArrayList<>();

        final List<ComponentName> dbactvnames = db().getAppNames();

        Set<ComponentName> pmactvnames = new HashSet<>();
        List<AppLauncher> newapps = new ArrayList<>();

        // Set MAIN and LAUNCHER filters, so we only get activities with that defined on their manifest
        Intent intent = new Intent(Intent.ACTION_MAIN, null);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);

        if (showProgress) setProgressBarMax(dbactvnames.size()+2);

        // Get all activities that have those filters
        final List<ResolveInfo> activities;

        try {
            if (showProgress) incProgressBar(1);
            activities = mPackageMan.queryIntentActivities(intent, PackageManager.GET_META_DATA);
            if (showProgress) incProgressBar(1);
        } catch (final Exception e) {
            Log.e(TAG, "Problem getting app list: " + e.getLocalizedMessage(), e);
            iconHandler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "Problem getting app list: " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
                }
            });
            return launchers;
        }

        if (showProgress) setProgressBarMax(activities.size() + dbactvnames.size()+2);

        for (int i = 0; i < activities.size(); i++) {

            if (showProgress && i%5==0) {
                incProgressBar(5);
            }
            try {
                AppLauncher app;

                ResolveInfo ri = activities.get(i);

                if (ri == null || ri.activityInfo==null || !ri.activityInfo.exported) {
                    continue;
                }

                String actvname = ri.activityInfo.name;
                ComponentName appcn = new ComponentName(ri.activityInfo.packageName, actvname);

                int enabledflag = mPackageMan.getComponentEnabledSetting(appcn);

                if (!(enabledflag == PackageManager.COMPONENT_ENABLED_STATE_ENABLED || (enabledflag==PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && ri.activityInfo.enabled))) {
                    continue;
                }

                if (!pmactvnames.contains(appcn)) {
                    pmactvnames.add(appcn);

                    app = db().getApp(appcn);

                    if (dbactvnames.contains(appcn) && app != null) {
                        app.loadAppIconAsync(this);
                        String label = ri.loadLabel(mPackageMan).toString();
                        if (app.getLabel()==null || !app.getLabel().equals(label)) {
                            db().updateAppLabel(ri.activityInfo.packageName, actvname, label);
                            app.setLabel(label);
                        }

                        //  Log.d(TAG, "app was in db " + actvname + " " +  ri.activityInfo.packageName);
                    } else {
                        //  Log.d(TAG, "app was not in db " + actvname + " " +  ri.activityInfo.packageName);
                        app = AppLauncher.createAppLauncher(this, mPackageMan, ri);
                        newapps.add(app);
                    }

                    launchers.add(app);


                }
            } catch (Exception e) {
                Log.e(TAG, e.getMessage(), e);
            }

        }
        if (showProgress) setProgressBar(activities.size());

        //remove launchers if they are not in the system
        for (Iterator<ComponentName> it = dbactvnames.iterator(); it.hasNext(); ) {
            ComponentName dbactv = it.next();
            if (!pmactvnames.contains(dbactv)) {
                AppLauncher app = db().getApp(dbactv);
                if (app==null || !isAppInstalled(app.getPackageName())) {  //might be a widget, check packagename
                    Log.d(TAG, "Removing " + dbactv);
                    it.remove();
                    db().deleteApp(dbactv);
                    // removeFromQuickApps(dbactv);
                }
            }
            if (showProgress) incProgressBar(1);
        }

        db().addApps(newapps);


        return launchers;
    }


    private void firstRunPostApps() {
        if (db().isFirstRun()) {
            String selfAct = this.getPackageName() + "." + this.getClass().getSimpleName();
            Log.d(TAG, "My name is " + selfAct);

            //Move self icon to hidden
            db().updateAppCategory(selfAct, this.getPackageName(), Categories.CAT_HIDDEN);

            //Take a backup now that things are pre-sorted.
            //db().backup("After install");

            //Show the help screen on very first run.
//            iconHandler.postDelayed(new Runnable() {
//                @Override
//                public void run() {
//                    Intent help = new Intent(MainActivity.this, AboutActivity.class);
//                    help.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//                    startActivity(help);
//                }
//            }, 3000);

        }

        MsgBox.showNewsMessage(this, mPrefs);

    }



    //All db access is routed through here.
    // We don't store the connection, because the connection might end up closed.
    public DB db() {
        return GlobState.getGlobState(this).getDB();
    }

    private final View.OnDragListener iconSheetDropRedirector = new View.OnDragListener() {
        @Override
        public boolean onDrag(View view, DragEvent dragEvent) {
            return mMainDragListener.onDrag(mIconSheet, dragEvent);
        }
    };

    private final InteractiveScrollView.OnSwipeHorizontalListener mHSwipeListener = new InteractiveScrollView.OnSwipeHorizontalListener() {

        @Override
        public void onLeftSwipe(float absDist) {
            if (mChildLock) return;
            switchCategory(getNextCategory(mCategory, -1), AnimateDirection.Right, false);
            scrollToCategoryTab();
        }

        @Override
        public void onRightSwipe(float absDist) {
            if (mChildLock) return;
            switchCategory(getNextCategory(mCategory, 1), AnimateDirection.Left, false);
            scrollToCategoryTab();
        }
    };

    private void scrollToCategoryTab() {
        if (mCategory!=null) {
            View cattab = mCategoryTabs.get(mCategory);
            if (cattab!=null) {
                mCategoriesScroller.smoothScrollTo(0, cattab.getTop() - 20);
            }
        }
    }

    private String getNextCategory(String category, int dir) {
        if (dir>0) dir = 1;
        if (dir<0) dir = -1;

        List<String> categories = db().getCategories();
        for (ListIterator<String> it = categories.listIterator(); it.hasNext();) {
            String cat = it.next();
            if (!cat.equals(category) && (Categories.isHiddenCategory(cat) || db().isHiddenCategory(cat))) it.remove();
        }
        int last = categories.size() -1;
        for (int i=0; i<categories.size(); i++) {
            if (categories.get(i).equals(category)) {
                if (i==0 && dir==-1) return categories.get(last);
                if (i==last && dir==1) return categories.get(0);
                return categories.get(i+dir);
            }
        }
        return null;
    }

    //screen rotation, etc.
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mScreenDim = getScreenDimensions();
        checkConfig();
        //showButtonBar(false, true);
    }

    private String getTopCategory() {
        for (String category: db().getCategories()) {
            // If the category has been deleted, pick a known-good category
            if (category==null || db().getCategoryDisplay(category)==null || db().isHiddenCategory(category) || Categories.isHiddenCategory(category)) {
                continue;
            }
            return category;
        }
        // If the category has been deleted, pick a known-good category
        return Categories.CAT_TALK;
    }


    private int getCategoryPos(String category) {
        View cattab = mCategoryTabs.get(category);
        int pos = -1;
        if (cattab!=null) {
            for (int i = 0; i < mCategoriesLayout.getChildCount(); i++) {
                if (mCategoriesLayout.getChildAt(i) == cattab) {
                    pos = i;
                    break;
                }
            }
        }
        return pos;
    }

    //private List<String> prefsChanging = Collections.synchronizedList(new ArrayList<String>());

    private final Object prefsChanging = new Object();

    private volatile int prefsUpdate;

    //int pc = 0;
    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

        synchronized (prefsChanging) {
            Log.d(TAG, "A preference has been changed: " + key);

            if (key != null) {

                // when the Theme updates many of these prefs at once, it sends "prefsUpdate" first so that we ignore everything until it is done.
                if (key.equals(Theme.PREFS_UPDATE_KEY)) {
                    prefsUpdate = sharedPreferences.getBoolean(key, false) ? prefsUpdate+1 : prefsUpdate-1;
                    if (prefsUpdate<0) prefsUpdate=0;
                }

                if (prefsUpdate>0) return;
                Log.d(TAG, "still here " + key);

//                if (pc++ > 10) {
//                    Log.d("prefsChanging", "too many: bailing!");
//                    prefsUpdate--;
//                    pc--;
//                    return;
//                }

                checkConfig();

                boolean repop = false;

                //Delete our icon cache so the labels can be regenerated.
                if (key.equals(getString(R.string.pref_key_textcolor)) || key.equals(getString(R.string.pref_key_iconsize)) || key.equals("icon-update")) {
                    clearImageViews();
                    repop = true;
                }
                if (key.equals(getString(R.string.pref_key_icon_tint)) || key.equals(Theme.PREFS_UPDATE_KEY)) {
                    AppLauncher.clearIcons();
                    clearImageViews();
                    repop = true;
                }
                if (key.equals(getString(R.string.pref_key_icons_pack))) {
                    AppLauncher.clearIcons();
                    clearImageViews();

                    //mIconSheet.removeAllViews();
                    IconsHandler ich = GlobState.getIconsHandler(this);

                    ich.loadIconsPack(sharedPreferences.getString(getString(R.string.pref_key_icons_pack), IconsHandler.DEFAULT_PACK));
                    //ich.updateStyles(mStyle);

                } else {

                    final boolean repop2 = repop;
                    iconHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            //switchCategory(mCategory);
                            if (repop2) {
                                mQuickRow.repopulate();

                            }

                        }
                    }, 200);


                    if (key.equals(getString(R.string.pref_key_toddler_lock))) {
                        mChildLock = sharedPreferences.getBoolean(getString(R.string.pref_key_toddler_lock), false);
                        if (mChildLock) mChildLockSetup = false;
                        checkChildLock();
                        hideCatsIfAutoHide(false);
                    }

                    if (key.equals(getString(R.string.pref_key_dumbmode))) {
                        mDumbMode = sharedPreferences.getBoolean(getString(R.string.pref_key_dumbmode), false);
                        if (mDumbMode) {
                            startDumbMode();
                        }
                    }



                    if (key.equals(getString(R.string.pref_key_show_badges))) {
                        if (!sharedPreferences.getBoolean(getString(R.string.pref_key_show_badges), true)) {
                            GlobState.getBadger(this).clearAll();
                        }
                    }
                    if (key.equals(getString(R.string.pref_key_autohide_cats_timeout))) {
                        handleAutohide();
                    }
                    if (key.equals(getString(R.string.pref_key_animate_duration))) {
                        readAnimationDuration();
                    }

                    if (key.equals(getString(R.string.pref_key_center_sheet))) {
                        setAllIconSheetsLayout();
                    }

                    if (key.equals(getString(R.string.pref_key_show_action_menus))
                            || key.equals(getString(R.string.pref_key_show_dropzones))
                            || key.equals(getString(R.string.pref_key_show_action_extra))
                            || key.equals(getString(R.string.pref_key_show_action_activities))) {
                        mActionMenu.readActionMenuConfig();
                    }

                }
                if (key.toLowerCase().contains("cat") || key.equals(Theme.PREFS_UPDATE_KEY)) {
                    setCategoryTabStyles();
                }
            }
        }
    }





    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        //checkChildLock();
    }

    @Override
    public void onDestroy() {
        mAppPreferences.unregisterOnSharedPreferenceChangeListener(this);
        mWidgetHelper.removeWidgetChangedListener(this);

        super.onDestroy();
    }


    public static String getLatestCategory() {
        return latestCategory;
    }


    public synchronized void switchCategory(String category) {
        switchCategory(category, mStyle.isLeftHandCategories()?AnimateDirection.Left:AnimateDirection.Right);
    }

    private synchronized void switchCategory(String category, AnimateDirection dir) {
        switchCategory(category, dir, true);
    }


    private synchronized void switchCategory(String category, AnimateDirection dir, boolean bounce) {
        try {
            //String prevCat = mCategory;
            mActionMenu.dismissActionPopup();
            if (category == null) return;
            if (mCategory!=null && !mCategory.equals(category)) {
                mStyle.animateHide(mIconsArea, dir, true, bounce);
            }
            mCategory = category;
            latestCategory = mCategory;

            //make sure selected category is in the database.
            if (db().getCategoryDisplay(mCategory) == null) {
                mCategory = getTopCategory();
            }

//            if (prevCat==null || !mCategory.equals(prevCat)) {
                setCategoryTabStyles();
//            }


            mIconSheet = mIconSheets.get(mCategory);
            if (mIconSheet==null) return;

            //Check the screen rotation and changes column count, if needed
            checkConfig();

            //refresh icons on page
            iconHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    repopulateIconSheet(mCategory);

                    //the top frame holds the search zone, but only on the search page.
                    mIconSheetTopFrame.removeAllViews();
                    if (mCategory.equals(Categories.CAT_SEARCH)) {

                        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                        lp.gravity = Gravity.START;
                        mIconSheetTopFrame.setLayoutParams(lp);
                        mIconSheetTopFrame.addView(mSearchBox.getSearchView());

                        //Show recent apps
                        populateRecentApps();

                        //load our cursor
                        mSearchBox.refreshSearch(true);

                    } else {
                        // not the search page: close the cursor
                        mSearchBox.closeSeachAdapter();

                        if (isAutohide() && mAppPreferences.getBoolean(getString(R.string.pref_key_cat_label), true)) {
                            // not the search page: close the cursor
                            mSearchBox.closeSeachAdapter();

                            if (mCategoryLabel==null) {
                                mCategoryLabel = new TextView(MainActivity.this);
                            }
                            mCategoryLabel.setTextColor(mStyle.getCattabTextColor());
                            mCategoryLabel.setText(db().getCategoryDisplayFull(mCategory));
                            mCategoryLabel.setTextSize(mStyle.getCategoryTabFontSize()+1);
                            mCategoryLabel.setShadowLayer(8,4,4,mStyle.getCattabTextColorInvert());
                            mCategoryLabel.setBackgroundColor(mStyle.getCattabBackground());
                            if (mStyle.isRoundedTabs()) {
                                mCategoryLabel.setBackground(mStyle.getBgDrawableFor(mCategoryLabel, Style.CategoryTabStyle.Default,true));
                            }
                            mCategoryLabel.setPadding(60,5,60,5);
                            mCategoryLabel.setAlpha(.96f);

                            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                            lp.gravity=Gravity.CENTER;
                            lp.topMargin=10;
                            mIconSheetTopFrame.setLayoutParams(lp);

                            mIconSheetTopFrame.addView(mCategoryLabel);
                        }

                        if (mCategory.equals(Categories.CAT_DUMB) && !mDumbMode) {
                            Button dumbmode = new Button(MainActivity.this);
                            dumbmode.setText(R.string.activate_dumbmode);
                            dumbmode.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {
                                    view.setVisibility(View.GONE);
                                    mAppPreferences.edit().putBoolean(getString(R.string.pref_key_dumbmode), true).apply();
                                }
                            });
                            mIconSheetTopFrame.addView(dumbmode);
                        }
                    }

                    try {
                        //Actually switch the icon sheet.
                        mIconSheetHolder.removeAllViews();
                        mIconSheetHolder.addView(mIconSheet);
                    } catch(Exception|Error e) {
                        Log.e(this.getClass().getSimpleName(), "switchCategory", e);
                    }
                }
            }, mAnimationDuration);

//
//            iconHandler.postDelayed(new Runnable() {
//                @Override
//                public void run() {
//                    animateUpShow(mIconsArea);
//                }
//            },300);

            if (!isAutohide()) showButtonBar(false, true);
        } catch (Exception e) {
            Log.e(this.getClass().getSimpleName(), "SwitchCat", e);
        }
    }

    private void setCategoryTabStyles() {
        //Log.d(TAG, "setCategoryTabStyles");
        //switch all category tabs to their default style and text
        for (TextView catTab : mCategoryTabs.values()) {
            styleCategorySpecial(catTab, Style.CategoryTabStyle.Default);

            String category = mRevCategoryMap.get(catTab);
            if (!mCategory.equals(category)) {
                if (mAnimationDuration>0) {
                    int oldText = catTab.getText().length();
                    String newText = db().getCategoryDisplay(category);
                    if (newText.length() < oldText) {
                        mStyle.animateChangingSize(catTab, catTab.getHeight(), catTab.getHeight() * 2 / 3, null, null);
                    }
                }
                catTab.setText(db().getCategoryDisplay(category));
            }
        }

        //change the selected tab to the full label name
        final TextView catTab = mCategoryTabs.get(mCategory);
        if (catTab==null) return;

        final String text = db().getCategoryDisplayFull(mCategory);
        if (mAnimationDuration>0) {
            int initheight = catTab.getHeight();
            if (catTab.getVisibility()!=View.VISIBLE) initheight = 0;
            int finalheight = initheight;

            if (text.length()>catTab.getText().length()) {
                finalheight = (int)(catTab.getHeight()*1.5);
            }
            if (finalheight==0) {
                finalheight = 50;
            }
            mStyle.animateChangingSize(catTab, initheight, finalheight, null, new Runnable() {
                @Override
                public void run() {
                    catTab.setText(text);
                }
            });
        } else {
            catTab.setText(text);
        }

        catTab.setVisibility(View.VISIBLE);
    }


    private int mBackPressedSessionCount;
    @Override
    public void onBackPressed() {
        if (mInitCalling) return;
        try {
            hideCatsIfAutoHide(false);

            //back does nothign if in toddler mode
            if (mChildLock) {
                if (++mBackPressedSessionCount == 17) {
                    deactivateChildLock();
                }
                return;
            }


            String topCat = getTopCategory();
            if (mIconSheetBottomFrame.getVisibility() == View.VISIBLE
                    || mOpenPrefs2Button.getVisibility() == View.VISIBLE
                    || mHideButtons.getVisibility() == View.VISIBLE) {
                showButtonBar(false, true);
            } else if (mQuickRow.getScrollPos() > 0) {
                mQuickRow.scrollToStart();
            } else if (mIconSheetScroller.getScrollY() > 0) {
                //Otherwise, scroll to top
                mIconSheetScroller.smoothScrollTo(0, 0);
            } else if (mCategory.equals(Categories.CAT_SEARCH) && mSearchBox.getSeachText().length() != 0) {
                //If search is open, clear the searchbox
                mSearchBox.setSearchText("");
            } else if (!mCategory.equals(topCat)) {
                //Otherwise, switch to known-good category
                switchCategory(topCat);
                mCategoriesScroller.smoothScrollTo(0, 0);
            } else if (mCategoriesScroller.getScrollY() > 0) {
                mCategoriesScroller.smoothScrollTo(0, 0);
            }
        } catch (Exception e){
            Log.e(TAG, e.getMessage(), e);
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        // Log.d(TAG, keyCode + "");
        if (!mChildLock &&keyCode == KeyEvent.KEYCODE_MENU) {
            openSettings(this);
        }
        return super.onKeyDown(keyCode, event);
    }


    //Catch home key press
    @Override
    protected void onNewIntent(Intent intent) {
        if (mInitCalling) return;

        super.onNewIntent(intent);


        if (System.currentTimeMillis() - mPauseTime < 1000  && Intent.ACTION_MAIN.equals(intent.getAction())) {

            final boolean alreadyOnHome =
                    ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
                            != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
            Log.d("LaunchTime", " new intent " + alreadyOnHome);
            if (alreadyOnHome && !mChildLock) {

                //mPrefs.edit().putString("category",getTopCategory()).apply();
                switchCategory(getTopCategory());
                // If we are on home screen, reset most things and go to top category.
                iconHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            mActionMenu.dismissAppinfo();
                            mSearchBox.setSearchText("");
                            mCategoriesScroller.smoothScrollTo(0, 0);
                            showButtonBar(false, true);
                            mIconSheetScroller.smoothScrollTo(0, 0);
                            mQuickRow.scrollToStart();
                            mIconSheetScroller.smoothScrollTo(0, 0);
                            mCategoriesScroller.smoothScrollTo(0, 0);
                        } catch (Exception e) {
                            Log.e(TAG, e.getMessage(), e);
                        }

                    }
                }, 200);
            }

        }
    }

    private String getPreference(int key, String defValue) {
       String val = mAppPreferences.getString(getString(key), defValue);
       return val==null?defValue:val;
    }

    private void readPrefs() {

        //Checks application preferences and adjust accordingly
        try {

            mStyle.readPrefs();

            mDumbMode = mAppPreferences.getBoolean(getString(R.string.pref_key_dumbmode), false);
            mChildLock = mAppPreferences.getBoolean(getString(R.string.pref_key_toddler_lock), false);
            readAnimationDuration();
            mActionMenu.readActionMenuConfig();

            int orientationPref = Integer.parseInt(getPreference(R.string.pref_key_orientation, "0"));

            switch (orientationPref) {
                case 0:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                    break;
                case 1:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
                    break;
                case 2:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    break;
                case 3:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    break;

            }

        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }


    private void readAnimationDuration() {
        mAnimationDuration = Integer.parseInt(getPreference(R.string.pref_key_animate_duration, "150"));
        mActionMenu.setAnimationDuration(mAnimationDuration);
        //if (mAnimationDuration==0) mAnimationDuration=1; //small hack to make everything still work
    }

    //This is run on switchcategory and screen rotation, etc.
    // Checks global preferences and
    //  if we need to change the column count, etc
    private void checkConfig() {
        readPrefs();

        //View view = findViewById(R.id.main_layout_view);
        //view.setBackgroundColor(mStyle.getWallpaperColor());
        int bgcolor = mStyle.getWallpaperColor();
        if (Color.alpha(bgcolor)==0) {
            bgcolor = 0;
            getWindow().setBackgroundDrawable(null); // needed to clear it
        }
        getWindow().setBackgroundDrawable(new ColorDrawable(bgcolor));

        //Log.d(TAG,"bg:" + mStyle.getWallpaperColor());

        boolean autohideCats = isAutohide();
        try {

            String force = isLandscape() ? getPreference(R.string.pref_key_columns_landscape, "0") : getPreference(R.string.pref_key_columns_portrait, "0");
            //String force = mAppPreferences.getString(isLandscape()?getString(R.string.pref_key_columns_landscape):getString(R.string.pref_key_columns_portrait), "0");
            if (force.equals("0")) {
                mScreenDim = getScreenDimensions();
                //float launcherw = getResources().getDimension(R.dimen.launcher_width);
                float launcherw = mStyle.getLauncherSize();
                float catwidth = getResources().getDimension(R.dimen.cattabbar_width) + 3;

                float wr = (mScreenDim.x - (autohideCats ? 0 : catwidth)) / launcherw;

                //Log.d(TAG, "density=" + getResources().getDisplayMetrics().density + " wr=" + wr + " x=" + mScreenDim.x + " catwidth=" + catwidth + " launcherw=" + launcherw);
                if (wr < 3) mColumns = 2;
                else if (wr < 4.16) mColumns = 3;
                else if (wr < 5.4) mColumns = 4;
                else if (wr < 7.4) mColumns = 5;
                else if (wr < 9) mColumns = 6;
                else if (wr < 10.2) mColumns = 8;
                else if (wr < 13.2) mColumns = 9;
                else mColumns = 10;
            } else {
                mColumns = Integer.parseInt(force);
            }


            //Log.d(TAG, "x=" + mScreenDim.x + " catwidth=" + catwidth + " launcherw=" + launcherw);


            if (mIconSheet!=null && mIconSheet.getColumnCount() != mColumns) {
                changeColumnCount(mIconSheet, mColumns);
            }

            mShowButtons.setBackgroundColor(mStyle.getCattabBackground());
            mShowButtons.setColorFilter(mStyle.getCattabTextColor());
            mShowButtons.setMinimumHeight(mStyle.getCategoryTabPaddingHeight()*3);

            mHideButtons.setBackgroundColor(mStyle.getCattabBackground());
            mHideButtons.setColorFilter(mStyle.getCattabTextColor());
            mHideButtons.setMinimumHeight(mStyle.getCategoryTabPaddingHeight()*3);

            mShowCats.setBackgroundColor(mStyle.getCattabBackground());
            mShowCats.setColorFilter(mStyle.getCattabTextColor());
            mShowCats.setMinimumHeight(mStyle.getCategoryTabPaddingHeight()*3);
            mShowCats.setAlpha(.95f);

            //mShowButtons.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, categoryTabPaddingHeight*3));
            //mShowButtons.setPadding(2,categoryTabPaddingHeight,2,4);
            mSortCategoryButton.setBackgroundColor(mStyle.getCattabBackground());
            mAddCategoryButton.setBackgroundColor(mStyle.getCattabBackground());
            mRenameCategoryButton.setBackgroundColor(mStyle.getCattabBackground());
            mEditWidgetsButton.setBackgroundColor(mStyle.getCattabBackground());
            mOpenPrefsButton.setBackgroundColor(mStyle.getCattabBackground());
            mOpenPrefs2Button.setBackgroundColor(mStyle.getCattabBackground());

            mSortCategoryButton.setTextColor(mStyle.getCattabTextColor());
            mAddCategoryButton.setTextColor(mStyle.getCattabTextColor());
            mRenameCategoryButton.setTextColor(mStyle.getCattabTextColor());
            mEditWidgetsButton.setTextColor(mStyle.getCattabTextColor());
            mOpenPrefsButton.setColorFilter(mStyle.getCattabTextColor());
            mOpenPrefs2Button.setColorFilter(mStyle.getCattabTextColor());

            int c = mStyle.getWallpaperColor();
            mIconSheetBottomFrame.setBackgroundColor(Color.argb(255, Color.red(c), Color.green(c), Color.blue(c)));


            handleAutohide();
            setAllIconSheetsLayout();
            showButtonBar(false, true);

            setWindowOpts();

        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }


    private void setWindowOpts() {

        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                boolean full = mAppPreferences.getBoolean(getString(R.string.pref_key_use_fullscreen), false);
                if (full) {
                    //hard to get correct.
                    getWindow().getDecorView().setSystemUiVisibility(
                            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

                    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);

                    // retrieve the position of the DecorView
                    Rect visibleFrame = new Rect();
                    getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleFrame);

                    DisplayMetrics dm = getResources().getDisplayMetrics();
                    // check if the DecorView takes the whole screen vertically or horizontally
                    boolean isRight = dm.heightPixels == visibleFrame.bottom;
                    //boolean isBelow  = dm.widthPixels  == visibleFrame.right;


                    int status = 70;
                    int statusresourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
                    if (statusresourceId > 0) {
                        status = getResources().getDimensionPixelSize(statusresourceId) + 8;
                    }
                    int nav = 125;
                    int navresourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
                    if (navresourceId > 0) {
                        nav = getResources().getDimensionPixelSize(navresourceId);
                    }
                    findViewById(R.id.whole_thing).setPadding(0, status, isRight?nav:0, isRight?0:nav);
                }

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                    getWindow().setStatusBarColor(Color.TRANSPARENT);
                    if (full) {
                        getWindow().setNavigationBarColor(Color.TRANSPARENT);
                    }
                }
            }

        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }


    private int mOkToAutohide;

    private void cancelHide() {
        mOkToAutohide = 0;
    }

    private void hideCatsIfAutoHide(boolean delay) {
        if (delay) {
            int hidetime = getAutohideTimeout();

            mOkToAutohide = (int)(Math.random()*10000000)+2;
            final int okhidse = mOkToAutohide;
            iconHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (isAutohide() && mOkToAutohide==okhidse) showCats(false);
                }
            }, hidetime);
        } else {
            cancelHide();
            if (isAutohide()) showCats(false);
        }
        mActionMenu.dismissActionPopup();
    }


    private void showCats(boolean show) {
        cancelHide();

        showButtonBar(false,false);

        if (!show) {
            if (mCategoryTabsWrap.getVisibility() == View.VISIBLE) {
                mStyle.animateDownHide(mCategoryTabsWrap);
            }
            mStyle.animateUpShow(mShowCats);
        } else {
            mStyle.animateDownHide(mShowCats);
            if (mCategoryTabsWrap.getVisibility() == View.GONE) {
                mStyle.animateUpShow(mCategoryTabsWrap);
            }
        }
        mActionMenu.dismissActionPopup();
    }


    @SuppressLint("RtlHardcoded")
    private void handleAutohide() {
        //Switch the menu left/right

        boolean autohideCats = isAutohide();

        float catwidth = getResources().getDimension(R.dimen.cattabbar_width);

        FrameLayout.LayoutParams iconsarealp = (FrameLayout.LayoutParams)mIconsArea.getLayoutParams();
        if (iconsarealp==null){
            iconsarealp = new FrameLayout.LayoutParams(this,null);
        }


        FrameLayout.LayoutParams catslp = (FrameLayout.LayoutParams)mCategoryTabsWrap.getLayoutParams();
        if (catslp==null){
            catslp = new FrameLayout.LayoutParams(this,null);
        }

        ViewGroup pparent = ((ViewGroup)mOpenPrefs2Button.getParent());

        if (mStyle.isLeftHandCategories()) {
            catslp.gravity = LEFT;
            ((FrameLayout.LayoutParams)mShowCats.getLayoutParams()).gravity=Gravity.LEFT|Gravity.BOTTOM;
            if (autohideCats) {
                iconsarealp.leftMargin = 2;
                iconsarealp.rightMargin = 2;
            } else {
                iconsarealp.leftMargin = (int) catwidth+1;
                iconsarealp.rightMargin = 2;
            }

            pparent.removeView(mOpenPrefs2Button);
            pparent.addView(mOpenPrefs2Button,0);
        } else {
            catslp.gravity = Gravity.RIGHT;
            ((FrameLayout.LayoutParams)mShowCats.getLayoutParams()).gravity=Gravity.RIGHT|Gravity.BOTTOM;
            if (autohideCats) {
                iconsarealp.leftMargin = 2;
                iconsarealp.rightMargin = 2;
            } else {
                iconsarealp.leftMargin = 2;
                iconsarealp.rightMargin = (int) catwidth+1;
            }
            pparent.removeView(mOpenPrefs2Button);
            pparent.addView(mOpenPrefs2Button,1);
        }
//        int origback = mStyle.getCattabBackground();
//
//        if (autohideCats) {
////            GradientDrawable newback = new GradientDrawable(
////                    mStyle.isLeftHandCategories()?
////                            GradientDrawable.Orientation.RIGHT_LEFT:
////                            GradientDrawable.Orientation.LEFT_RIGHT,
////                    new int[] {Color.argb(1, Color.red(origback), Color.green(origback), Color.blue(origback)),
////                            Color.argb(160, Color.red(origback), Color.green(origback), Color.blue(origback))});
////            //  Color.argb(50, Color.red(origback), Color.green(origback), Color.blue(origback));
////            newback.setGradientCenter(.07f, .5f);
////            cats.setBackground(newback);
//            if (Color.alpha(origback) < 80) {
//                //cats.setBackgroundColor(Color.argb(50, 128, 128, 128));
//                //cats.setBackgroundColor(Color.argb(100, Color.red(origback)/2, Color.green(origback)/2, Color.blue(origback)/2));
//            }
//        } else {
//            cats.setBackground(null);
//            cats.setBackgroundColor(Color.TRANSPARENT);
//        }

        if (autohideCats) {
            hideCatsIfAutoHide(true);
        } else {
            showCats(true);
            mShowCats.setVisibility(View.GONE);
        }


    }

    private boolean isAutohide() {
        return !getPreference(R.string.pref_key_autohide_cats_timeout, "-1").equals("-1");
    }

    private int getAutohideTimeout() {
        return Integer.parseInt(getPreference(R.string.pref_key_autohide_cats_timeout, "1500"));
    }

    public LaunchApp getAppLauncher() {
        return mLaunchApp;
    }


    private void showProgressBar(final int max) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (max>0) {
                    mProgressBar.setMax(max);
                }
                mProgressBar.setProgress(0);
                mProgressBar.setVisibility(View.VISIBLE);
            }
        });
    }

    private void hideProgressBar() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mProgressBar.setVisibility(View.GONE);
            }
        });
    }

    private void setProgressBarMax(final int max) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mProgressBar.setMax(max);
            }
        });
    }

    private void setProgressBar(final int progress) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mProgressBar.setProgress(progress);
            }
        });
    }

    private void incProgressBar(final int progressDiff) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mProgressBar.incrementProgressBy(progressDiff);
            }
        });
    }


    private void setAllIconSheetsLayout() {
        mQuickRow.setCenterIcons(mAppPreferences.getBoolean(getString(R.string.pref_key_center_sheet), true));
        for (GridLayout sheet: mIconSheets.values()) {
            setIconSheetLayout(sheet);
        }
    }

    private void setIconSheetLayout(GridLayout iconSheet) {

        FrameLayout.LayoutParams lp;
        if (mAppPreferences.getBoolean(getString(R.string.pref_key_center_sheet), true)) {
            lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            lp.gravity = Gravity.CENTER_HORIZONTAL;
        } else {
            lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        iconSheet.setLayoutParams(lp);
    }


    private void createIconSheet(String category, int pos) {
        final GridLayout iconSheet = new GridLayout(MainActivity.this);
        if (mIconSheet == null) mIconSheet = iconSheet;
        mIconSheets.put(category, iconSheet);
        mRevCategoryMap.put(iconSheet, category);
        iconSheet.setColumnCount(mColumns);
        iconSheet.setOnDragListener(mMainDragListener);
        setIconSheetLayout(iconSheet);

        final TextView categoryTab = createCategoryTab(category, iconSheet, pos);


        mCategoryTabs.put(category, categoryTab);
        mRevCategoryMap.put(categoryTab, category);
    }

    public void populateRecentApps() {

        //GridLayout iconSheet = mIconSheets.get(Categories.CAT_SEARCH);

        removeIconSheetSend(Categories.CAT_SEARCH);
        //iconSheet.removeAllViews();

        int i=0;
        for (ComponentName actvname : db().getAppLaunchedList()) {
            if (db().isAppInstalled(actvname)) {
                AppLauncher app = db().getApp(actvname);
                //Log.d("Recent", "Trying " + actvname + " " + app);

                addAppToIconSheet(Categories.CAT_SEARCH, app, false);
                i++;
                if (i >= 45) break;
            }
        }
    }

    private void repopulateIconSheet(String category) {
        GridLayout iconSheet = mIconSheets.get(category);

        removeIconSheetSend(category);
        //iconSheet.removeAllViews();

        final List<ComponentName> apporder = db().getAppCategoryOrder(category);
        List<AppLauncher> apps = db().getApps(category);

        for (ComponentName actvname : apporder) {
            //Log.d("app", "repopulateIconSheet " + category + " " +  actvname.getClassName() + " " +  actvname.getPackageName());
            for (Iterator<AppLauncher> it = apps.iterator(); it.hasNext(); ) {
                AppLauncher app = it.next();
                if (actvname.equals(app.getComponentName())) {
                    addAppToIconSheet(category, app, true);
                    it.remove();
                }
            }
        }

        for (AppLauncher app : apps) {
            addAppToIconSheet(category, app, true);
            // Log.d(TAG, app.getActivityName());
        }

        if (apps.size()>0 && iconSheet!=null) {
            db().setAppCategoryOrder(category, iconSheet);
        }
        if (db().getAppCount(category) == 0) {
            showNoIconsSend(category);
        }
    }

    private void addAppToIconSheet(String category, AppLauncher app, boolean reuse) {
        addAppToIconSheetSend(category, app, -1, reuse);
    }



    private void addAppToIconSheetSend(String category, AppLauncher app, int pos, boolean reuse) {
        Message msg = new Message();
        msg.arg1 = AddIconHandler.ADD_ICON;
        Bundle data = new Bundle();
        data.putString("category", category);
        data.putParcelable("componentName", app.getComponentName());
        data.putInt("pos", pos);
        data.putBoolean("reuse", reuse);
        msg.setData(data);
        iconHandler.sendMessage(msg);
    }

    private void addAppToIconSheetRecv(Message msg) {
        Bundle data = msg.getData();
        String category = data.getString("category");
        ComponentName compName = data.getParcelable("componentName");
        AppLauncher app = AppLauncher.getAppLauncher(compName);
        if (app==null) {
            app = db().getApp(compName);
            if (app==null) return;
        }
        int pos = data.getInt("pos");
        boolean reuse = data.getBoolean("reuse");

        addAppToIconSheet(mIconSheets.get(category), app, pos, reuse);
    }


    private void removeIconSheetRecv(Message msg) {
        Bundle data = msg.getData();
        String category = data.getString("category");
        GridLayout iconSheet = mIconSheets.get(category);
        if (iconSheet!=null) {
            try {
                iconSheet.removeAllViews();
            } catch (Throwable t) {
                Log.e(TAG, t.getMessage(), t);
                Toast.makeText(this, t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
            }
        }
    }

    private void removeIconSheetSend(String category) {
        Message msg = new Message();
        msg.arg1 = AddIconHandler.REMOVE_ALL_ICONS;
        Bundle data = new Bundle();
        data.putString("category", category);
        msg.setData(data);
        iconHandler.sendMessage(msg);
    }

    private void showNoIconsRecv(Message msg) {
        Bundle data = msg.getData();
        String category = data.getString("category");
        GridLayout iconSheet = mIconSheets.get(category);
        if (iconSheet!=null) {
            TextView v = new TextView(this);
            v.setText(R.string.nothing_in_cat);
            v.setTextColor(mStyle.getTextColor());
            v.setTextSize(mStyle.getLauncherFontSize());
            v.setPadding(2,40,2,2);
            v.setMaxLines(3);
            iconSheet.addView(v);
        }
    }

    private void showNoIconsSend(String category) {
        Message msg = new Message();
        msg.arg1 = AddIconHandler.NO_ICONS;
        Bundle data = new Bundle();
        data.putString("category", category);
        msg.setData(data);
        iconHandler.sendMessage(msg);
    }


    public void removeAppFromIconSheet(AppLauncher app) {
        if (app.isWidget()) {
            mWidgetHelper.removeWidget(app.getComponentName());
        }
        db().deleteApp(app.getComponentName());
        repopulateIconSheet(mCategory);
    }

    private void addAppToIconSheet(GridLayout iconSheet, AppLauncher app, int pos, boolean reuse) {
        if (app != null) {
            try {
                if (((app.isWidget() || app.isLink()) && isAppInstalled(app.getPackageName())) || mLaunchApp.isValidActivity(app)) {
                    ViewGroup item = getLauncherView(app, false, reuse);
                    if (item != null) {
                        if (!app.iconLoaded()) {
                            app.loadAppIconAsync(this);
                        }
                        item.setTranslationX(0);
                        ViewGroup parent = (ViewGroup) item.getParent();
                        if (parent != null) parent.removeView(item);
                        GridLayout.LayoutParams lp = getAppLauncherLayoutParams(iconSheet, app);
                        iconSheet.addView(item, pos, lp);
                    }
                } else {
                    db().deleteApp(app.getComponentName());
                    Log.d(TAG, "removed " + app.getPackageName() + " " + app.getActivityName() + ": activity not valid.");
                }
            } catch (Exception e) {
                Log.e(TAG, "exception adding icon to sheet", e);
                Toast.makeText(this,"Couldn't place icon: " + e.getMessage(), Toast.LENGTH_LONG).show();
            }
        } else {
            Log.d(TAG, "Not showing recent: Null.");
        }

    }


    //Too much magic here.  Widgets are weird.

    @NonNull
    private GridLayout.LayoutParams getAppLauncherLayoutParams(GridLayout grid, AppLauncher app) {

        GridLayout.LayoutParams lp = new GridLayout.LayoutParams();

        final int wDp = getLauncherWidth(app);
        final int hDp = getLauncherHeight(app);
        float w = dipToPx(wDp);
        float h = dipToPx(hDp);

        int wcells = getWidgetWCells(app);
        int hcells = getWidgetHCells(app);

        float sw = mStyle.getLauncherSize();

        float cellwidth = sw * 1f;
        float cellheight = cellwidth *1.1f;  // ~square cells


        if (w>0 || wcells>0) {

            if (wcells == 0) wcells = (int) Math.round(Math.max(w / cellwidth * .75, 1));

            if (wcells > 1) {
                int start = GridLayout.UNDEFINED;
                if (wcells > grid.getColumnCount()) {
                    wcells = grid.getColumnCount();
                }
                if (wcells > mStyle.getMaxWCells()) {
                    wcells = mStyle.getMaxWCells();
                }

                lp.columnSpec = GridLayout.spec(start, wcells, GridLayout.FILL);

                //Log.d("widcol", "w=" + w + " wcells=" + wcells + " start=" + start + " cellwidth=" + cellwidth + " r=" + cellwidth * wcells);
            } else {
                lp.columnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1, GridLayout.FILL);
            }
        }
        if (h>0 || hcells>0) {

            if (hcells == 0) {
                hcells = (int) Math.round(Math.max(h / cellheight * .75, 1));
                if (hcells > mStyle.getMaxWCells()) {
                    hcells = mStyle.getMaxWCells();
                }
            }

            if (hcells > 1) {
                lp.rowSpec = GridLayout.spec(GridLayout.UNDEFINED, hcells, GridLayout.FILL);
            } else {
                lp.rowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1, GridLayout.FILL);
            }

        }

        final AppWidgetHostView appwid = mWidgetHelper.getAppWidgetHostView(app);

        if (appwid != null) {

            lp.width = (int)(cellwidth*wcells*1.16);

            int calcHeight = (int)(cellheight*hcells*(hcells/(hcells-.2)));
            lp.height = calcHeight;
//            if (h > cellheight*hcells*1.3) {
//                lp.height = calcHeight;
//            } else {
//                lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;//(int)(cellheight*hcells*1.2);
//            }

            storeWidgetWCells(app, wcells);
            storeWidgetHCells(app, hcells);
            //lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;//(int)(cellheight*hcells*1.2);

            final int hDpf;
            if (h<calcHeight) {
                hDpf = pxToDip(calcHeight);
            } else {
                hDpf = hDp;
            }
            //Log.d("widcol2", "wDp=" + wDp + " w=" + w + " wcells=" + wcells  + " cellwidth=" + cellwidth + " r=" + cellwidth * wcells);
            //Log.d("widcol2", "hDp=" + hDp + " hDpf=" + hDpf + " h=" + h + " hcells=" + hcells  + " cellheight=" + cellheight + " r=" + cellheight * hcells);
            iconHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    appwid.updateAppWidgetSize(null, wDp, hDpf, wDp, hDpf);
                    if (appwid.getParent()!=null) {
                        appwid.getParent().requestLayout();
                    }
                    appwid.requestLayout();
                    appwid.postInvalidate();
                }
            }, 1000);


        }

        return lp;
    }

    @SuppressLint("RtlHardcoded")
    public void showWidgetResize(final AppLauncher appitem) {

//        AppWidgetHostView appwid = mWidgetHelper.getLoadedAppWidgetHostView(appitem.getComponentName());
//        if (appwid==null) {
//            Log.e(TAG, "No appwid for " + appitem.getComponentName());
//            return;
//        }
        //final int resizeMode = appwid.getAppWidgetInfo().resizeMode;

        ViewGroup item = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.widget_size, null);


        final PopupWindow pw = new PopupWindow(item,
                (int)getResources().getDimension(R.dimen.widget_resize_width),
                (int)getResources().getDimension(R.dimen.widget_resize_height));

        pw.setOutsideTouchable(false);
        pw.setFocusable(true);

        Button ok = item.findViewById(R.id.wid_size_ok);
        ok.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                pw.dismiss();
            }
        });

        Button defaults = item.findViewById(R.id.wid_set_default_size);

        final SeekBar width = item.findViewById(R.id.width_seek);
        final SeekBar height = item.findViewById(R.id.height_seek);

        final Runnable resize = new Runnable() {
            @Override
            public void run() {
                //Log.d(TAG, (width.getProgress()+1) +","+ (height.getProgress()+1));
                storeWidgetWCells(appitem, width.getProgress()+1);
                storeWidgetHCells(appitem, height.getProgress()+1);

                GridLayout.LayoutParams lp = getAppLauncherLayoutParams(mIconSheet, appitem);
                View widframe = getLauncherView(appitem,false);
                if (widframe != null) {
                    widframe.setLayoutParams(lp);
                }
            }
        };

        defaults.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                storeWidgetWCells(appitem, 0);
                storeWidgetHCells(appitem, 0);
                GridLayout.LayoutParams lp = getAppLauncherLayoutParams(mIconSheet, appitem);
                View widframe = getLauncherView(appitem,false);
                if (widframe != null) {
                    widframe.setLayoutParams(lp);
                }
                pw.dismiss();
            }
        });


        int wcells = getWidgetWCells(appitem);
        width.setProgress(wcells-1);
        int hcells = getWidgetHCells(appitem);
        height.setProgress(hcells-1);

        TextView widthLab = item.findViewById(R.id.width_num);
        bindSeek(width, widthLab, wcells,  mStyle.getMaxWCells(), resize);


        TextView heightLab = item.findViewById(R.id.height_num);
        bindSeek(height, heightLab, hcells,8, resize);


        int xpos = 0;
        int ypos = mScreenDim.y*2/3;

        int gravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP;

        View widframe = getLauncherView(appitem,false);
        int [] viewpos = new int[2];
        if (widframe!=null) {
            widframe.getLocationOnScreen(viewpos);

            if (viewpos[1] > mScreenDim.y*.3) {
                ypos = mScreenDim.y/20;
            }
//                else  if (viewpos[1] > mScreenDim.y*.3 && viewpos[1] < mScreenDim.y*.4) {
//                    mIconSheetScroller.smoothScrollBy(0, (int)(mScreenDim.y*.25));
//                }

            if (mScreenDim.x>mScreenDim.y) {
                gravity = Gravity.LEFT|Gravity.TOP;
                if (viewpos[0] > mScreenDim.x * .3) {
                    xpos = mScreenDim.x / 24;
                } else {
                    xpos = mScreenDim.x * 2 / 3;
                }
            }

        }

        pw.showAtLocation(findViewById(R.id.icon_and_cat_wrap), gravity, xpos, ypos);


    }

    private void bindSeek(SeekBar seekBar, final TextView seekLabel, int start, int max, final Runnable onChange) {

        final int min = 1;
        if (start<min) start = min;
        if (start>max) max = start;
        //seekLabel.setText(start+"");
        seekBar.setMax(max-min);
        seekBar.setProgress(start-min);

        String value = start + "";
        seekLabel.setText(value);

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                String value = (i+min) + "";
                seekLabel.setText(value);
                if (onChange!=null) onChange.run();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }



    private int pxToDip(float pixel){
        float scale = getResources().getDisplayMetrics().density;
        return (int)((pixel - 0.5f)/scale);
    }

    private float dipToPx(float dip){
        if (dip==0) return 0;

        float scale = getResources().getDisplayMetrics().density;
        return (int)(dip * scale + .5f);
//        Resources r = getResources();
//        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, r.getDisplayMetrics());
        // return (int)((pixel - 0.5f)/scale);
    }

    private void changeColumnCount(GridLayout gridLayout, int columnCount) {
        if (gridLayout.getColumnCount() != columnCount) {

            List<View> childViews = new ArrayList<>();

            for (int i = gridLayout.getChildCount()-1; i >=0 ; i--) {
                View view = gridLayout.getChildAt(i);
                if (view == null) {
                    Log.d(TAG, "null child at " + i);
                    continue;
                }
                childViews.add(view);
                gridLayout.removeView(view);
            }


            gridLayout.setColumnCount(columnCount);
            Collections.reverse(childViews);

            for (View view: childViews) {
                GridLayout.LayoutParams lp;
                if (view.getTag() instanceof AppLauncher) {
                    AppLauncher app = (AppLauncher) view.getTag();

                    lp = getAppLauncherLayoutParams(gridLayout, app);
                } else {
                    lp = new GridLayout.LayoutParams();
                }
                view.setLayoutParams(lp);

                gridLayout.addView(view);
            }
        }
    }

//
//    private void listAll() {
//        Intent intent = new Intent(Intent.ACTION_MAIN, null);
//
//
//        // Get all activities that have those filters
//        List<ResolveInfo> activities = mPackageMan.queryIntentActivities(intent, 0);
//
//
//        for (int i = 0; i < activities.size(); i++) {
//
//            AppLauncher app;
//
//            ResolveInfo resolveInfo = activities.get(i);
//            ActivityInfo activityInfo = resolveInfo.activityInfo;
//            String name = activityInfo.name;
//            String packageName = activityInfo.applicationInfo.packageName;
//            String className = activityInfo.applicationInfo.className;
//            String label = resolveInfo.loadLabel(mPackageMan).toString();
//            boolean enabled = activityInfo.enabled;
//            boolean exported = activityInfo.exported;
//
//        }
//    }

    public void onWidgetRemoved(ComponentName provider, AppWidgetHostView view) {
        AppLauncher app = AppLauncher.getAppLauncher(provider);
        if (app!=null) {
            mAppLauncherViews.remove(app);
            db().deleteApp(app.getComponentName());
        }
        AppLauncher.removeAppLauncher(provider);
        repopulateIconSheet(mCategory);

    }


    private void clearImageViews() {
//        for (AppLauncher app: mAppLauncherViews.keySet()) {
//            app.clearImageViews();
//        }

        mAppLauncherViews.clear();

    }


    public ViewGroup getLauncherView(final AppLauncher app, boolean smallIcon) {
        return getLauncherView(app, smallIcon, true);
    }

    public ViewGroup getLauncherView(final AppLauncher app, boolean smallIcon, boolean reuse) {


        if (smallIcon) reuse = false;
        ViewGroup item = mAppLauncherViews.get(app);
        if (reuse) {
            if (item!=null) return item;
        }

        if (app.isWidget()) {
            item = new FrameLayout(this);

            AppWidgetHostView hostView = mWidgetHelper.getOrCreateWidget(this, app.getComponentName());
            if (hostView == null) {
                db().deleteApp(app.getComponentName());
                return null;
            }

            AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
            //Log.d(TAG, "Min: " + pinfo.minWidth + "," + pinfo.minHeight);
            //Log.d(TAG, "MinResize: " + pinfo.minResizeWidth + "," + pinfo.minResizeHeight);
            //Log.d(TAG, "Resizemode: " + pinfo.resizeMode);

            storeLauncherDimen(app, pinfo.minWidth, pinfo.minHeight);

            ViewGroup parent = (ViewGroup) hostView.getParent();
            if (parent != null) {
                parent.removeView(hostView);
            }
            item.addView(hostView);
            final View wrap = item;
            hostView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    if (mChildLock) {
                        return false;
                    }
                    return MainActivity.this.onLongClick(wrap);
                }
            });
            hostView.setOnDragListener(new View.OnDragListener() {
                @Override
                public boolean onDrag(View view, DragEvent dragEvent) {
                    if (mChildLock) {
                        return false;
                    }
                    return mMainDragListener.onDrag(wrap, dragEvent);
                }
            });



        } else {


            item = (ViewGroup) LayoutInflater.from(this).inflate(smallIcon ? R.layout.launcher_small_icon : R.layout.launcher_icon, null);



            item.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    view.startAnimation(itemClickedAnim);
                    mLaunchApp.launchApp(app);
                    showButtonBar(false, true);
                    hideCatsIfAutoHide(false);
                }
            });

            ImageView iconImage = item.findViewById(R.id.launcher_icon);

            if (!smallIcon) {

                setLayoutSize(item, mStyle.getLauncherSize(), ViewGroup.LayoutParams.WRAP_CONTENT);

                TextView iconLabel = item.findViewById(R.id.launcher_text);
                iconLabel.setTextColor(mStyle.getTextColor());
                iconLabel.setText(app.getLabel());

                setLayoutSize(iconImage, mStyle.getLauncherIconSize(), mStyle.getLauncherIconSize());

                iconLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mStyle.getLauncherFontSize());
                setLayoutSize(iconLabel, mStyle.getLauncherSize(), mStyle.getLauncherSize()/2.1);

            }
            app.addIconImage(iconImage);
            app.loadAppIconAsync(this);

            ComponentName compName = app.getBaseComponentName();
            int bcount = GlobState.getBadger(this).getUnreadCount(compName);
            updateAppBadgeCount(item, bcount);

            item.setOnLongClickListener(this);
            item.setOnDragListener(mMainDragListener);
        }
        item.setTag(app);
        item.setClickable(true);

        if (reuse) {
            mAppLauncherViews.put(app, item);
        }
        return item;
    }

    @SuppressLint("SetTextI18n")
    private void updateAppBadgeCount(ViewGroup item, int bcount) {
        if (item!=null) {
            TextView badge = item.findViewById(R.id.launcher_badge);
            if (badge!=null) {
                if (bcount <= 0 || !mAppPreferences.getBoolean(getString(R.string.pref_key_show_badges), true)) {
                    badge.setVisibility(View.GONE);
                } else {
                    badge.setVisibility(View.VISIBLE);
                    badge.setText(bcount + "");
                }
            }
        }
    }

    @Override
    public void badgerCountChanged(ComponentName compname, int count) {
        AppLauncher app = AppLauncher.getAppLauncher(compname);
        if (app!=null) {
            ViewGroup view = mAppLauncherViews.remove(app);
            if (view!=null) {
                updateAppBadgeCount(view,count);
            }

            if (mQuickRow.appAlreadyHere(app)) {
                mQuickRow.repopulate();
            }
        }

    }



    private void setLayoutSize(View view, double width, double height) {
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        if (lp!=null) {
            lp.height = (int) height;
            lp.width = (int) width;
        } else {
            lp = new ViewGroup.LayoutParams((int) width, (int) height);
        }
        view.setLayoutParams(lp);
    }

    public void setupWidget() {
        try {
            mWidgetHelper.popupSelectWidget(this);
        } catch (Throwable t) {
            //very rare
            Toast.makeText(this, "Can't show widgets: " + t.getLocalizedMessage(),Toast.LENGTH_LONG).show();
            Log.e("Widgets", t.getMessage(), t);
        }
    }

    private String mActionCategory;


    private void addWidget(AppWidgetHostView appwid) {
        if (mChildLock) return;

        ComponentName cn = appwid.getAppWidgetInfo().provider;
        String actvname = cn.getClassName();
        String pkgname = cn.getPackageName();

        String catId = db().getAppCategory(cn);
        if (mActionCategory==null) mActionCategory = mCategory;
        if (catId == null || catId.equals(mActionCategory)) {

            //Log.d(TAG, actvname + " " + pkgname);

            mWidgetHelper.saveLoadedWidget(cn,appwid);
//            mLoadedWidgets.put(actvname, appwid);
//            mPrefs.edit().putInt(actvname, appwid.getAppWidgetId()).apply();
            AppLauncher.removeAppLauncher(cn);
            AppLauncher app = AppLauncher.createAppLauncher(actvname, pkgname, pkgname, mActionCategory, true);

            db().addApp(app);
            db().addAppCategoryOrder(mActionCategory, app.getComponentName());
        } else {
            Toast.makeText(this, getString(R.string.widget_alreay,db().getCategoryDisplay(catId)), Toast.LENGTH_LONG).show();
        }


    }

    private void storeWidgetWCells(AppLauncher app, int wcells) {
        SharedPreferences.Editor ePrefs = mPrefs.edit();

        ePrefs.putInt(app.getComponentName() + "_wcells", wcells);

        ePrefs.apply();

    }

    private void storeWidgetHCells(AppLauncher app, int hcells) {
        SharedPreferences.Editor ePrefs = mPrefs.edit();

        ePrefs.putInt(app.getComponentName() + "_hcells", hcells);

        ePrefs.apply();

    }

    private int getWidgetWCells(AppLauncher app) {
        return mPrefs.getInt(app.getComponentName() + "_wcells", 0);
    }

    private int getWidgetHCells(AppLauncher app) {

        //Log.d(TAG, "in getWidgetHCells " + cells);
        return mPrefs.getInt(app.getComponentName() + "_hcells", 0);
    }



    private void storeLauncherDimen(AppLauncher app, int width, int height) {
        SharedPreferences.Editor ePrefs = mPrefs.edit();

        ePrefs.putInt(app.getComponentName() + "_width", width);

        ePrefs.putInt(app.getComponentName() + "_height", height);

        ePrefs.apply();

    }

    private int getLauncherWidth(AppLauncher app) {
        return mPrefs.getInt(app.getComponentName() + "_width", 0);
    }

    private int getLauncherHeight(AppLauncher app) {
        return mPrefs.getInt(app.getComponentName() + "_height", 0);
    }


    private Style.CategoryTabStyle getDefaultCategoryStyle(String category) {
        Style.CategoryTabStyle catstyle =  Style.CategoryTabStyle.Normal;

        if (category.equals(mCategory)) {
            catstyle =  Style.CategoryTabStyle.Selected;
        } else if (db().isHiddenCategory(category) || Categories.isHiddenCategory(category)) {
            catstyle =  Style.CategoryTabStyle.Hidden;
        } else if (db().isTinyCategory(category)) {
            catstyle =  Style.CategoryTabStyle.Tiny;
        }
        return catstyle;
    }

    private void styleCategorySpecial(TextView categoryTab,  Style.CategoryTabStyle catstyle) {
        styleCategorySpecial(categoryTab, catstyle, mRevCategoryMap.get(categoryTab));
    }


    private void styleCategorySpecial(TextView categoryTab,  Style.CategoryTabStyle catstyle, String category) {

        if (catstyle ==  Style.CategoryTabStyle.Default) {
            catstyle = getDefaultCategoryStyle(category);
        }
        mStyle.styleCategoryStyle(categoryTab, catstyle, isAutohide());

    }

    @SuppressLint("ClickableViewAccessibility")
    private TextView createCategoryTab(final String category, final GridLayout iconSheet, int pos) {
        final TextView categoryTab = new TextView(this);
        categoryTab.setText(db().getCategoryDisplay(category));
        categoryTab.setTag(category);
        // categoryTab.setWidth((int)Utils.dpToPx(this,categoryTabWidth));

        categoryTab.setTypeface(null, Typeface.BOLD);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

        final Style.CategoryTabStyle catstyle = getDefaultCategoryStyle(category);

        if (Categories.isHiddenCategory(category) || db().isHiddenCategory(category)) {
            categoryTab.setVisibility(View.GONE);
        }

        if (catstyle == Style.CategoryTabStyle.Normal) {
            lp.weight = 1;
        }
        lp.gravity = Gravity.CENTER;
        lp.setMargins(2, 4, 2, 3);

        categoryTab.setLayoutParams(lp);

        categoryTab.setGravity(Gravity.CENTER);

        styleCategorySpecial(categoryTab, Style.CategoryTabStyle.Default, category);


        categoryTab.setClickable(true);
        categoryTab.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                cancelHide();
                return false;
            }
        });
        categoryTab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mChildLock) return;
                // view.startAnimation(itemClickedAnim);
                switchCategory(category);
                hideCatsIfAutoHide(true);

            }
        });
        categoryTab.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(final View view) {
                if (mChildLock) return true;


                if (mActionMenu.useExtraActions() || !mActionMenu.useDropZones()) {
                    mActionCategory = category;
                    mActionMenu.showCatagoryActionMenu(categoryTab);
                    setMenuOnTouchListener(categoryTab, false, new Runnable() {
                        @Override
                        public void run() {
                            startDragCategory(categoryTab, category);
                            categoryTab.setOnTouchListener(new View.OnTouchListener() {
                                @Override
                                public boolean onTouch(View view, MotionEvent motionEvent) {
                                    cancelHide();
                                    return false;
                                }
                            });
                        }
                    });

                } else {
                    startDragCategory(categoryTab, category);

                }

                return true;
            }
        });

        categoryTab.setOnDragListener(new View.OnDragListener() {
            @Override
            public boolean onDrag(View overView, final DragEvent event) {
                if (mChildLock) return true;
                View dragObj = (View) event.getLocalState();
                if (dragObj==null) {
                    Log.e(TAG, "dragobj was null; " + event);
                    return true;
                }
                boolean isAppLauncher = dragObj.getTag() instanceof AppLauncher;
                boolean isNoDrop = Categories.isNoDropCategory(category);
                boolean isCurrent = category.equals(mCategory);
                switch (event.getAction()) {
                    case DragEvent.ACTION_DRAG_ENTERED:
                        if (!isCurrent && !isNoDrop) {
                            styleCategorySpecial(categoryTab, Style.CategoryTabStyle.DragHover);
                        }
                        //Log.d(TAG, "DRAG_ENTERED: " + category + ((AppLauncher)dragObj.getTag()).getActivityName());

                        break;

                    case DragEvent.ACTION_DRAG_LOCATION:

                        scrollOnDrag(overView, event, mCategoriesScroller);
                        break;
                    case DragEvent.ACTION_DRAG_ENDED:
                        mBeingDragged = null;
                        hideRemoveDropzone();
                        hideHiddenCategories();
                    case DragEvent.ACTION_DRAG_EXITED:
                        //Log.d(TAG, "DRAG_EXITED: " + category + ((AppLauncher)dragObj.getTag()).getActivityName());

                        styleCategorySpecial(categoryTab, Style.CategoryTabStyle.Default);

                        break;

                    case DragEvent.ACTION_DROP:
                        if (isAppLauncher) {
                            if (!isNoDrop && !isCurrent) {
                                mBeingDragged.setCategory(category);
                                db().updateAppCategory(mBeingDragged.getComponentName(), category);
                                mMainDragListener.onDrag(iconSheet, event);
                            }
                        } else {
                            ViewGroup container1 = (ViewGroup) overView.getParent();
                            ViewGroup container2 = (ViewGroup) dragObj.getParent();


                            int index = -1;
                            for (int i = 0; i < container1.getChildCount(); i++) {
                                if (container1.getChildAt(i) == overView) {
                                    index = i;
                                }
                            }
                            container2.removeView(dragObj);
                            if (index == -1) {
                                container1.addView(dragObj);
                            } else {
                                container1.addView(dragObj, index);
                            }
                            db().setCategoryOrder(container1);
                        }
                        break;
                }
                return true;
            }
        });

        if (pos == -1 && !category.equals(Categories.CAT_HIDDEN)) {
            pos = getCategoryPos(Categories.CAT_SEARCH);
        }

        if (pos>-1) {
            mCategoriesLayout.addView(categoryTab, pos);
        } else {

            mCategoriesLayout.addView(categoryTab);
        }

        return categoryTab;
    }

    private void startDragCategory(View view, String category) {
        ClipData data = ClipData.newPlainText(category, category);
        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
        //view.startDrag(data, shadowBuilder, view, 0);

        boolean dragstarted;
        if (Build.VERSION.SDK_INT>=24) {
            dragstarted = view.startDragAndDrop(data, shadowBuilder, view, 0);
        } else {
            dragstarted = view.startDrag(data, shadowBuilder, view, 0);
        }

        if (dragstarted) {
            mDragDropSource = mCategoriesLayout;
            if (!Categories.isSpeacialCategory(category)) {
                showRemoveDropzone();
            }
            showHiddenCategories();
        }
        cancelHide();
    }

    private long mDropZoneHover=0;
    private final View.OnDragListener mMainDragListener = new View.OnDragListener() {
        @Override
        public boolean onDrag(View droppedOn, DragEvent event) {
            if (mChildLock) return false;

            View dragObj = (View) event.getLocalState();
            if (dragObj==null) return true;

            boolean isLauncher = true;
            boolean isSpecial = false;
            boolean isApplink = false;
            if (dragObj.getTag() == null || !(dragObj.getTag() instanceof AppLauncher)) {
                isLauncher = false;
            } else  {
                AppLauncher app = (AppLauncher)dragObj.getTag();
                isSpecial = app.isLink() || app.isWidget();
                isApplink = app.isAppLink();
            }

            if (mCategory.equals(Categories.CAT_SEARCH) && isAncestor(mIconSheet, droppedOn)) return false;

            boolean nocolor = droppedOn instanceof GridLayout || droppedOn == mRemoveDropzone
                    || droppedOn == mLinkDropzone || !isLauncher || mQuickRow.isSelf(mDragDropSource)
                    || isAncestor(mSearchBox.getSearchView(), droppedOn);

            //prevent dropping categories anywhere but category area and trash
            if (mDragDropSource==mCategoriesLayout && !(droppedOn==mCategoriesLayout || droppedOn==mRemoveDropzone )) {
                return false;
            }

            if ((isSpecial && !isApplink) && (mQuickRow.isSelf(droppedOn) || isAncestor(mQuickRow.getGridLayout(), droppedOn))) return false;

            switch (event.getAction()) {
                case DragEvent.ACTION_DRAG_STARTED:
                    // do nothing
                    //Log.d(TAG, "" + dragObj.getTag());

                    if (mCategory.equals(Categories.CAT_SEARCH)) {
                        try {
                            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                            if (imm != null)
                                imm.hideSoftInputFromWindow(findViewById(R.id.search_box).getWindowToken(), 0);
                        } catch (Throwable t) {
                            //Log.e(TAG, t.getMessage(), t);
                        }
                    }

                    break;

                case DragEvent.ACTION_DRAG_LOCATION:
                    //scroll the scrollview

                    if (isLauncher) {
                        scrollOnDrag(droppedOn, event, mIconSheetScroller);
                        hscrollOnDrag(droppedOn, event, mQuickRow.getScroller());

                        if (mActionMenu.useDropZones()) {
                            if (!isSpecial && !mCategory.equals(Categories.CAT_SEARCH) && mLinkDropzone.getVisibility() != View.VISIBLE && (droppedOn == mRemoveDropzone || droppedOn == mLinkDropzonePeek) && System.currentTimeMillis() - mDropZoneHover > 400) {
                                //mLinkDropzone.setVisibility(View.VISIBLE);
                                mStyle.animateShow(mLinkDropzone, AnimateDirection.Right);
                                //mLinkDropzonePeek.setVisibility(View.GONE);
                                mStyle.animateDownHide(mLinkDropzonePeek);
                                // Log.d(TAG, "mLinkDropzone.setVisibility(View.VISIBLE)");
                            }
                        }
                    }
                    break;
                case DragEvent.ACTION_DRAG_ENTERED:
                    if (!nocolor ) {
                        droppedOn.setBackgroundColor(mStyle.getDragoverBackground());
                    }
                    if (droppedOn==mRemoveDropzone || droppedOn==mLinkDropzonePeek) {
                        mDropZoneHover = System.currentTimeMillis();
                        //Log.d("LaunchTime", "DRAG_ENTERED: " + ((AppLauncher)dragObj.getTag()).getActivityName());
                    }
                    //Log.d("LaunchTime", "DRAG_ENTERED: " + ((AppLauncher)dragObj.getTag()).getActivityName());
                    break;
                case DragEvent.ACTION_DRAG_EXITED:

                    if (!nocolor) droppedOn.setBackgroundColor(mStyle.getBackgroundDefault());
                    break;
                case DragEvent.ACTION_DROP:

                    //Log.d("dropon", droppedOn.toString());

                    if (!nocolor) droppedOn.setBackgroundColor(mStyle.getBackgroundDefault());
                    // Dropped, reassign View to ViewGroup

                    if (dragObj == droppedOn) {
                        // Log.d(TAG, "self drop");
                        hideCatsIfAutoHide(false);
                        break;
                    }

                    try {
                        if (handleDrop(droppedOn, dragObj, isLauncher)) return true;
                    } catch (Exception e) {
                        Log.e(TAG, e.getMessage(), e);
                    }
                    break;
                case DragEvent.ACTION_DRAG_ENDED:
                    if (!nocolor) droppedOn.setBackgroundColor(mStyle.getBackgroundDefault());
                    mBeingDragged = null;
                    hideRemoveDropzone();
                    hideHiddenCategories();
                    break;
                default:
                    break;
            }
            return true;
        }

        private boolean handleDrop(View droppedOn, View dragObj, boolean isLauncher) {
            if (mChildLock) return false;

            ViewGroup target;
            Object droppedOnTag = droppedOn.getTag();
            if (droppedOn == mRemoveDropzone) {  // need to delete the dropped thing
                //Stuff to be deleted
                if (mQuickRow.isSelf(mDragDropSource)) {
                    removeDroppedItem(dragObj);
                    hideCatsIfAutoHide(false);
                } else if (mDragDropSource == mIconSheets.get(Categories.CAT_SEARCH)) {
                    removeDroppedRecentItem(dragObj);
                    hideCatsIfAutoHide(false);
                } else if (mBeingDragged != null && (mBeingDragged.isWidget() || mBeingDragged.isLink())) {
                    removeDroppedItem(dragObj);
                    mSearchBox.refreshSearch(true);
                    hideCatsIfAutoHide(false);

                } else if (mDragDropSource == mCategoriesLayout && !isLauncher) {
                    //delete category tab
                    promptDeleteCategory((String) dragObj.getTag());

                } else {
                    //uninstall app
                    if (mBeingDragged!=null) {
                        launchUninstallIntent(mBeingDragged, mDragDropSource, dragObj);
                    }
                    hideCatsIfAutoHide(true);
                }
                setAllIconSheetsLayout();
                return true;
            } else if (droppedOn == mLinkDropzone) {
                hideCatsIfAutoHide(false);
                if (isLauncher) {
                    AppLauncher app = (AppLauncher)dragObj.getTag();
                    makeAppLink(app);
                } else {
                    Log.d(TAG, "non-launcher dropped on linker: " + dragObj + " tag=" + dragObj.getTag());
                }
                return true;
            } else if (droppedOn instanceof GridLayout) {
                target = (GridLayout) droppedOn;

                hideCatsIfAutoHide(droppedOn!=mIconSheet);
            } else if (droppedOn instanceof FrameLayout) {
                target = (FrameLayout) droppedOn;
                hideCatsIfAutoHide(false);
            } else if (droppedOn.getParent() instanceof GridLayout){
                target = (GridLayout) droppedOn.getParent();
                hideCatsIfAutoHide(false);
            } else {
                hideCatsIfAutoHide(true);
                return true;
            }

            if (droppedOnTag instanceof AppLauncher) {
                if (((AppLauncher)droppedOnTag).isWidget()) {
                    target = (GridLayout) droppedOn.getParent();
                }
            }

            //Find the drop position
            int index = -1;
            for (int i = 0; i < target.getChildCount(); i++) {
                if (target.getChildAt(i) == droppedOn) {
                    index = i;
                }
            }


            //remove icon from source?
            boolean remove = false;
            if (mQuickRow.isSelf(mDragDropSource)  && mQuickRow.isSelf(target)) remove = true;

            if (!mCategory.equals(Categories.CAT_SEARCH)) {
                if (!mQuickRow.isSelf(mDragDropSource) && !mQuickRow.isSelf(target)) remove = true;
            }

            if (remove) {
                mDragDropSource.removeView(dragObj);
            } else {
                if (mQuickRow.isSelf(target)) {
                    if (!mQuickRow.isSelf(mDragDropSource)) {
                        //prevent copies of the same app on the quickrow
                        if (mQuickRow.appAlreadyHere((AppLauncher) dragObj.getTag())) {
                            return true;
                        }
                    }
                    //make a copy of the launcher to put on the quickbar
                    dragObj = getLauncherView(AppLauncher.createAppLauncher((AppLauncher) dragObj.getTag(), true), true);

                } else {
                    dragObj = getLauncherView(AppLauncher.createAppLauncher((AppLauncher) dragObj.getTag()), false, false);
                }
            }


            if (!(target != mQuickRow.getGridLayout() && mQuickRow.getGridLayout() == mDragDropSource)) {
                try {
                    ViewParent parent = dragObj.getParent();
                    if (parent!=null) {
                        Log.e(TAG, "dragObj " + dragObj + " still has parent " + parent, new Throwable() );
                        ((ViewGroup)parent).removeView(dragObj);
                    }

                    ViewGroup.LayoutParams lp = null;
                    if (target instanceof GridLayout && dragObj.getTag() instanceof AppLauncher) {
                        lp = getAppLauncherLayoutParams((GridLayout)target, (AppLauncher)dragObj.getTag());
                    }

                    if (index == -1) {
                        target.addView(dragObj, lp);
                    } else {
                        target.addView(dragObj, index, lp);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "exception adding icon to sheet", e);
                    Toast.makeText(MainActivity.this,"Couldn't place icon: " + e.getMessage(), Toast.LENGTH_LONG).show();
                }
            }

            //save the new order
            db().setAppCategoryOrder(mRevCategoryMap.get(target), target);
            if (!target.equals(mDragDropSource)) {
                db().setAppCategoryOrder(mRevCategoryMap.get(mDragDropSource), mDragDropSource);
            }

            if (mCategory.equals(Categories.CAT_SEARCH)) {
                mSearchBox.refreshSearch(true);
            }
            setAllIconSheetsLayout();
            return false;
        }

        private void removeDroppedRecentItem(View dragObj) {
            if (mChildLock) return;

            try {
                db().deleteAppLaunchedRecord(mBeingDragged.getComponentName());
                mDragDropSource.removeView(dragObj);
            } catch (Exception e) {
                Log.e(TAG, e.getMessage(), e);
            }
        }

        private void removeDroppedItem(View dragObj) {
            if (mChildLock) return;

            try {
                mDragDropSource.removeView(dragObj);
                db().setAppCategoryOrder(mRevCategoryMap.get(mDragDropSource), mDragDropSource);

                if (mBeingDragged.isLink()) {
                    db().deleteApp(mBeingDragged.getComponentName());
                }

                if (mBeingDragged.isWidget()) {
                    mWidgetHelper.removeWidget(mBeingDragged.getComponentName());
                    db().deleteApp(mBeingDragged.getComponentName());
                }
            } catch (Exception e) {
                Log.e(TAG, e.getMessage(), e);
            }
        }

    };

    public void makeAppLink(AppLauncher app) {
        Log.d(TAG, "Making link: " + app.getActivityName() + " " + app.getPackageName());
        AppLauncher applauncher = app.makeAppLink();
        String category = mCategory;
        if (Categories.isNoDropCategory(category)) {
            category = Categories.CAT_OTHER;
        }
        applauncher.setCategory(category);
        db().addApp(applauncher);
        repopulateIconSheet(category);
    }





    public boolean isOnQuickRow(View view) {
        return isAncestor(mQuickRow.getGridLayout(), view);
    }

    public boolean isOnSearchView(View view) {
        return isAncestor(mSearchBox.getSearchView(), view);
    }

    private static boolean isAncestor(ViewGroup potentialParent, View potentialChild) {

        if (potentialParent==potentialChild) return true; //self;

        ViewParent parent = potentialChild.getParent();

        do {

            if (parent == potentialParent) {
                return true;
            }
        } while (parent!=null && (parent = parent.getParent()) != null);
        return false;

    }

    private void scrollOnDrag(View view, DragEvent event, ScrollView scrollView) {
        float ty = view.getTop() + event.getY();

        if (isAncestor(scrollView, view)) {

            int thresh = scrollView.getHeight() / 6;

            if (ty < scrollView.getScrollY() + thresh) {
                scrollView.smoothScrollBy(0, -10);
            } else if (ty > scrollView.getScrollY() + scrollView.getHeight() - thresh) {
                scrollView.smoothScrollBy(0, 10);
            }
        }
    }

    private void hscrollOnDrag(View view, DragEvent event, HorizontalScrollView scrollView) {
        float tx = view.getLeft() + event.getX();

        if (isAncestor(scrollView, view)) {

            int thresh = scrollView.getWidth() / 6;

            if (tx < scrollView.getScrollX() + thresh) {
                scrollView.smoothScrollBy(-10, 0);
            } else if (tx > scrollView.getScrollX() + scrollView.getWidth() - thresh) {
                scrollView.smoothScrollBy(10,0);
            }
        }
    }


    private View mDragPotential;
    @Override
    public boolean onLongClick(final View view) {
        if (mChildLock) return false;

        final AppLauncher dragitem = (AppLauncher) view.getTag();
        mDragPotential = view;
        final boolean iswidget = dragitem.isWidget();

        if ((mActionMenu.useActionMenus() || iswidget) && mActionMenu.displayActionShortcuts(view, dragitem)) {

            setMenuOnTouchListener(view, iswidget, new Runnable() {
                @Override
                public void run() {
                    startDrag();
                }
            });

        } else {
            startDrag();
        }
        return true;
    }

    private void setMenuOnTouchListener(final View view, final boolean iswidget, final Runnable startDrag) {
        View.OnTouchListener tl = new View.OnTouchListener() {
            float oX = -1;
            float oY = -1;

            @SuppressLint("ClickableViewAccessibility")
            @Override
            public boolean onTouch(View v, MotionEvent event) {

                if (event.getActionMasked() == MotionEvent.ACTION_CANCEL && event.getSource()!= InputDevice.SOURCE_ANY) {
                    if (view != null) {
                        view.setOnTouchListener(null);
                        if (iswidget) {
                            setTouchListener(view, null);
                        }
                    }
                    mActionMenu.dismissActionPopup();
                    startDrag.run();
                } else if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {

                    if (oX == -1) {
                        oX = event.getX();
                        oY = event.getY();
                    } else {
                        final int slop  = ViewConfiguration.get(MainActivity.this).getScaledTouchSlop();
                        if (Math.abs(oX - event.getX()) > slop || Math.abs(oY - event.getY()) > slop) {
                            clearDragPotential(false);
                            mActionMenu.dismissActionPopup();
                            startDrag.run();
                        }
                    }
                    //Log.d("movet", event.getX() + "," + event.getY());
                } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                    clearDragPotential(true);
                    setTouchListener(view, null);
                    //dismissActionPopup();
                } else {
                    Log.d(TAG, event.getActionMasked() + " event");
                }


                return false;
            }
        };

        view.setOnTouchListener(tl);

        if (iswidget) {
            setTouchListener(view, tl);
        }
    }

    private void setTouchListener(View view, View.OnTouchListener tl) {
        if (view==null) return;
        view.setOnTouchListener(tl);
        if (view instanceof ViewGroup) {
            ViewGroup vg = (ViewGroup)view;
            for (int i = 0; i < vg.getChildCount(); i++) {
                View vc = vg.getChildAt(i);
                setTouchListener(vc, tl);
            }
        }
    }

    private void startDrag() {

        if (mDragPotential==null) return;

        AppLauncher dragitem = (AppLauncher) mDragPotential.getTag();
        String label = dragitem.getLabel();
        ClipData data = ClipData.newPlainText(label, label);
        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(mDragPotential);

        boolean dragstarted;
        if (Build.VERSION.SDK_INT>=24) {
            dragstarted = mDragPotential.startDragAndDrop(data, shadowBuilder, mDragPotential, 0);
        } else {
            dragstarted = mDragPotential.startDrag(data, shadowBuilder, mDragPotential, 0);
        }

        if (dragstarted) {
            mBeingDragged = dragitem;
            mDragDropSource = (ViewGroup) mDragPotential.getParent();
            Log.d(TAG, "Drag started: " + dragitem.getActivityName() +  ", source = " + mDragDropSource);
            showCats(true);
            showHiddenCategories();
            // Log.d(TAG, "source = " + mDragDropSource);
            //if (mDragDropSource.getId()!=R.id.icontarget) {
            showRemoveDropzone();
            //}
        }
        mDragPotential = null;

    }


    public void clearDragPotential(boolean nullit) {
        if (mDragPotential!=null) {
            mDragPotential.setOnTouchListener(null);
            if (mDragPotential instanceof ViewGroup) {
                setTouchListener(mDragPotential, null);
            }
        }
        if (nullit) {
            mDragPotential = null;
        }
    }


    private void showHiddenCategories() {
        //Log.d(TAG,"showHiddenCategories");
        for (String cat: db().getCategories()) {
            if (mCategoryTabs!=null) {
                final View cattab = mCategoryTabs.get(cat);
                if (cat != null && cattab != null) {
                    if (mAnimationDuration > 0 && cattab.getVisibility() == View.GONE) {

                        //Log.d(TAG,"showHiddenCategories " + cat);
                        int finalheight = cattab.getHeight();
                        if (finalheight <= 0) {
                            finalheight = 30;
                        }
                        mStyle.animateChangingSize(cattab, 1, finalheight, new Runnable() {
                            @Override
                            public void run() {
                                cattab.setVisibility(View.VISIBLE);
                            }
                        }, null);

                    } else {
                        cattab.setVisibility(View.VISIBLE);
                    }
                }
            }
        }
    }

    private void hideHiddenCategories() {

        if (isAutohide()) {
            return;
        }

        for (String cat : db().getCategories()) {
            boolean isNewCat = cat.equals(mCategoryJustCreated);

            final TextView cattab = mCategoryTabs.get(cat);

            if (cattab!=null) {
                if (mCategory.equals(cat)) {
                    cattab.setVisibility(View.VISIBLE);
                } else if ((Categories.isHiddenCategory(cat) || db().isHiddenCategory(cat))
                        || (!cat.equals(Categories.CAT_SEARCH) && !mCategory.equals(cat) && db().getAppCount(cat) == 0 && !isNewCat)) {

                    if (mAnimationDuration>0 && cattab.getVisibility() != View.GONE) {
                        int h = cattab.getHeight();
                        mStyle.animateChangingSize(cattab, h, 1, null, new Runnable() {
                            @Override
                            public void run() {
                                cattab.setVisibility(View.GONE);
                            }
                        });
                    } else {
                        cattab.setVisibility(View.GONE);
                    }
                } else {
                    cattab.setVisibility(View.VISIBLE);
                }
            }
        }

    }


    private void showRemoveDropzone() {
        if (mChildLock) return;

        showButtonBar(false, false);

        if (mActionMenu.useDropZones()) {
            if (mDragDropSource == mQuickRow.getGridLayout()
                    || mDragDropSource == mCategoriesLayout
                    || mDragDropSource == mIconSheets.get(Categories.CAT_SEARCH)
                    || (mBeingDragged != null && (mBeingDragged.isWidget() || mBeingDragged.isLink())
            )) {
                mRemoveDropzone.setBackgroundColor(Color.YELLOW);
                //mRemoveAppText.setText(getString(R.string.remove_launcher) + "\n\u267B");
                mRemoveAppText.setText(R.string.remove_launcher);
                mRemoveAppText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, R.drawable.recycle);
                mRemoveAppText.setTextColor(Color.BLACK);
                //mLinkDropzonePeek.setVisibility(View.GONE);
                mStyle.animateDownHide(mLinkDropzonePeek);
            } else {
                mRemoveDropzone.setBackgroundColor(Color.RED);
                // mRemoveAppText.setText(getString(R.string.uninstall_app) + "\n" + new String(Character.toChars(0x1F5D1)));
                mRemoveAppText.setText(R.string.uninstall_app);
                mRemoveAppText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, R.drawable.trash);
                //mRemoveAppText.setCompoundDrawablesWithIntrinsicBounds(R.drawable.showy, 0,0,R.drawable.trash);
                mRemoveAppText.setTextColor(Color.WHITE);
                if (!Categories.CAT_SEARCH.equals(mCategory)) {
                    //mLinkDropzonePeek.setVisibility(View.VISIBLE);
                    mStyle.animateUpShow(mLinkDropzonePeek);
                }
            }
            mStyle.animateUpShow(mRemoveDropzone);
        }
        //mRemoveDropzone.setVisibility(View.VISIBLE);
        mStyle.animateDownHide(mShowButtons);
        mStyle.animateUpShow(mHideButtons);
        //mShowButtons.setVisibility(View.GONE);
    }

    private void hideRemoveDropzone() {

        mStyle.animateDownHide(mRemoveDropzone);
        mStyle.animateHide(mLinkDropzone, AnimateDirection.Right);
        mStyle.animateDownHide(mLinkDropzonePeek);
//        mRemoveDropzone.setVisibility(View.GONE);
//        mLinkDropzone.setVisibility(View.GONE);
//        mLinkDropzonePeek.setVisibility(View.GONE);
        //mShowButtons.setVisibility(View.VISIBLE);
        mStyle.animateUpShow(mShowButtons);
        mStyle.animateDownHide(mHideButtons);
    }

    public void launchUninstallIntent(AppLauncher app, View launcher) {
        launchUninstallIntent(app, null, launcher);
    }

    private void launchUninstallIntent(AppLauncher app, ViewGroup parent, View launcher) {
        if (mChildLock) return;

        mDragDropSource = parent;
        mBeingUninstalled = launcher;
        Log.d(TAG, "Uninstalling " + app.getPackageName());
        Uri packageUri = Uri.parse("package:" + app.getPackageName());
        Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
        uninstallIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
        startActivityForResult(uninstallIntent, UNINSTALL_RESULT);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        try {
            if (requestCode == UNINSTALL_RESULT) {
                final ComponentName actvname = ((AppLauncher) mBeingUninstalled.getTag()).getComponentName();
                switch (resultCode) {
                    case RESULT_OK:
                        db().deleteApp(actvname);
                        mQuickRow.removeFromQuickApps(actvname);
                        AppLauncher.removeAppLauncher(actvname);
                        mSearchBox.refreshSearch(true);
                        if (mDragDropSource!=null) {
                            mDragDropSource.removeView(mBeingUninstalled);
                            db().setAppCategoryOrder(mRevCategoryMap.get(mDragDropSource), mDragDropSource);
                        }
                        Toast.makeText(this, R.string.app_was_uninstalled, Toast.LENGTH_SHORT).show();
                        break;
                    case RESULT_CANCELED:
                        Toast.makeText(this, R.string.uninstall_canceled, Toast.LENGTH_LONG).show();
                        break;
                    default:
                        AlertDialog.Builder build = new AlertDialog.Builder(this);
                        build.setMessage(R.string.could_not_uninstall);
                        build.setPositiveButton(R.string.go_to_settings, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                try {
                                    openInSettings(actvname.getPackageName());
                                } catch (Throwable t) {
                                    Log.e(TAG, t.getMessage(), t);
                                }
                            }
                        });
                        build.setNeutralButton(R.string.cancel, null);
                        build.show();
                        //Toast.makeText(this, R.string.could_not_uninstall, Toast.LENGTH_LONG).show();

                }
            } else {
                AppWidgetHostView appwid = mWidgetHelper.onActivityResult(this, requestCode, resultCode, data);
                if (appwid == null) {
                    Log.d(TAG, "appwid is null.");
//                    ComponentName cn = mWidgetHelper.getComponentNameFromIntent(data);
//                    if (cn != null) {
//                        Log.d(TAG, "classname is " + cn.getClassName());
//                        //db().deleteApp(cn);
//                    } else {
                        super.onActivityResult(requestCode, resultCode, data);
//                    }
                } else {
                    addWidget(appwid);
                }
            }
            //  setCategoryTabStyles();
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }

    public void openInSettings(String packagename) {
        try {
            Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            intent.setData(Uri.parse("package:" + packagename));
            startActivity(intent);
        } catch (ActivityNotFoundException e ) {
            Toast.makeText(this, "Package not found", Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
            startActivity(intent);
        }
    }

    private boolean populateDeletedCategorySpinner(Spinner catDeletedSpinner, final EditText shortname, final EditText fullname) {
        final List<String> deldCats = new ArrayList<>();
        deldCats.add("");
        for (String cat: Categories.DefCategoryOrder) {
            String displayName = db().getCategoryDisplay(cat);
            if (displayName==null) {
                deldCats.add(cat);
            }
        }
        catDeletedSpinner.setAdapter(new ArrayAdapter<>(this,android.R.layout.simple_spinner_item,deldCats));
        catDeletedSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                if (i>0) {
                    shortname.setText(Categories.getCatLabel(MainActivity.this,deldCats.get(i)));
                    fullname.setText(Categories.getCatFullLabel(MainActivity.this,deldCats.get(i)));
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });
        return deldCats.size()>1;
    }

    private void promptGetCategoryName(String title, String message, final String category, String defName,
                                       String defFullName, boolean defIsTiny, boolean defIsHidden, final CategoryChangerListener categoryChangerListener) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(title);

        ViewGroup view = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.category_name, null);

        TextView messageView = view.findViewById(R.id.message_txt);

        final TextView catDeletedLabel = view.findViewById(R.id.cat_deleted_label);
        final Spinner catDeletedSpinner = view.findViewById(R.id.cat_deleted_spinner);

        final EditText shortname = view.findViewById(R.id.shortname);
        final EditText fullname = view.findViewById(R.id.fullname);
        final CheckBox isTiny = view.findViewById(R.id.istiny_checkbox);
        final CheckBox isHidden = view.findViewById(R.id.ishidden_checkbox);

        int vis = View.GONE;
        if (category.length()==0 && populateDeletedCategorySpinner(catDeletedSpinner, shortname, fullname)) {
            vis = View.VISIBLE;
        }
        catDeletedLabel.setVisibility(vis);
        catDeletedSpinner.setVisibility(vis);


        shortname.setSelectAllOnFocus(true);
        fullname.setSelectAllOnFocus(true);

        messageView.setText(message);
        shortname.setText(defName);
        fullname.setText(defFullName);
        isTiny.setChecked(defIsTiny);
        if (Categories.isHiddenCategory(category)) {
            isHidden.setVisibility(View.INVISIBLE);
        } else {
            isHidden.setVisibility(View.VISIBLE);
            isHidden.setChecked(defIsHidden);
        }

        builder.setView(view);

        builder.setPositiveButton(R.string.done, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String delcat = (String)catDeletedSpinner.getSelectedItem();
                categoryChangerListener.onClick(dialog, which, (delcat!=null && delcat.length()>0?delcat:category), shortname.getText().toString(), fullname.getText().toString(), isTiny.isChecked(), isHidden.isChecked());
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                if (imm!=null) imm.hideSoftInputFromWindow(shortname.getWindowToken(), 0);
            }
        });
        builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                if (imm!=null) imm.hideSoftInputFromWindow(shortname.getWindowToken(), 0);
            }
        });


        builder.show();
    }


    ///////////
    // Category manipulation

    public void promptRenameCategory(final String category) {

        promptGetCategoryName(getString(R.string.rename_cat),
                getString(Categories.isSpeacialCategory(category)?R.string.rename_cat3:R.string.rename_cat2),
                category,
                db().getCategoryDisplay(category),
                db().getCategoryDisplayFull(category),
                db().isTinyCategory(category),
                db().isHiddenCategory(category),
                new CategoryChangerListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which, String category, String newDisplayName, String newDisplayFullName, boolean isTiny, boolean isHidden) {
                        try {
                            renameCategory(category, newDisplayName, newDisplayFullName, isTiny, isHidden);
                        } catch (IllegalArgumentException e) {

                            Toast.makeText(MainActivity.this, R.string.need_name, Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }

    private void renameCategory(String category, String newDisplayName, String newDisplayFullName, boolean isTiny, boolean isHidden) {
        newDisplayName = newDisplayName.trim();
        newDisplayFullName = newDisplayFullName.trim();

        if (newDisplayFullName.length() == 0) {
            newDisplayFullName = newDisplayName;
        }

        if (newDisplayName.length() < 1) {
            throw new IllegalArgumentException("Must give a name");
        }

        if (db().updateCategory(category, newDisplayName, newDisplayFullName, isTiny, isHidden)) {

            TextView categoryTab = mCategoryTabs.get(category);
            if (categoryTab!=null) {
                if (category.equals(mCategory)) {
                    categoryTab.setText(newDisplayFullName);
                } else {
                    categoryTab.setText(newDisplayName);
                }
            }
        } else {
            Toast.makeText(MainActivity.this, R.string.no_rename, Toast.LENGTH_SHORT).show();
        }

    }

    public void promptAddCategory() {

        promptGetCategoryName(getString(R.string.add_cat),
                getString(R.string.add_cat2),
                "",
                "",
                "",
                false,
                false,
                new CategoryChangerListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which, String category, String newDisplayName, String newDisplayFullName, boolean isTiny, boolean isHidden) {
                        try {
                            addCategory(category, newDisplayName, newDisplayFullName, isTiny, isHidden);
                        } catch (IllegalArgumentException e) {

                            Toast.makeText(MainActivity.this, R.string.need_name, Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }

    private void addCategory(String category, String newDisplayName, String newDisplayFullName,  boolean isTiny, boolean isHidden) {
        category = category.trim();
        newDisplayName = newDisplayName.trim();
        newDisplayFullName = newDisplayFullName.trim();

        if (category.length() == 0) {
            category = newDisplayName;
        }
        if (newDisplayName.length() == 0) {
            category = newDisplayName;
        }

        if (newDisplayFullName.length() == 0) {
            newDisplayFullName = newDisplayName;
        }

        if (newDisplayName.length() < 1) {
            throw new IllegalArgumentException("Must give a name");
        }
        mCategoryJustCreated = category;

        Log.d(TAG, category +", " + newDisplayName +", " +  newDisplayFullName +", " +  isTiny);
        if (db().addCategory(category, newDisplayName, newDisplayFullName, isTiny, isHidden)) {
            if (mActionCategory==null) mActionCategory = mCategory;

            createIconSheet(category, getCategoryPos(mActionCategory));

            switchCategory(category);

            db().setCategoryOrder(mCategoriesLayout);
        } else {
            Toast.makeText(MainActivity.this, R.string.no_add_cat, Toast.LENGTH_SHORT).show();
        }
    }

    public void promptDeleteCategory(final String category) {

        if (Categories.isSpeacialCategory(category)) return;

        final String message = getString(R.string.cat_deleted, db().getCategoryDisplay(Categories.CAT_OTHER));
        new AlertDialog.Builder(MainActivity.this)
                .setIcon(android.R.drawable.ic_dialog_alert)
                .setTitle(R.string.delete_cat)
                .setMessage(R.string.delete_cat_prompt)
                .setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (deleteCategory(category)) {
                            Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
                        }
                    }

                })
                .setNegativeButton(R.string.cancel, null)
                .show();

    }



    private boolean deleteCategory(final String category) {
        if (Categories.isSpeacialCategory(category)) return false;

        TextView categoryTab = mCategoryTabs.get(category);

        boolean appsInCat = db().getAppCount(category) > 0;

        if (db().deleteCategory(category)) {

            View iconSheet = mIconSheets.get(category);
            mRevCategoryMap.remove(iconSheet);

            mCategoryTabs.remove(category);
            mRevCategoryMap.remove(categoryTab);

            mCategoriesLayout.removeView(categoryTab);

            //repopulateIconSheet(Categories.CAT_OTHER);
            //String newcat = mCategoryTabs.keySet().iterator().next();

            if (category.equals(mCategory) && appsInCat) {
                switchCategory(Categories.CAT_OTHER);
                categoryTab = mCategoryTabs.get(Categories.CAT_OTHER);
                if (categoryTab!=null) categoryTab.setVisibility(View.VISIBLE);
                return true;
            } else if (category.equals(mCategory)) {
                switchCategory(getTopCategory());
            }

        } else {
            Toast.makeText(MainActivity.this, R.string.no_delete_cat, Toast.LENGTH_SHORT).show();
        }
        return false;
    }

    public void hideCategory(String category) {
        db().hideCategory(category, !db().isHiddenCategory(category));
        setCategoryTabStyles();
        //hideHiddenCategories();
        showButtonBar(false, true);
    }


    private static final int APPSORT_NONE = -1;
    private static final int APPSORT_LABEL = 0;
    private static final int APPSORT_USAGE = 1;
    private static final int APPSORT_INSTALL_REV = 2;
    private static final int APPSORT_INSTALL = 3;
    private static final int APPSORT_PACKAGE = 4;

    public void promptSortCategory(final String category) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setTitle(R.string.sort_prompt_title);

        builder.setItems(R.array.sort_strings, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();

                sortCategory(category, i);
                repopulateIconSheet(category);

            }
        });

        builder.setNegativeButton(R.string.cancel, null);

        builder.show();
    }

    private void sortCategory(String category, final int sortby) {

        final List<ComponentName> recents = db().getAppLaunchedList();
        if (sortby != APPSORT_NONE) {
            List<AppLauncher> apps = db().getApps(category);
            Collections.sort(apps, new Comparator<AppLauncher>() {
                @Override
                public int compare(AppLauncher appfirst, AppLauncher appsecond) {
                    switch (sortby) {
                        case APPSORT_LABEL:
                            return appfirst.getLabel().compareToIgnoreCase(appsecond.getLabel());
                        case APPSORT_INSTALL_REV:
                            return getInstallTime(appsecond.getPackageName()).compareTo(getInstallTime(appfirst.getPackageName()));
                        case APPSORT_INSTALL:
                            return getInstallTime(appfirst.getPackageName()).compareTo(getInstallTime(appsecond.getPackageName()));
                        case APPSORT_USAGE:
                            int p1 = recents.indexOf(appfirst.getComponentName());
                            int p2 = recents.indexOf(appsecond.getComponentName());
                            if (p1==-1) p1 = Integer.MAX_VALUE;
                            if (p2==-1) p2 = Integer.MAX_VALUE;
                            int comp = ((Integer)p1).compareTo(p2);
                            if (comp==0) {
                                return appfirst.getLabel().compareToIgnoreCase(appsecond.getLabel());
                            }
                            return comp;
                        case APPSORT_PACKAGE:
                            return appfirst.getPackageName().compareToIgnoreCase(appsecond.getPackageName());

                    }
                    return 0;
                }
            });

            db().setAppCategoryOrder(category, apps);
        }

    }

    public void promptRecategorize(final String category) {

        MsgBox.alert(this,
                getString(R.string.cat_recategorize),
                getString(R.string.recat_prompt),
                true,
                true,
                new Runnable() {
            @Override
            public void run() {
                recategorize(category);
            }
        });
    }


    public void recategorize(final String category) {

        GlobState.execute(this, new Runnable() {
            @Override
            public void run() {
                int moved = 0;
                final int dir = mStyle.isLeftHandCategories()?-1:1;
                for (AppLauncher app: db().getApps(category)) {
                    if (!app.isNormalApp()) continue;
                    String newCat = Categories.getCategoryForComponent(MainActivity.this, app.getBaseComponentName(),true,null);
                    if (newCat!=null && !newCat.equals(category)) {
                        //Log.d(TAG, app.getLabel() +" " + newCat);
                        moved++;
                        app.setCategory(newCat);
                        db().updateAppCategory(app.getComponentName(), newCat);
                        if (mAnimationDuration>0) {
                            final View appview = mAppLauncherViews.get(app);
                            if (appview != null) {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        appview.animate().translationXBy(dir * mScreenDim.x).setDuration(mAnimationDuration).start();
                                    }
                                });
                            }
                        }
                       // addAppToIconSheet(newCat, app, true);
                    }
                }

                final String text =  getString(R.string.moved_apps, moved);

                iconHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        db().setAppCategoryOrder(category, mIconSheets.get(category));
                        repopulateIconSheet(category);
                        hideHiddenCategories();
                        Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT ).show();
                    }
                },500);
            }
        });
    }

    private Long getInstallTime(String packagename) {
        try {
            return getPackageManager()
                    .getPackageInfo(packagename, 0)
                    .firstInstallTime;
        } catch (PackageManager.NameNotFoundException e) {
            return -1L;
        }
    }

    private void initUI() {

        mIconsArea = findViewById(R.id.iconarea_wrap);

        mCategoriesLayout = findViewById(R.id.layout_categories);

        mIconSheetTopFrame = findViewById(R.id.layout_icons_topframe);
        mIconSheetScroller = findViewById(R.id.layout_icons_scroller);

        mIconSheetHolder = findViewById(R.id.icon_sheet_holder);

        mIconSheetBottomFrame = findViewById(R.id.layout_icons_bottomframe);

        mRemoveDropzone = findViewById(R.id.remove_dropzone);
        mRemoveDropzone.setOnDragListener(mMainDragListener);
        mRemoveAppText = findViewById(R.id.remove_dz_txt);

        mLinkDropzone = findViewById(R.id.link_dropzone);

        mLinkDropzonePeek = findViewById(R.id.link_dropzone_peek);

        //((TextView) findViewById(R.id.link_dz_text)).setCompoundDrawablesWithIntrinsicBounds(0,0,0,R.drawable.linkicon);

        mCategoryTabsWrap = findViewById(R.id.category_tabs_wrap);

        mCategoriesScroller = findViewById(R.id.layout_categories_scroller);
        mProgressBar = findViewById(R.id.progressBar);
        mShowButtons = findViewById(R.id.show_buttonbar);
        mHideButtons = findViewById(R.id.hide_buttonbar);
        mShowCats = findViewById(R.id.show_cats_buttom);

        mIconSheets = new TreeMap<>();
        mCategoryTabs = new TreeMap<>();
        mRevCategoryMap = new HashMap<>();
        mRevCategoryMap.put(mQuickRow.getGridLayout(), QuickRow.QUICK_ROW_CAT);

        mSortCategoryButton = findViewById(R.id.btn_sort_cat);
        mAddCategoryButton = findViewById(R.id.btn_add_cat);
        mRenameCategoryButton = findViewById(R.id.btn_rename_cat);

        mEditWidgetsButton = findViewById(R.id.btn_widgets);
        mOpenPrefsButton = findViewById(R.id.btn_prefs);
        mOpenPrefs2Button = findViewById(R.id.btn_prefs2);

        itemClickedAnim = new ScaleAnimation(.85f,1,.85f,1,Animation.RELATIVE_TO_SELF,.5f,Animation.RELATIVE_TO_SELF,.5f);
        itemClickedAnim.setDuration(200);
        itemClickedAnim.setInterpolator(new AccelerateDecelerateInterpolator());



        mLinkDropzone.setOnDragListener(mMainDragListener);

        mShowButtons.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                cancelHide();
                showButtonBar(true, !isAutohide());
                hideCatsIfAutoHide(false);
            }
        });



        mHideButtons.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                cancelHide();
                showButtonBar(false, !isAutohide());
                hideCatsIfAutoHide(false);
            }
        });


        mShowCats.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                cancelHide();
                if (!mChildLock) {
                    showCats(true);
                    showButtonBar(true, true);
                }
            }
        });


        mSortCategoryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                promptSortCategory(mCategory);
                showButtonBar(false, true);
            }
        });

        mRenameCategoryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                promptRenameCategory(mCategory);
                showButtonBar(false, true);
            }
        });

        mAddCategoryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                promptAddCategory();
                showButtonBar(false, true);
            }
        });


        mEditWidgetsButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                setupWidget();
                showButtonBar(false, true);
            }
        });

        View.OnClickListener prefsclick = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                openSettings(MainActivity.this);
                showButtonBar(false, true);
            }
        };

        mOpenPrefsButton.setOnClickListener(prefsclick);
        mOpenPrefs2Button.setOnClickListener(prefsclick);


        mIconSheetScroller.setOnTouchListener(mDismissClick);
        mIconSheetScroller.setOnPositionChangedListener(new InteractiveScrollView.OnPositionChangedListener() {
            @Override
            public void onPositionChanged(float percentUp, float percentDown, int distFromTop, int distFromBottom) {
                mActionMenu.dismissActionPopup();
                hideCatsIfAutoHide(false);
            }
        });

        mIconSheetScroller.setHSwipeListener(mHSwipeListener);
        mCategoriesScroller.setHSwipeListener(mHSwipeListener);

        mIconSheetHolder.setOnDragListener(iconSheetDropRedirector);

        mIconsArea.setOnDragListener(iconSheetDropRedirector);

    }

    private final View.OnTouchListener mDismissClick = new View.OnTouchListener() {
        @SuppressLint("ClickableViewAccessibility")
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
                mActionMenu.dismissActionPopup();
                mActionMenu.dismissAppinfo();
                hideCatsIfAutoHide(false);
            }
            return false;
        }
    };


    public static void openSettings(Activity activity) {
        Intent settingsIntent = new Intent(activity, SettingsActivity.class);
        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        activity.startActivity(settingsIntent);
    }



    public void showButtonBar(boolean makevisible, boolean hideCats) {
        if (mChildLock) return;

//        StackTraceElement from = Thread.currentThread().getStackTrace()[3];
//
//        Log.d(TAG,"showButtonBar(" + makevisible + ", " + hideCats + ") from "  + from.getMethodName() + " line " + from.getLineNumber());

        if (makevisible) {
            mActionCategory = mCategory;
            showHiddenCategories();
            if (!mActionMenu.useExtraActions()) {
                if (mCategory!=null && mCategory.equals(Categories.CAT_SEARCH)) {
                    mSortCategoryButton.setVisibility(View.INVISIBLE);
                    mEditWidgetsButton.setVisibility(View.INVISIBLE);
                } else {
                    mSortCategoryButton.setVisibility(View.VISIBLE);
                    mEditWidgetsButton.setVisibility(View.VISIBLE);
                }

                mStyle.animateUpShow(mIconSheetBottomFrame);
            } else {
                mStyle.animateUpShow(mOpenPrefs2Button);

            }
            mStyle.animateDownHide(mShowButtons);

            mStyle.animateUpShow(mHideButtons);


        } else {
            if (hideCats) {hideHiddenCategories();}

            //if (!mUseExtraActions) {
            mStyle.animateDownHide(mIconSheetBottomFrame);
            //} else {
            mStyle.animateDownHide(mOpenPrefs2Button);
            //}

            mStyle.animateDownHide(mHideButtons);

            mStyle.animateUpShow(mShowButtons);

        }
    }

    private String kidaccumecode = "";
    private String kidcode = "";

    private final View.OnClickListener kidescape = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            view.startAnimation(itemClickedAnim);
            kidaccumecode += view.getTag();
            if (kidaccumecode.endsWith(kidcode)) {
                deactivateChildLock();
            } else if (kidaccumecode.length()>kidcode.length()) {
                kidaccumecode = kidaccumecode.substring(kidaccumecode.length()-1-kidcode.length());
            }
            mBackPressedSessionCount=0;
        }
    };


    private void deactivateChildLock() {
        boolean oldDumbMode = mDumbMode;
        mChildLock = false;
        mDumbMode = false;
        mAppPreferences.edit().putBoolean(getString(R.string.pref_key_toddler_lock), false).apply();
        mAppPreferences.edit().putBoolean(getString(R.string.pref_key_dumbmode), false).apply();
        kidaccumecode = "";
        checkChildLock();
        if (oldDumbMode) switchCategory(getTopCategory());
    }


    private void checkChildLock() {
        View kid_escape_area = findViewById(R.id.kid_escape_area);
        View decorView = getWindow().getDecorView();

        if (mDumbMode) {
            switchCategory(Categories.CAT_DUMB);
        }

        if (mChildLock ) {

            // Hide the status bar.
            int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
            decorView.setSystemUiVisibility(uiOptions);


            if (!mChildLockSetup) {
                mQuickRow.getScroller().setVisibility(View.GONE);

                mIconSheetBottomFrame.setVisibility(View.GONE);
                mShowButtons.setVisibility(View.GONE);
                mHideButtons.setVisibility(View.GONE);
                mShowCats.setVisibility(View.GONE);
                kid_escape_area.setVisibility(View.VISIBLE);

                TextView kid_code_txt = findViewById(R.id.kid_code_txt);

                TextView[] b = new TextView[4];
                b[0] = findViewById(R.id.btn_kid1);
                b[1] = findViewById(R.id.btn_kid2);
                b[2] = findViewById(R.id.btn_kid3);
                b[3] = findViewById(R.id.btn_kid4);

                List<String> letters = Arrays.asList(getString(R.string.letters).split("(?!^)"));
                Collections.shuffle(letters);
                kidcode = "";
                for (int i = 0; i < b.length; i++) {

                    b[i].setOnClickListener(kidescape);
                    String c = letters.get(i);
                    b[i].setTag(c);
                    b[i].setText(c);
                    kidcode += c;
                }
                if (mDumbMode) {
                    //kidcode += kidcode + kidcode;
                    kidcode += kidcode;
                }

                List<String> kidcodearr = Arrays.asList(kidcode.split("(?!^)"));
                String shuffled;
                int i = 0;
                do {
                    Collections.shuffle(kidcodearr);
                    shuffled = "";
                    for (String letter : kidcodearr) {
                        shuffled += letter;
                    }
                    if (i++>100) break;
                } while (kidcode.equals(shuffled) || (new StringBuilder(kidcode)).reverse().toString().equals(shuffled));
                kidcode = shuffled;

                kid_code_txt.setText(getString(R.string.kid_escape_text, kidcode) );
                mChildLockSetup = true;


            }
        } else {

            mChildLockSetup = false;
            mQuickRow.getScroller().setVisibility(View.VISIBLE);

            mShowButtons.setVisibility(View.VISIBLE);
            kid_escape_area.setVisibility(View.GONE);
            int uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
            decorView.setSystemUiVisibility(uiOptions);
        }
    }


    private void startDumbMode() {

        mDumbMode = true;

        final String category = Categories.CAT_DUMB;
        if (db().getCategoryDisplay(category)==null) {
            db().addCategory(category, Categories.getCatLabel(this, category), Categories.getCatFullLabel(this, category), Categories.isTinyCategory(Categories.CAT_DUMB),true, 100);
            createIconSheet(category, -2);
            Runnable setup = new Runnable() {
                @Override
                public void run() {
                    final List<AppLauncher> appLauncherss = processActivities(false);
                    List<ComponentName> apps = new ArrayList<>();
                    List<String> onlyTypes = new ArrayList<>();
                    onlyTypes.add("camera");
                    onlyTypes.add("phone");
                    onlyTypes.add("msg");
                    onlyTypes.add("music");
                    DefaultApps.checkDefaultApps(MainActivity.this, appLauncherss, apps, onlyTypes);

                    String [] needs = new String[] {"calc", "clock", "calendar", "maps"};


                    NEEDS:
                    for(String key: needs) {
                        for (AppLauncher app: appLauncherss) {
                            String packagename = app.getPackageName().toLowerCase();
                            if (packagename.contains(key)) {
                                apps.add(app.getComponentName());
                                continue NEEDS;
                            }
                        }
                    }


                    for(ComponentName ap: apps) {
                        Log.d(TAG, "Trying " + ap);
                        if (ap!=null) {
                            AppLauncher app = db().getApp(ap);
                            if (app==null) {
                                Log.d(TAG, "null app for " + ap);
                            } else {
                                AppLauncher applauncher = app.makeAppLink();
                                applauncher.setCategory(category);
                                db().addApp(applauncher);
                                //addAppToIconSheet(category, applauncher, true);
                                // applauncher.loadAppIconAsync(this,mPackageMan);

                            }
                        } else {
                            Log.d(TAG, "null ap");
                        }
                    }
                    mAppPreferences.edit().putBoolean(getString(R.string.pref_key_toddler_lock), true).apply();

                    //iconHandler.post()checkChildLock();

                }
            };
            GlobState.execute(this, setup);

        } else {

            mAppPreferences.edit().putBoolean(getString(R.string.pref_key_toddler_lock), true).apply();

            //checkChildLock();
        }


    }


    private boolean isAppInstalled(String packageName) {
        if (packageName.equals(AppLauncher.ACTION_PACKAGE)) return true;
        try {
            getPackageManager().getApplicationInfo(packageName, 0);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }


    private boolean isLandscape() {
        int orientation = getResources().getConfiguration().orientation;
        return orientation == Configuration.ORIENTATION_LANDSCAPE;
    }

    public Point getScreenDimensions() {

        Point size = new Point();
        getWindowManager().getDefaultDisplay().getSize(size);
        return size;
    }


    private static class AddIconHandler extends Handler {

        static final int ADD_ICON = 1;
        static final int REMOVE_ALL_ICONS = 2;
        static final int NO_ICONS = 3;
        //static final int REMOVE_ICON = 4;


        private final WeakReference<MainActivity> instref;
        AddIconHandler(MainActivity inst) {
            super();
            instref = new WeakReference<>(inst);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity inst = instref.get();
            if (inst!=null) {
                switch (msg.arg1) {
                    case ADD_ICON:
                        inst.addAppToIconSheetRecv(msg);
                        break;
                    case REMOVE_ALL_ICONS:
                        inst.removeIconSheetRecv(msg);
                        break;
                    case NO_ICONS:
                        inst.showNoIconsRecv(msg);
                }
            }
        }
    }


    public void removeViewFromQuickBar(View view) {
        mQuickRow.getGridLayout().removeView(view);
        //mQuickRow.removeFromQuickApps(appitem.getComponentName());
        db().setAppCategoryOrder(QuickRow.QUICK_ROW_CAT, mQuickRow.getGridLayout());
    }


    public void launchApp(AppLauncher appitem) {
        mLaunchApp.launchApp(appitem);
    }


    public String getCurrentCategory() {
        if (mCategory==null) return getTopCategory();
        return mCategory;
    }


    interface CategoryChangerListener {
        void onClick(DialogInterface dialog, int which, String category, String newDisplayName, String newDisplayFullName, boolean istiny, boolean ishidden);
    }


}