package com.quaap.launchtime.db;


import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;

import android.database.sqlite.SQLiteStatement;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.ViewGroup;

import com.quaap.launchtime.R;
import com.quaap.launchtime.apps.AppLauncher;
import com.quaap.launchtime.components.Categories;
import com.quaap.launchtime.components.FsTools;
import com.quaap.launchtime.components.SpecialIconStore;
import com.quaap.launchtime.components.Theme;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;

import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 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.
 */
@SuppressWarnings({"TryFinallyCanBeTryWithResources", "WeakerAccess"})
public class DB extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "db";
    private static final int DATABASE_VERSION = 11;

    private static final String ACTVNAME = "actvname";
    private static final String PKGNAME = "pkgname";
    private static final String LABEL = "label";
    private static final String LABELFULL = "labelfull";
    private static final String CATID = "catID";
    private static final String ISWIDGET = "iswidget";
    private static final String ISUNINSTALLED = "isuninstalled";
    private static final String CUSTOMLABEL = "customlabel";

    private static final String INDEX = "pos";
    private static final String TIME = "time";
    private static final String FLAGS = "tiny";
    private static final String LEVEL = "level";


    private static final int FLAGS_ISTINY = 1;
    private static final int FLAGS_ISHIDDEN = 2;


    private static final String APP_TABLE_OLD = "apps";
    private static final String APP_TABLE = "apps2";
    private static final String[] appcolumns = {ACTVNAME, PKGNAME, LABEL, CATID, ISWIDGET, ISUNINSTALLED, CUSTOMLABEL};
    private static final String[] appcolumntypes = {"TEXT", "TEXT", "TEXT", "TEXT", "SHORT", "SHORT", "TEXT"};
    private static final String APP_TABLE_CREATE = buildCreateTableStmt(APP_TABLE, appcolumns, appcolumntypes);

    private static final String[] appcolumnsindex = {ACTVNAME, PKGNAME, CATID, ISUNINSTALLED};

    private static final String APP_ORDER_TABLE = "apps_order";
    private static final String[] appordercolumns = {CATID, ACTVNAME, INDEX, PKGNAME};
    private static final String[] appordercolumntypes = {"TEXT", "TEXT", "INT", "TEXT"};
    private static final String APP_ORDER_TABLE_CREATE = buildCreateTableStmt(APP_ORDER_TABLE, appordercolumns, appordercolumntypes);

    private static final String[] appordercolumnsindex = {CATID + ", " + ACTVNAME, INDEX};


    private static final String CATEGORIES_TABLE = "tab_order";
    private static final String[] categoriescolumns = {CATID, LABEL, LABELFULL, FLAGS, INDEX};
    private static final String[] categoriescolumntypes = {"TEXT primary key", "TEXT", "TEXT", "SHORT", "INT"};
    private static final String CATEGORIES_TABLE_CREATE = buildCreateTableStmt(CATEGORIES_TABLE, categoriescolumns, categoriescolumntypes);

    private static final String[] categoriescolumnsindex = {INDEX};


    private static final String APP_HISTORY_TABLE = "apps_hist";
    private static final String[] apphistorycolumns = {ACTVNAME, TIME, PKGNAME};
    private static final String[] apphistorycolumntypes = {"TEXT", "DATETIME", "TEXT"};
    private static final String APP_HISTORY_TABLE_CREATE = buildCreateTableStmt(APP_HISTORY_TABLE, apphistorycolumns, apphistorycolumntypes);

    private static final String[] apphistorycolumnsindex = {ACTVNAME, PKGNAME};


    private static final String APP_CAT_MAP_TABLE = "app_category_map";
    private static final String[] appcatmapcolumns = {ACTVNAME, PKGNAME, CATID, LEVEL};
    private static final String[] appcatmapcolumntypes = {"TEXT", "TEXT", "TEXT", "SHORT"};
    private static final String APP_CAT_MAP_TABLE_CREATE = buildCreateTableStmt(APP_CAT_MAP_TABLE, appcatmapcolumns, appcatmapcolumntypes);


    private static final String[] appcatmapcolumnsindex = {"ACTVNAME, LEVEL", "PKGNAME, LEVEL"};


    private static final String APP_CURSOR_SQL = "select " + ACTVNAME + " _id, " + PKGNAME + " pkg,  app." + LABEL + " label, tab." + LABEL + " category " + ", app." + CUSTOMLABEL + " customlabel" +
            " from " + APP_TABLE + " as app " +
            " inner join " + CATEGORIES_TABLE + " as tab on app." + CATID + "=tab." + CATID +
            " where (app." + LABEL + " like ? or app." + CUSTOMLABEL + " like ?) and " +  ISWIDGET + "=0 and (" + ISUNINSTALLED+"=0)" +
            " order by LOWER(case when customlabel is null then app.label else customlabel end) ";

    private boolean firstRun;

    private final Context mContext;

    private final DBClosedListener mDBClosedListener;

    private ComponentName latestInstall;
    private long latestInstallTime;

    public static DB openDB(Context context, DBClosedListener dBClosedListener) {
         return new DB(context, dBClosedListener);
    }

    private DB(Context context, DBClosedListener dBClosedListener ) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        mContext = context;
        mDBClosedListener = dBClosedListener;


        try {
            this.getWritableDatabase(); //force onCreate();
        } catch (SQLiteException se) {
            Log.e("db", se.getMessage(), se);
            try {
                Thread.sleep(1000);
                this.getWritableDatabase();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        if (isFirstRun()) {
            Log.d("db", "first run: creating categories");
            for (int i = 0; i < Categories.DefCategoryOrder.length; i++) {
                String cat = Categories.DefCategoryOrder[i];
                addCategory(cat, Categories.getCatLabel(mContext, cat), Categories.getCatFullLabel(mContext, cat), Categories.isTinyCategory(cat), Categories.isHiddenCategory(cat), i);
            }
        } else {
            Log.d("db", "opening database");
        }


    }

    private static String buildCreateTableStmt(String tablename, String[] cols, String[] coltypes) {

        String create = "CREATE TABLE " + tablename + " (";
        for (int i = 0; i < cols.length; i++) {
            if (i != 0) create += ", ";
            create += cols[i] + " " + coltypes[i];
        }
        create += ");";
        return create;

    }

    private static String buildIndexStmt(String tablename, String col) {


        return "CREATE INDEX " + ((col + tablename).replaceAll("\\W+", "_")) + " on " + tablename + "(" + col + ");";


    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        firstRun = true;
        Log.i("db", "create database");
        sqLiteDatabase.execSQL(APP_TABLE_CREATE);
        for (String createind : appcolumnsindex) {
            sqLiteDatabase.execSQL(buildIndexStmt(APP_TABLE, createind));
        }

        sqLiteDatabase.execSQL(APP_ORDER_TABLE_CREATE);
        for (String createind : appordercolumnsindex) {
            sqLiteDatabase.execSQL(buildIndexStmt(APP_ORDER_TABLE, createind));
        }

        sqLiteDatabase.execSQL(CATEGORIES_TABLE_CREATE);
        for (String createind : categoriescolumnsindex) {
            sqLiteDatabase.execSQL(buildIndexStmt(CATEGORIES_TABLE, createind));
        }

        sqLiteDatabase.execSQL(APP_HISTORY_TABLE_CREATE);
        for (String createind : apphistorycolumnsindex) {
            sqLiteDatabase.execSQL(buildIndexStmt(APP_HISTORY_TABLE, createind));
        }

        buildCatTable(sqLiteDatabase);


    }

    private void buildCatTable(SQLiteDatabase sqLiteDatabase) {
        Log.i("db", "creating category table");
        sqLiteDatabase.execSQL("drop table if exists " + APP_CAT_MAP_TABLE);
        sqLiteDatabase.execSQL(APP_CAT_MAP_TABLE_CREATE);
        for (String createind : appcatmapcolumnsindex) {
            sqLiteDatabase.execSQL(buildIndexStmt(APP_CAT_MAP_TABLE, createind));
        }

        loadCategories(sqLiteDatabase, true, R.raw.submitted_activities,1);
        loadCategories(sqLiteDatabase, false, R.raw.submitted_packages, 2);
        loadCategories(sqLiteDatabase, false, R.raw.packages1,3);
        loadCategories(sqLiteDatabase, false, R.raw.packages2,4);
    }

    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        Log.i("db", "upgrade database");

        if (oldVersion==1) {
            sqLiteDatabase.execSQL("alter table " + APP_TABLE_OLD + " add column " + ISUNINSTALLED + " SHORT");
            //sqLiteDatabase.execSQL(buildIndexStmt(APP_TABLE_OLD,  ISUNINSTALLED));

            ContentValues values = new ContentValues();
            values.put(ISUNINSTALLED, 0);
            sqLiteDatabase.update(APP_TABLE_OLD, values, null, null);
        }
        if (oldVersion<=2) {

            upgradeTo2(sqLiteDatabase);
        }

        if (oldVersion<6) {
            sqLiteDatabase.execSQL("alter table " + APP_TABLE + " add column " + CUSTOMLABEL + " TEXT");
        }


        if (oldVersion<10) {
            ContentValues values = new ContentValues();
            values.put(INDEX, 101);
            sqLiteDatabase.update(CATEGORIES_TABLE, values, CATID + "=?", new String[]{Categories.CAT_SEARCH});
        }

        if (oldVersion<11) {
            buildCatTable(sqLiteDatabase);
        }

        sqLiteDatabase.delete(APP_ORDER_TABLE, PKGNAME + " is null", null);
    }
    public boolean isFirstRun() {
        return firstRun;
    }


    public List<ComponentName> getAppNames() {
        List<ComponentName> actvnames = new ArrayList<>();

        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(APP_TABLE, new String[]{ACTVNAME, PKGNAME}, ISUNINSTALLED+"=0", null, null, null, LABEL);
        try {
            while (cursor.moveToNext()) {
                String actv = cursor.getString(cursor.getColumnIndex(ACTVNAME));
                String pkg = cursor.getString(cursor.getColumnIndex(PKGNAME));
                try {
                    ComponentName cn = new ComponentName(pkg, actv);
                    actvnames.add(cn);
                } catch (Exception e) {
                    Log.e("LaunchDB", e.getMessage(), e);
                }
            }
        } finally {
            cursor.close();
        }
        return actvnames;
    }

    public AppLauncher getApp(ComponentName appname) {

        String actvname = appname.getClassName();
        String pkgname =  appname.getPackageName();

        AppLauncher appLauncher = null;
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(APP_TABLE, appcolumns, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname}, null, null, null);
        try {
            if (cursor.moveToNext()) { //ACTVNAME, PKGNAME, LABEL, CATID
                //pkgname = cursor.getString(1);
                String label = cursor.getString(cursor.getColumnIndex(LABEL));
                String catID = cursor.getString(cursor.getColumnIndex(CATID));
                boolean widget = cursor.getShort(cursor.getColumnIndex(ISWIDGET)) == 1;
                String customlabel = cursor.getString(cursor.getColumnIndex(CUSTOMLABEL));

                // Log.d("LaunchDB", "getApp " + pkgname + " " + catID);
                appLauncher = AppLauncher.createAppLauncher(actvname, pkgname, customlabel==null?label:customlabel, catID, widget);
            }
        } finally {
            cursor.close();
        }
        return appLauncher;
    }

    public boolean appHasCustomLabel(ComponentName appname) {
        String actvname = appname.getClassName();
        String pkgname =  appname.getPackageName();

        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.query(APP_TABLE, new String [] {CUSTOMLABEL}, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname}, null, null, null);
        try {
            if (cursor.moveToNext()) { //ACTVNAME, PKGNAME, LABEL, CATID
                String customlabel = cursor.getString(cursor.getColumnIndex(CUSTOMLABEL));
                if (customlabel!=null && !customlabel.isEmpty()) {
                    return true;
                }
            }
        } finally {
            cursor.close();
        }
        return false;
    }

    public List<AppLauncher> getAppsForPackage(String pkgname) {


        List<AppLauncher> appLaunchers = new ArrayList<>();

        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(APP_TABLE, appcolumns, PKGNAME + "=?", new String[]{pkgname}, null, null, null);
        try {
            while (cursor.moveToNext()) { //ACTVNAME, PKGNAME, LABEL, CATID
                String actvname = cursor.getString(cursor.getColumnIndex(ACTVNAME));
                //pkgname = cursor.getString(1);
                String label = cursor.getString(cursor.getColumnIndex(LABEL));
                String catID = cursor.getString(cursor.getColumnIndex(CATID));
                boolean widget = cursor.getShort(cursor.getColumnIndex(ISWIDGET)) == 1;
                String customlabel = cursor.getString(cursor.getColumnIndex(CUSTOMLABEL));

                // Log.d("LaunchDB", "getApp " + pkgname + " " + catID);
                appLaunchers.add(AppLauncher.createAppLauncher(actvname, pkgname, customlabel==null?label:customlabel, catID, widget));
            }
        } finally {
            cursor.close();
        }
        return appLaunchers;
    }



    public String getAppCategory(ComponentName appname) {
        String actvname = appname.getClassName();
        String pkgname =  appname.getPackageName();

        String catID = null;
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(APP_TABLE, new String[]{CATID}, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname}, null, null, null);
        try {
            if (cursor.moveToNext()) { //ACTVNAME, PKGNAME, LABEL, CATID
                catID = cursor.getString(cursor.getColumnIndex(CATID));
            }
        } finally {
            cursor.close();
        }
        return catID;
    }

    public boolean isAppInstalled(ComponentName appname) {
        String actvname = appname.getClassName();
        String pkgname =  appname.getPackageName();

        boolean installed = false;
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(APP_TABLE, new String[]{ISUNINSTALLED}, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname}, null, null, null);
        try {
            if (cursor.moveToNext()) { //ACTVNAME, PKGNAME, LABEL, CATID
                installed = cursor.getShort(cursor.getColumnIndex(ISUNINSTALLED)) != 1;
            }
        } finally {
            cursor.close();
        }
        return installed;
    }


    public int getAppCount(String catID) {
        int count = 0;
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(APP_TABLE,  new String[]{"count(*)"}, CATID + "=? and ("+ISUNINSTALLED+"=0)", new String[]{catID}, null, null, null);
        try {
            if (cursor.moveToNext()) {
                count = cursor.getInt(0);
            }
        } finally {
            cursor.close();
        }
        return count;
    }

    public List<AppLauncher> getApps(String catID) {

        List<AppLauncher> apps = new ArrayList<>();

        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(APP_TABLE, appcolumns, CATID + "=? and ("+ISUNINSTALLED+"=0)", new String[]{catID}, null, null, CUSTOMLABEL + " ," + LABEL);
        try {
            while (cursor.moveToNext()) {
                String actv = cursor.getString(cursor.getColumnIndex(ACTVNAME));
                String pkg = cursor.getString(cursor.getColumnIndex(PKGNAME));
                String label = cursor.getString(cursor.getColumnIndex(LABEL));
                boolean widget = cursor.getShort(cursor.getColumnIndex(ISWIDGET)) == 1;
                String customlabel = cursor.getString(cursor.getColumnIndex(CUSTOMLABEL));

                //if (widget) {
                   // Log.d("db", "Found widget: " + actvname + " " + pkgname);
                //}
                apps.add(AppLauncher.createAppLauncher(actv, pkg, customlabel==null?label:customlabel, catID, widget));
            }
        } finally {
            cursor.close();
        }

        return apps;
    }

    public boolean addApp(AppLauncher launcher) {
        return addApp(launcher.getActivityName(), launcher.getPackageName(), launcher.getLabel(), launcher.getCategory(), launcher.isWidget());
    }

    public void addApps(List<AppLauncher> launchers) {
        SQLiteDatabase db = this.getWritableDatabase();
        for (AppLauncher launcher : launchers) {
            addApp(db, launcher.getActivityName(), launcher.getPackageName(), launcher.getLabel(), launcher.getCategory(), launcher.isWidget());
        }
    }

    public boolean addApp(String actvname, String pkgname, String label, String catID, boolean widget) {
        SQLiteDatabase db = this.getWritableDatabase();
       return addApp(db, actvname, pkgname, label, catID, widget);
    }

    public long getLatestInstallTime() {
        return latestInstallTime;
    }

    public ComponentName getLatestInstall() {
        return latestInstall;
    }

    private boolean addApp(SQLiteDatabase db, String actvname, String pkgname, String label, String catID, boolean widget) {
        try {

            if (pkgname!=null && actvname!=null) {
                latestInstall = new ComponentName(pkgname, actvname);
                latestInstallTime = System.currentTimeMillis();
            }

            String dbcat = getCategoryDisplay(catID);
            if (dbcat==null) {
                Log.i("LaunchDB", "Use of category not in the database: " + catID, new Throwable());
                catID = Categories.CAT_OTHER;  //the user deleted the category
            }

            //Log.d("LaunchDB", "actvname " + actvname + " pkgname "  +pkgname + " added to db");
            ContentValues values = new ContentValues();
            values.put(LABEL, label);
            values.put(CATID, catID);
            values.put(ISWIDGET, widget ? 1 : 0);
            values.put(ISUNINSTALLED, 0);

            if (db.update(APP_TABLE, values, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname})==0) {
                values.put(ACTVNAME, actvname);
                values.put(PKGNAME, pkgname);
                db.insert(APP_TABLE, null, values);
               // Log.i("LaunchDB", "inserted " + actvname + " " + pkgname);
            }
            AppLauncher.removeAppLauncher(actvname,pkgname);

            return true;
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't insert package " + pkgname, e);
        }
        return false;
    }

    public void updateAppLabel(String actvname, String pkgname, String label) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(LABEL, label);
        db.update(APP_TABLE, values, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname});
        AppLauncher.removeAppLauncher(actvname,pkgname);
    }

    public void setAppCustomLabel(String actvname, String pkgname, String customlabel) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        if (customlabel==null) {
            values.putNull(CUSTOMLABEL);
        } else {
            values.put(CUSTOMLABEL, customlabel);
        }
        db.update(APP_TABLE, values, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname});
        AppLauncher.removeAppLauncher(actvname,pkgname);

    }


    public Cursor getAppCursor(String filter) {
        SQLiteDatabase db = this.getReadableDatabase();

        //Cursor cursor = db.query(APP_TABLE, new String[]{CATID}, null, null, null, null, INDEX, null);
        Cursor cursor = db.rawQuery(
                APP_CURSOR_SQL,
                new String[]{filter, filter});

        return cursor;
    }

    public void updateAppCategory(ComponentName comp, String catID) {
        updateAppCategory(comp.getClassName(), comp.getPackageName(), catID);
    }


    public void updateAppCategory(String actvname, String pkgname, String catID) {
        SQLiteDatabase db = this.getWritableDatabase();


        ContentValues values = new ContentValues();
        values.put(CATID, catID);

        db.update(APP_TABLE, values, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname});

        // Log.d("LaunchDB", "update " + pkgname + " " + catID);
    }



//    public boolean deleteApp(ComponentName appname) {
//        return deleteApp(actvorpkgname, false);
//    }

    public void deleteApp(ComponentName appname) {

        String actvname = appname.getClassName();

        String pkgname = appname.getPackageName();

        try {
            AppLauncher app = getApp(appname);
            if (app != null && (app.isLink() || app.isWidget())) {
                SQLiteDatabase db = this.getWritableDatabase();

                db.delete(APP_TABLE, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname});
                AppLauncher.removeAppLauncher(actvname,pkgname);

                return;
            }
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't delete app " + actvname, e);
        }


        deleteApp(actvname, pkgname);
    }

    public boolean deleteApp(String actvname, String pkgname) {

        SQLiteDatabase db = this.getWritableDatabase();

        try {
            if (actvname==null) {

                List<AppLauncher> apps = getAppsForPackage(pkgname);
                if (apps != null) {
                    for (AppLauncher app: apps) {
                        if (app.isWidget() || app.isLink() || app.isActionLink()) {
                            db.delete(APP_TABLE, ACTVNAME + "=?", new String[]{app.getActivityName()});
                        }

                    }
                }
                ContentValues values = new ContentValues();
                values.put(ISUNINSTALLED, 1);

                db.update(APP_TABLE, values, PKGNAME + "=?", new String[]{pkgname});
            } else {


                ContentValues values = new ContentValues();
                values.put(ISUNINSTALLED, 1);

                db.update(APP_TABLE, values, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname});
            }
            AppLauncher.removeAppLauncher(actvname,pkgname);

            //db.delete(APP_TABLE, (isPackagename?PKGNAME:ACTVNAME) + "=?", new String[]{actvorpkgname});
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't delete app " + actvname, e);
            return false;
        }
        return true;
    }

    public boolean addCategory(String catID, String displayName, String displayNameFull) {
        return addCategory(catID, displayName, displayNameFull, -1);
    }

    public boolean addCategory(String catID, String displayName, String displayNameFull, int index) {
        return addCategory(catID, displayName, displayNameFull, false, false, index);
    }

    public boolean addCategory(String catID, String displayName, String displayNameFull, boolean isTiny, boolean isHidden) {
        return addCategory(catID, displayName, displayNameFull, isTiny, isHidden, -1);
    }

    public boolean addCategory(String catID, String displayName, String displayNameFull, boolean isTiny, boolean isHidden, int index) {
        if (catID==null || catID.isEmpty()) {
            return false;
        }
        try {

            for (String existcat: getCategories()) {
                if (existcat.equalsIgnoreCase(catID)) return false;
            }

            SQLiteDatabase db = this.getWritableDatabase();

            int flags = 0;
            if (isTiny) flags |= FLAGS_ISTINY;
            if (isHidden) flags |= FLAGS_ISHIDDEN;

            // Log.d("DB", "adding catID " + catID);
            ContentValues values = new ContentValues();
            values.put(CATID, catID);
            values.put(LABEL, displayName);
            values.put(LABELFULL, displayNameFull);
            values.put(FLAGS, flags);
            values.put(INDEX, index);

            db.insert(CATEGORIES_TABLE, null, values);
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't add catID " + catID, e);
            return false;
        }
        return true;
    }


    public boolean updateCategory(String catID, String displayName, String displayNameFull) {
        return updateCategory(catID, displayName, displayNameFull, false, false);
    }

    public boolean updateCategory(String catID, String displayName, String displayNameFull, boolean isTiny, boolean isHidden) {
        if (catID==null || catID.isEmpty()) {
            return false;
        }
        try {
            SQLiteDatabase db = this.getWritableDatabase();

            // Log.d("DB", "adding catID " + catID);
            ContentValues values = new ContentValues();

            int flags = 0;
            if (isTiny) flags |= FLAGS_ISTINY;
            if (isHidden) flags |= FLAGS_ISHIDDEN;

            values.put(LABEL, displayName);
            values.put(LABELFULL, displayNameFull);
            values.put(FLAGS, flags);

            db.update(CATEGORIES_TABLE, values, CATID + "=?", new String[]{catID});
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't select catID " + catID, e);
            return false;
        }
        return true;
    }

    public void hideCategory(String catID, boolean isHidden) {
        if (catID==null || catID.isEmpty()) {
            return;
        }
        boolean isTiny = isTinyCategory(catID);
        try {
            SQLiteDatabase db = this.getWritableDatabase();

            // Log.d("DB", "adding catID " + catID);
            ContentValues values = new ContentValues();

            int flags = 0;
            if (isTiny) flags |= FLAGS_ISTINY;
            if (isHidden) flags |= FLAGS_ISHIDDEN;

            values.put(FLAGS, flags);

            db.update(CATEGORIES_TABLE, values, CATID + "=?", new String[]{catID});
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't select catID " + catID, e);

        }

    }

    public boolean deleteCategory(String catID) {
        if (catID==null || catID.isEmpty()) {
            return false;
        }

        SQLiteDatabase db = this.getWritableDatabase();
        try {

            db.beginTransaction();

            {
                //delete the shortcuts we made
                if (catID.equals(Categories.CAT_DUMB)) {
                    db.delete(APP_TABLE, CATID + "=? and " + ACTVNAME + " like ?", new String[]{Categories.CAT_DUMB, AppLauncher.OLDSHORTCUT + "%"});
                }
                //Update existing apps with the deleted category to category other
                ContentValues values = new ContentValues();
                values.put(CATID, Categories.CAT_OTHER);
                db.update(APP_TABLE, values, CATID + "=?", new String[]{catID});
            }

            {
                //Move old cat app order to category other
                List<ComponentName> oldAppOrder = getAppCategoryOrder(catID);
                List<ComponentName> otherAppOrder = getAppCategoryOrder(Categories.CAT_OTHER);

                otherAppOrder.addAll(oldAppOrder);
                setAppCategoryOrder(Categories.CAT_OTHER, otherAppOrder, true);
            }

            db.delete(CATEGORIES_TABLE, CATID + "=?", new String[]{catID});
            db.setTransactionSuccessful();
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't delete catID " + catID, e);
            return false;
        } finally {
            db.endTransaction();
        }
        return true;
    }

    public List<String> getCategories() {
        List<String> categories = new ArrayList<>();

        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(true, CATEGORIES_TABLE, new String[]{CATID}, null, null, null, null, INDEX, null);
        try {
            //Log.d("DB", "getting catagories");
            while (cursor.moveToNext()) {
                String cat = cursor.getString(cursor.getColumnIndex(CATID));
                if (cat!=null) {
                    categories.add(cat);
                }
                //          Log.d("DB", "got catID " + cursor.getString(0));
            }
        } finally {
            cursor.close();
        }
        cursor.close();
        return categories;
    }

    public String getCategoryDisplay(String catID) {
        String display = null;
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(CATEGORIES_TABLE, new String[]{LABEL}, CATID + "=?", new String[]{catID}, null, null, null, null);
        try {
            if (cursor.moveToNext()) {
                display = cursor.getString(cursor.getColumnIndex(LABEL));
            }
        } finally {
            cursor.close();
        }
        return display;
    }

    public String getCategoryDisplayFull(String catID) {
        String display = null;
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(CATEGORIES_TABLE, new String[]{LABELFULL}, CATID + "=?", new String[]{catID}, null, null, null, null);
        try {
            if (cursor.moveToNext()) {
                display = cursor.getString(cursor.getColumnIndex(LABELFULL));
            }
        } finally {
            cursor.close();
        }
        return display;
    }

    public boolean isTinyCategory(String catID) {

        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(CATEGORIES_TABLE, new String[]{FLAGS}, CATID + "=?", new String[]{catID}, null, null, null, null);
        boolean tiny = false;
        try {
            if (cursor.moveToNext()) {
                tiny = ((cursor.getShort(cursor.getColumnIndex(FLAGS)) & FLAGS_ISTINY) == FLAGS_ISTINY);
            }
        } catch (Exception e) {
            Log.e("LaunchDB", "tiny error.", e);
        } finally {
            cursor.close();
        }
        return tiny;
    }


    public boolean isHiddenCategory(String catID) {

        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(CATEGORIES_TABLE, new String[]{FLAGS}, CATID + "=?", new String[]{catID}, null, null, null, null);
        boolean hidden = false;
        try {
            if (cursor.moveToNext()) {
                hidden = ((cursor.getShort(cursor.getColumnIndex(FLAGS)) & FLAGS_ISHIDDEN) == FLAGS_ISHIDDEN);
            }
        } catch (Exception e) {
            Log.e("LaunchDB", "tiny error.", e);
        } finally {
            cursor.close();
        }
        return hidden;
    }


    public void setCategoryOrder(ViewGroup container) {

        List<String> cats = new ArrayList<>();

        for (int i = 0; i < container.getChildCount(); i++) {
            Object tag = container.getChildAt(i).getTag();
            if (tag instanceof String) {
                cats.add((String) tag);
            }
        }

        setCategoryOrder(cats);
    }

    public void setCategoryOrder(List<String> cats) {
        SQLiteDatabase db = this.getWritableDatabase();

        try {
            db.beginTransaction();

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

                ContentValues values = new ContentValues();

                values.put(INDEX, i);

                db.update(CATEGORIES_TABLE, values, CATID + "=?", new String[]{cats.get(i)});
            }

            db.setTransactionSuccessful();
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't setCategoryOrder ", e);

        } finally {
            db.endTransaction();
        }
    }

    public void setAppCategoryOrder(String catID, ViewGroup container) {

        if (container==null) return;

        List<AppLauncher> apps = new ArrayList<>();

        for (int i = 0; i < container.getChildCount(); i++) {
            Object tag = container.getChildAt(i).getTag();
            if (tag instanceof AppLauncher) {
                apps.add((AppLauncher) tag);
            }
        }

        setAppCategoryOrder(catID, apps);
    }

    public void setAppCategoryOrder(String catID, List<AppLauncher> apps) {
        List<ComponentName> actvnames = new ArrayList<>();
        for (AppLauncher app : apps) {
            actvnames.add(new ComponentName(app.getPackageName(), app.getActivityName()));
        }
        setAppCategoryOrder(catID, actvnames, true);
    }

    public void setAppCategoryOrder(String catID, List<ComponentName> actvnames, boolean dummy) {
        SQLiteDatabase db = this.getWritableDatabase();
       // Log.d("db", "setAppCategoryOrder " + catID);
        try {
            db.beginTransaction();
            db.delete(APP_ORDER_TABLE, CATID + "=?", new String[]{catID}); //CATID, PKGNAME, INDEX};

            for (int i = 0; i < actvnames.size(); i++) {
                try {
                    ContentValues values = new ContentValues();
                    values.put(CATID, catID);
                    values.put(ACTVNAME, actvnames.get(i).getClassName());
                    values.put(PKGNAME, actvnames.get(i).getPackageName());
                    values.put(INDEX, i);
                    db.insert(APP_ORDER_TABLE, null, values);
                } catch (Exception e) {
                    Log.e("LaunchDB", "Can't setAppCategoryOrder for catID " + catID, e);
                }
                // Log.d("db", "  " + i + " " + actvnames.get(i).getPackageName());
            }

            db.setTransactionSuccessful();
        } catch (Exception e) {
            Log.e("LaunchDB", "Can't setAppCategoryOrder for catID " + catID, e);

        } finally {
            if (db.inTransaction()) {
                db.endTransaction();
            }
        }
    }

    public void addAppCategoryOrder(String catID, ComponentName actvname) {
        SQLiteDatabase db = this.getWritableDatabase();

        try {

            ContentValues values = new ContentValues();
            values.put(CATID, catID);
            values.put(ACTVNAME, actvname.getClassName());
            values.put(PKGNAME, actvname.getPackageName());
            values.put(INDEX, 100);
            db.insert(APP_ORDER_TABLE, null, values);

        } catch (Exception e) {
            Log.e("LaunchDB", "Can't setAppCategoryOrder for catID " + catID, e);

        }
    }

    public List<ComponentName> getAppCategoryOrder(String catID) {
        List<ComponentName> actvnames = new ArrayList<>();
        SQLiteDatabase db = this.getReadableDatabase();
        //Log.d("db", "getAppCategoryOrder " + catID);

        Cursor cursor = db.query(APP_ORDER_TABLE, new String[]{ACTVNAME, PKGNAME}, CATID + "=?", new String[]{catID}, null, null, INDEX);
        try {
           // int i=0;
            while (cursor.moveToNext()) {
                try {
                    actvnames.add(new ComponentName(cursor.getString(cursor.getColumnIndex(PKGNAME)), cursor.getString(cursor.getColumnIndex(ACTVNAME))));
                } catch (Exception e) {
                    Log.e("LaunchDB", e.getMessage(), e);
                }
              //  Log.d("db", "  " + i + " " + cursor.getInt(2) + " " + cursor.getString(1));
              //  i++;
            }
        } finally {
            cursor.close();
        }
        return actvnames;

    }

    public void appLaunched(ComponentName activityname) {

        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(ACTVNAME, activityname.getClassName());
        values.put(PKGNAME, activityname.getPackageName());
        values.put(TIME, System.currentTimeMillis());
        db.insert(APP_HISTORY_TABLE, null, values);


        //delete old apps
        Calendar old = Calendar.getInstance();
        old.add(Calendar.MONTH, -2);

        db.delete(APP_HISTORY_TABLE, TIME+"<?", new String[] { old.getTimeInMillis() + ""});

    }

    public void deleteAppLaunchedRecord(ComponentName appname) {

        String actvname = appname.getClassName();
        String pkgname =  appname.getPackageName();

        SQLiteDatabase db = this.getWritableDatabase();


        db.delete(APP_HISTORY_TABLE, ACTVNAME + "=? and " + PKGNAME + "=?", new String[]{actvname, pkgname});

    }

    public void deleteAppLaunchedRecords() {

        SQLiteDatabase db = this.getWritableDatabase();


        db.delete(APP_HISTORY_TABLE, null, null);

    }

    public List<ComponentName> getAppLaunchedList() {

        List<ComponentName> activitynames = new ArrayList<>();

        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(
                false,
                APP_HISTORY_TABLE,
                new String[]{ACTVNAME, PKGNAME},
                null, null, null, null, TIME + " desc", null);

        try {
            while (cursor.moveToNext()) {
                if (!cursor.isNull(0) && !cursor.isNull(1) ) {
                    ComponentName cn = new ComponentName(cursor.getString(cursor.getColumnIndex(PKGNAME)), cursor.getString(cursor.getColumnIndex(ACTVNAME)));
                    if (!activitynames.contains(cn)) {
                        activitynames.add(cn);
                    }
                }
            }
        } finally {
            cursor.close();
        }
        return activitynames;

    }

    public List<ComponentName> getAppLaunchedListUsage() {

        List<ComponentName> activitynames = new ArrayList<>();

        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(
                true,
                APP_HISTORY_TABLE,
                new String[]{ACTVNAME, PKGNAME},
                null, null, "1,2", null, "count(*) desc", null);

        try {
            while (cursor.moveToNext()) {
                if (!cursor.isNull(0) && !cursor.isNull(1) ) {
                    activitynames.add(new ComponentName(cursor.getString(cursor.getColumnIndex(PKGNAME)), cursor.getString(cursor.getColumnIndex(ACTVNAME))));
                }
            }
        } finally {
            cursor.close();
        }
        return activitynames;

    }


    public int getAppLaunchedCount(ComponentName activityname) {
        return getAppLaunchedCount(activityname, new Date(0));
    }

    public int getAppLaunchedCount(ComponentName activityname, Date after) {
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(
                APP_HISTORY_TABLE,
                new String[]{"count(*)"},
                ACTVNAME + "=? and " + PKGNAME + "=? and "+ TIME + ">?",
                new String[]{activityname.getClassName(), activityname.getPackageName(), after.getTime() + ""},
                null, null, null, null);

        int count = 0;
        try {
            if (cursor.moveToNext()) {
                count = cursor.getInt(0);
            }
        } finally {
            cursor.close();
        }
        return count;

    }

//    private String condensePackname(String packageOrActivityname) {
//
//        packageOrActivityname = packageOrActivityname
//                .replaceAll("\\bcom\\b", "c")
//                .replaceAll("\\borg\\b", "o")
//                .replaceAll("\\bmobi\\b", "m")
//                .replaceAll("\\bmobile\\b", "mi")
//                .replaceAll("\\bnet\\b", "n")
//                .replaceAll("\\bair\\b", "a")
//                .replaceAll("\\bandroid\\b", "an")
//        ;
//
//        return packageOrActivityname;
//    }


    private String condensePackname(String packageOrActivityname) {

        packageOrActivityname = packageOrActivityname
                .replaceAll("\\bcom\\b", "")
        ;

        return packageOrActivityname;
    }

    public String getCategoryForPackage(String packagename) {
        SQLiteDatabase db = this.getReadableDatabase();

        // {ACTVNAME, PKGNAME, CATID, LEVEL};

        Cursor cursor = db.query(
                APP_CAT_MAP_TABLE, new String[]{CATID},
                PKGNAME + "=?",   new String[]{condensePackname(packagename)}, null, null, LEVEL + "");

        String catid = null;
        try {
            if (cursor.moveToNext()) {
                catid = cursor.getString(cursor.getColumnIndex(CATID));
                catid = Categories.unabbreviate(catid);
            }
        } finally {
            cursor.close();
        }
        return catid;
    }


    public String getCategoryForActivity(String activityname) {
        SQLiteDatabase db = this.getReadableDatabase();

        // {ACTVNAME, PKGNAME, CATID, LEVEL};

        Cursor cursor = db.query(
                APP_CAT_MAP_TABLE, new String[]{CATID},
                ACTVNAME + "=?",   new String[]{condensePackname(activityname)}, null, null, LEVEL + "");

        String catid = null;
        try {
            if (cursor.moveToNext()) {
                catid = cursor.getString(cursor.getColumnIndex(CATID));
                catid = Categories.unabbreviate(catid);
            }
        } finally {
            cursor.close();
        }
        return catid;
    }

    private void loadCategories(SQLiteDatabase db, boolean isactivity, int set, int level) {

        Log.d("LaunchDB", "loadCategories " + level + " " + set);

        InputStream inputStream = mContext.getResources().openRawResource(set);
        String line;
        String[] lineSplit;

        int count = 0;
        try {
            db.beginTransaction();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String sql = "INSERT INTO " + APP_CAT_MAP_TABLE + " values(?,?,?,?)";
            SQLiteStatement statement = db.compileStatement(sql);

            while ((line = reader.readLine()) != null) {
                if (!line.isEmpty() && !line.startsWith("#")) {
                    lineSplit = line.split("=",2);
                    if (lineSplit.length==2) {  // {ACTVNAME, PKGNAME, CATID, LEVEL};
                        statement.clearBindings();
                        if (isactivity) {
                            statement.bindString(1, lineSplit[0]);
                            statement.bindString(2, "");
                        } else {
                            statement.bindString(1, "");
                            statement.bindString(2, lineSplit[0]);
                        }
                        statement.bindString(3, lineSplit[1]);
                        statement.bindLong(4, level);
                        try {
                            statement.executeInsert();
                        } catch (Exception e) {
                            Log.d("LaunchDB", "Can't add category", e);
                        }

                        if (count++%1000==0) {
                            db.setTransactionSuccessful();
                            db.endTransaction();
                            db.beginTransaction();
                        }
                    }
                }
            }
            db.setTransactionSuccessful();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            db.endTransaction();
            try {
                if (inputStream != null) inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Log.d("LaunchDB", " loaded " + count + " rows");

    }




    //synchronize these so that we can also synchronize on the backup and restore, just in case

    @Override
    public synchronized SQLiteDatabase getReadableDatabase() {
        return super.getReadableDatabase();
    }

    @Override
    public synchronized SQLiteDatabase getWritableDatabase() {
        return super.getWritableDatabase();
    }



    public static final String BK_PRE = "db_bak.";


    public List<String> listBackups() {
        List<String> list = new ArrayList<>();
        Pattern bakpat = Pattern.compile("^" + Pattern.quote(BK_PRE)  + "(.+)");
        for (String file: mContext.fileList()) {
            Matcher m = bakpat.matcher(file);
            if (m.matches()) {
                list.add(m.group(1));
            }
        }
        Collections.sort(list);
        Collections.reverse(list);
        return list;
    }

    public boolean hasBackup(String backupName) {
        return listBackups().contains(backupName);
    }

    public File pullBackup(String backupName) {
        return mContext.getFileStreamPath(BK_PRE + backupName);
    }

    public synchronized File backup(String optionalName) {

        close();

        if (optionalName!=null && optionalName.length()>0) {
            optionalName =  "." + optionalName.replaceAll("[./(){}\"*|\\\\$]", "_");
            if (optionalName.length()>20) {
                optionalName = optionalName.substring(0,20);
            }
        } else {
            optionalName = "";
        }

        List<File> files = SpecialIconStore.getAllIcons(mContext);

        files.add(mContext.getDatabasePath(DATABASE_NAME));

        files.add(FsTools.saveSharedPreferencesToFile(mContext.getSharedPreferences("theme", Context.MODE_PRIVATE), mContext.getFileStreamPath("themes.prefs")));

        files.add(FsTools.saveSharedPreferencesToFile(mContext.getSharedPreferences("default", Context.MODE_PRIVATE), mContext.getFileStreamPath("default.prefs")));

        files.add(FsTools.saveSharedPreferencesToFile(PreferenceManager.getDefaultSharedPreferences(mContext.getApplicationContext()), mContext.getFileStreamPath("main.prefs")));


        File destFile = mContext.getFileStreamPath(BK_PRE + (new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).format(new Date())) + optionalName + ".zip");

        return FsTools.compressFiles(destFile, files.toArray(new File[0]));
    }



    public boolean restoreFullpathBackup( String filePath) {
        File srcFile = new File(filePath);

        return restore(srcFile);
    }

    public boolean restoreFullpathBackup(File file) {
        File srcFile = new File(file.getPath());

        return restore(srcFile);
    }
    public boolean restoreBackup(String backupName) {

        File srcFile = mContext.getFileStreamPath(BK_PRE + backupName);

        return restore(srcFile);
    }

    @SuppressLint("ApplySharedPref")
    private synchronized boolean restore(File srcFile) {

        boolean ret = false;
        if (srcFile.exists() && srcFile.canRead()) {

            File destFile = mContext.getDatabasePath(DATABASE_NAME);

            if (srcFile.getName().toLowerCase().endsWith(".zip")) {

                List<File> files = FsTools.uncompressFiles(srcFile, mContext.getFilesDir());


                FsTools.loadSharedPreferencesFromFile(mContext.getSharedPreferences("theme", Context.MODE_PRIVATE), mContext.getFileStreamPath("themes.prefs"));
                FsTools.loadSharedPreferencesFromFile(mContext.getSharedPreferences("default", Context.MODE_PRIVATE), mContext.getFileStreamPath("default.prefs"));

                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext.getApplicationContext());
                prefs.edit().putBoolean(Theme.PREFS_UPDATE_KEY, true).commit();
                FsTools.loadSharedPreferencesFromFile(prefs, mContext.getFileStreamPath("main.prefs"));
                prefs.edit().remove(Theme.PREFS_UPDATE_KEY).commit();


                close();
                FsTools.copyFile(mContext.getFileStreamPath(destFile.getName()), destFile);

                ret = true;

            } else {

                close();
                ret = FsTools.decompressFile(srcFile, destFile) != null;

            }
            try {
                getWritableDatabase().close();
            } catch (Exception e) {
                Log.e("LaunchDB", "couldn't load db after restore", e);
                return false;
            }
        }
        return ret;
    }


    public boolean deleteBackup(String backupName) {

        File srcFile = mContext.getFileStreamPath(BK_PRE + backupName);

        return srcFile.delete();

    }

    public synchronized void deleteDatabase() {
        close();
        Thread.yield();
        mContext.deleteDatabase(DB.DATABASE_NAME);
        Thread.yield();
        File destFile = mContext.getDatabasePath(DATABASE_NAME);
        if (destFile!=null && destFile.exists()) {
            if (destFile.delete()) {
                Log.d("LaunchTime", "db was there");
            }
        }

    }

    @Override
    public synchronized void close() {
        if (mDBClosedListener!=null) {
            mDBClosedListener.onDBClosed();
        }
        super.close();
    }

    public interface DBClosedListener {
        void onDBClosed();
    }

///


    public void upgradeTo2(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(APP_TABLE_CREATE);
        for (String createind : appcolumnsindex) {
            sqLiteDatabase.execSQL(buildIndexStmt(APP_TABLE, createind));
        }

        boolean first = true;
        String cols = "";
        for (String col : appcolumns) {
            // if (oldVersion<6 && col.equals(CUSTOMLABEL)) continue;
            if (first) first = false;
            else cols += ", ";
            cols += col;

        }


        Log.i("db", "copy to new table");

        sqLiteDatabase.execSQL(String.format("insert into %s (%s) select %s from %s", APP_TABLE, cols, cols, APP_TABLE_OLD));
        sqLiteDatabase.execSQL("drop table " + APP_TABLE_OLD);

        sqLiteDatabase.execSQL("alter table " + APP_ORDER_TABLE + " add column " + PKGNAME + " TEXT");
        sqLiteDatabase.execSQL("alter table " + APP_HISTORY_TABLE + " add column " + PKGNAME + " TEXT");

        Cursor cursor = sqLiteDatabase.query(APP_TABLE, new String[]{ACTVNAME, PKGNAME}, null, null, null, null, ACTVNAME);
        try {
            while (cursor.moveToNext()) {
                String actv = cursor.getString(cursor.getColumnIndex(ACTVNAME));
                String pkg = cursor.getString(cursor.getColumnIndex(PKGNAME));

                ContentValues values = new ContentValues();
                values.put(PKGNAME, pkg);
                sqLiteDatabase.update(APP_ORDER_TABLE, values, ACTVNAME + "=?", new String[]{actv});
                values.put(PKGNAME, pkg);
                sqLiteDatabase.update(APP_HISTORY_TABLE, values, ACTVNAME + "=?", new String[]{actv});
            }
        } finally {
            cursor.close();
        }

        ContentValues values = new ContentValues();
        values.put(INDEX, 1);

        //move search/recent away from top to correct old order.
        //{CATID, LABEL, LABELFULL, FLAGS, INDEX};
        if (sqLiteDatabase.update(CATEGORIES_TABLE, values, CATID + "=\"" + Categories.CAT_SEARCH + "\" and " + INDEX + "=0", null)>0) {
            values.put(INDEX, 0);
            sqLiteDatabase.update(CATEGORIES_TABLE, values, CATID + "!=\"" + Categories.CAT_SEARCH + "\" and " + INDEX + "=1", null);
        }
    }



}