app/src/main/java/ru/henridellal/emerald/listener/OnAppUnlockLongClickListener.java
public class OnAppUnlockLongClickListener implements View.OnLongClickListener {
	private SoftReference<Context> contextRef;
	public OnAppUnlockLongClickListener(Context context) {
		contextRef = new SoftReference<Context>(context);
	}
	
	@Override
	public boolean onLongClick(final View arg0) {
		AlertDialog.Builder builder = new AlertDialog.Builder(contextRef.get());
		builder.setTitle(contextRef.get().getResources().getString(R.string.type_password));
		final EditText inputBox = new EditText(contextRef.get());
		inputBox.setInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD);
		builder.setView(inputBox);
		builder.setPositiveButton(android.R.string.yes, 
			new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					if (inputBox.getText().toString().equals(((Apps)contextRef.get()).options.getString(Keys.PASSWORD, ""))) {
						BaseData tag = (BaseData) arg0.getTag();
						if (tag instanceof AppData) {
							((Apps)contextRef.get()).itemContextMenu((AppData)tag);
						} else if (tag instanceof ShortcutData) {
							((Apps)contextRef.get()).itemContextMenu((ShortcutData)tag);
						}
					} else {
						Toast.makeText(contextRef.get(), contextRef.get().getResources().getString(R.string.wrong_password), Toast.LENGTH_LONG).show();
					}
				}
			});
		builder.setNegativeButton(android.R.string.cancel,
			new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					
				}
			});

		builder.setCancelable(true);
		builder.show();
		return false;
	}
}
app/src/main/java/ru/henridellal/emerald/util/IconPackManager.java
public class IconPackManager {
	private Context context;
	private String iconPackName;
	private float factor = 1.0f;
	private ArrayList<Bitmap> iconBacks;
	private Bitmap iconMask, iconUpon;
	private boolean transformDrawable = true;
	private Map<String, String> iconsData;
	private Resources iconPackRes;

	public Resources getResources() {
		return iconPackRes;
	}

	public IconPackManager(Context context, String iconPack) {
		this.context = context;
		setIconPack(iconPack);
	}

	public Map<String, String> getIcons() {
		return iconsData;
	}

	public void setIconPack(String iconPackName) {
		if (getIconPacks().containsValue(iconPackName) || "default".equals(iconPackName)) {
			this.iconPackName = iconPackName;
		} else {
			this.iconPackName = "default";
			Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
			editor.putString(Keys.ICON_PACK, this.iconPackName).commit();
		}
		setIcons();
	}

	private Bitmap loadBitmap(String drawableName) {
		int id = iconPackRes.getIdentifier(drawableName, "drawable", iconPackName);
		if (id > 0)
			return ((BitmapDrawable)iconPackRes.getDrawable(id)).getBitmap();
		else
			return null;
	}

	/* Returns Bitmap for default icon pack.
	 * Includes code for retrieving Oreo+ adaptive icons.
	 */
	public Bitmap getDefaultBitmap(Drawable d) {
		if (d instanceof BitmapDrawable) {
			return ((BitmapDrawable) d).getBitmap();
		} else if ((Build.VERSION.SDK_INT >= 26)
			&& (d instanceof AdaptiveIconDrawable)) {
				AdaptiveIconDrawable icon = ((AdaptiveIconDrawable)d);
				int w = icon.getIntrinsicWidth();
				int h = icon.getIntrinsicHeight();
				Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
				Canvas canvas = new Canvas(result);
				icon.setBounds(0, 0, w, h);
				icon.draw(canvas);
				return result;
		}
		float density = context.getResources().getDisplayMetrics().density;
		int defaultWidth = (int)(48* density);
		int defaultHeight = (int)(48* density);
		return Bitmap.createBitmap(defaultWidth, defaultHeight, Bitmap.Config.ARGB_8888);
	}

	/* transforms an Drawable object to Bitmap
	 * and adds effects from icon pack
	 */
	public Bitmap transformDrawable(Drawable d) {
		Bitmap b = getDefaultBitmap(d);
		if ((iconBacks == null && iconMask == null && iconUpon == null && factor == 1.f) || !transformDrawable)
		{
			return b;
		}
		int w, h;
		if (null != iconBacks && iconBacks.size() > 0) {
			w = iconBacks.get(0).getWidth();
			h = iconBacks.get(0).getHeight();
		} else {
			w = b.getWidth();
			h = b.getHeight();
		}
		Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(result);
		if (null != iconBacks && iconBacks.size() > 0) {
			canvas.drawBitmap(iconBacks.get((int)(Math.random()*iconBacks.size()-1)), 0, 0, null);
		}
		Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
		Bitmap scaledBitmap = Bitmap.createScaledBitmap(b, (int)(w*factor), (int)(h*factor), false);
		canvas.drawBitmap(scaledBitmap, w*(1-factor)/2, h*(1-factor)/2, p);
		if (iconMask != null) {
			p.setXfermode(new PorterDuffXfermode(Mode.DST_OUT));
			canvas.drawBitmap(iconMask, 0.f, 0.f, p);
			p.setXfermode(null);
		}
		if (iconUpon != null) {
			canvas.drawBitmap(iconUpon, 0, 0, null);
		}
		return result;
	}

	public Bitmap getBitmap(String component) {
		if (iconsData.containsKey(component)) {
			String drawableName = iconsData.get(component);
			return loadBitmap(drawableName);
		} else {
			return null;
		}
	}

	/* used by Options class to list all icon packs available*/
	public Map<String, String> getIconPacks() {
		Map<String, String> iconPacks = new HashMap<String, String>();
		PackageManager pm = context.getPackageManager();
		List<ResolveInfo> iconPacksInfo = pm.queryIntentActivities(new Intent("org.adw.launcher.THEMES"), PackageManager.GET_META_DATA);
		String iconPackPackage = null;
		String iconPackName = null;
		for (ResolveInfo info: iconPacksInfo) {
			iconPackPackage = info.activityInfo.packageName;
			try {
				ApplicationInfo ai = pm.getApplicationInfo(iconPackPackage, PackageManager.GET_META_DATA);
				iconPackName = pm.getApplicationLabel(ai).toString();
			} catch (PackageManager.NameNotFoundException e) {
				Log.e(Apps.APP_TAG, e.toString());
			}
			iconPacks.put(iconPackName, iconPackPackage);
		}
		return iconPacks;
	}

	//sets icon from cache in ImageView
	public static void setIcon(Context c, ImageView img, BaseData a) {
		File iconFile = Cache.getCustomIconFile(c, a);
		if (!iconFile.exists()) {
			if (a instanceof AppData) {
				iconFile = Cache.getIconFile(c, a);
			} else {
				iconFile = Cache.getShortcutIconFile(c, ((ShortcutData)a).getUri());
			}
		}
		if (iconFile.exists()){
			try {
				img.setImageDrawable(Drawable.createFromStream(
					new FileInputStream(iconFile), null));
			} catch (Exception e) {
				img.setImageResource(android.R.drawable.sym_def_app_icon);
			}
		} else {
			img.setImageResource(android.R.drawable.sym_def_app_icon);
		}
	}

	/* sets icons data */
	public void setIcons() {
		iconsData = new HashMap<String, String>();
		iconBacks = null;
		iconMask = null;
		iconUpon = null;
		factor = 1.0f;
		iconPackRes = null;
		if ("default".equals(iconPackName)) {
			return;
		}
		transformDrawable = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(Keys.TRANSFORM_DRAWABLE, true);
		String component = null;
		String drawable = null;
		PackageManager pm = context.getPackageManager();
		iconBacks = new ArrayList<Bitmap>();
		try {
			iconPackRes = pm.getResourcesForApplication(iconPackName);
		} catch (PackageManager.NameNotFoundException nameNotFound) {
			Log.e(Apps.APP_TAG, nameNotFound.toString());
		}
		try {
			int id = iconPackRes.getIdentifier("appfilter", "xml", iconPackName);
			XmlPullParser parser = iconPackRes.getXml(id);
			int parserEvent = parser.getEventType();
			
			while (parserEvent != XmlPullParser.END_DOCUMENT) {
				if (parserEvent == XmlPullParser.START_TAG) {
					final String parserName = parser.getName();
					if ("item".equals(parserName)) {
						for (int i = 0; i < parser.getAttributeCount(); i++) {
							if ("component".equals(parser.getAttributeName(i))) {
								component = parser.getAttributeValue(i);
								int c = component.indexOf("{");
								component = component.substring(c + 1, component.length() - 1);
							} else if ("drawable".equals(parser.getAttributeName(i))) {
								drawable = parser.getAttributeValue(i);
							}
						}
						iconsData.put(component, drawable);
					} else if ("iconback".equals(parserName)) {
						for (int i = 0; i < parser.getAttributeCount(); i++) {
							iconBacks.add(loadBitmap(parser.getAttributeValue(i)));
						}
					} else if ("iconmask".equals(parserName) && parser.getAttributeCount() > 0 && "iconmask".equals(parser.getAttributeName(0))) {
						iconMask = loadBitmap(parser.getAttributeValue(0));
					} else if ("iconupon".equals(parserName) && parser.getAttributeCount() > 0 && "iconupon".equals(parser.getAttributeName(0))) {
						iconUpon = loadBitmap(parser.getAttributeValue(0));
					} else if ("scale".equals(parserName) && "factor".equals(parser.getAttributeName(0))) {
						factor = Float.valueOf(parser.getAttributeValue(0));
					}
				}
				parserEvent = parser.next();
			}
		} catch (Exception e) {
			Log.e(Apps.APP_TAG, e.toString());
		}
	}
}
app/src/main/java/ru/henridellal/emerald/ui/Themer.java
public class Themer {

	public static int theme;

	public static final int DEFAULT_THEME = 0;
	public static final int LIGHT = 1;
	public static final int DARK = 2;
	public static final int WALLPAPER_LIGHT = 3;
	public static final int WALLPAPER_DARK = 4;
	
	public static void applyTheme(Activity activity, SharedPreferences options) {
		theme = Integer.parseInt(options.getString(Keys.THEME, activity.getResources().getString(R.string.defaultThemeValue)));
		switch (theme) {
			case LIGHT:
				activity.setTheme(R.style.AppTheme_Light);
				break;
			case DARK:
				activity.setTheme(R.style.AppTheme_Dark);
				break;
			case WALLPAPER_LIGHT:
				activity.setTheme(R.style.AppTheme_Light_Wallpaper);
				break;
			case WALLPAPER_DARK:
				activity.setTheme(R.style.AppTheme_Dark_Wallpaper);
				break;
			default:
				return;
		}
	}

	public static void setWindowDecorations(Activity activity, SharedPreferences options) {
		if (!options.getBoolean(Keys.FULLSCREEN, false)) {
			activity.getWindow().setStatusBarColor(options.getInt(Keys.STATUS_BAR_BACKGROUND, 0x22000000));
		}
		activity.getWindow().setNavigationBarColor(options.getInt(Keys.NAV_BAR_BACKGROUND, 0x22000000));
	}
}
app/src/main/java/ru/henridellal/emerald/listener/OnAppClickListener.java
public class OnAppClickListener implements View.OnClickListener {
	private SoftReference<Apps> appsRef;
	public OnAppClickListener(Apps apps) {
		appsRef = new SoftReference<Apps>(apps);
	}
	
	@Override
	public void onClick(View arg0) {
		if (arg0.getTag() instanceof BaseData) {
			appsRef.get().launch((BaseData)arg0.getTag());
		}
	}
}
app/src/main/java/ru/henridellal/emerald/LauncherApp.java
public class LauncherApp extends Application {
	private static LauncherApp singleton;
	public static LauncherApp getInstance() {
		return singleton;
	}
	
	private static IconPackManager ipm;
	private static CategoryManager cm;
	public static IconPackManager getIconPackManager() {
		return ipm;
	}
	public static CategoryManager getCategoryManager() {
		return cm;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		String iconPack = PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
			.getString(Keys.ICON_PACK, "default");
		ipm = new IconPackManager(getApplicationContext(), iconPack);
		cm = new CategoryManager(getApplicationContext());
	}
}
app/src/main/java/ru/henridellal/emerald/preference/Keys.java
public final class Keys {
	public static final String SHOW_TUTORIAL = "show_tutorial";
	public static final String MESSAGE_SHOWN = "message_shown";
	public static final String KEEP_IN_MEMORY = "keep_in_memory";
	public static final String VOLUME_BUTTONS = "volume_buttons";
	public static final String FULLSCREEN = "fullscreen";
	
	public static final String BAR_BACKGROUND = "bar_background";
	public static final String DOCK_BACKGROUND = "dock_background";
	public static final String NAV_BAR_BACKGROUND = "nav_bar_background";
	public static final String STATUS_BAR_BACKGROUND = "status_bar_background";
	public static final String APPS_WINDOW_BACKGROUND = "apps_background";
	
	public static final String ICON_PACK = "icon_pack";
	public static final String ICON_PACK_CHANGED = "icon_pack_changed";
	public static final String TRANSFORM_DRAWABLE = "transform_drawable";
	public static final String APP_SHORTCUT = "app_shortcut";
	public static final String PREV_APP_SHORTCUT = "prevApp_shortcut";
	public static final String SHOW_KEYBOARD_ON_START = "show_keyboard";
	public static final String TILE = "tile";
	public static final String HIDE_MAIN_BAR = "hide_main_bar";
	public static final String BOTTOM_MAIN_BAR = "bottom_main_bar";
	public static final String STACK_FROM_BOTTOM = "stack_from_bottom";
	public static final String SCROLLBAR = "scrollbar";
	public static final String INVERT_FONT_COLOR = "invert_font_color";
	
	public static final String ICON_SIZE = "icon_size";
	public static final String TEXT_SIZE = "text_size";
	public static final String FONT_STYLE = "font_style";
	public static final String COLUMN_WIDTH = "column_width";
	public static final String VERTICAL_SPACING = "vertical_spacing";
	
	public static final String ICON_SIZE_LANDSCAPE = "icon_size_land";
	public static final String TEXT_SIZE_LANDSCAPE = "text_size_land";
	public static final String COLUMN_WIDTH_LANDSCAPE = "column_width_land";
	public static final String VERTICAL_SPACING_LANDSCAPE = "vertical_spacing_land";
	
	public static final String DOCK_IN_LANDSCAPE = "show_dock_in_landscape";
	
	public static final String HOME = "home";
	public static final String HOME_BUTTON = "home_button";
	public static final String CATEGORY = "category";
	public static final String ORIENTATION = "orientation";
	public static final String SEARCH_PROVIDER = "search_provider";
	public static final String THEME = "theme";
	public static final String HISTORY_SIZE = "history_size";
	public static final String HIDE_HISTORY = "hide_history";
	public static final String HIDE_UNCLASSIFIED = "hide_unclassified";
	public static final String HIDE_ALL = "hide_all";
	public static final String PASSWORD = "password";
	
	public static final String[] BACKUP = {SHOW_TUTORIAL,
		KEEP_IN_MEMORY, FULLSCREEN, BAR_BACKGROUND, DOCK_BACKGROUND,
		NAV_BAR_BACKGROUND, STATUS_BAR_BACKGROUND,
		APPS_WINDOW_BACKGROUND, ICON_PACK, TRANSFORM_DRAWABLE,
		APP_SHORTCUT, PREV_APP_SHORTCUT,
		SHOW_KEYBOARD_ON_START, TILE, BOTTOM_MAIN_BAR,
		HIDE_MAIN_BAR, STACK_FROM_BOTTOM, SCROLLBAR, INVERT_FONT_COLOR,
		ICON_SIZE, TEXT_SIZE, FONT_STYLE, COLUMN_WIDTH,
		VERTICAL_SPACING, ICON_SIZE_LANDSCAPE, 
		TEXT_SIZE_LANDSCAPE, COLUMN_WIDTH_LANDSCAPE,
		VERTICAL_SPACING_LANDSCAPE, DOCK_IN_LANDSCAPE,
		HOME, HOME_BUTTON, ORIENTATION, SEARCH_PROVIDER,
		THEME, HISTORY_SIZE
	};
	
	public static final String[] restart = {MESSAGE_SHOWN, FULLSCREEN, BAR_BACKGROUND,
		NAV_BAR_BACKGROUND, STATUS_BAR_BACKGROUND, DOCK_BACKGROUND,
		APPS_WINDOW_BACKGROUND, SHOW_KEYBOARD_ON_START, TILE, BOTTOM_MAIN_BAR, HIDE_MAIN_BAR,
		STACK_FROM_BOTTOM, FONT_STYLE, SCROLLBAR, INVERT_FONT_COLOR, THEME, ORIENTATION,
		APP_SHORTCUT, KEEP_IN_MEMORY, PASSWORD, ICON_PACK_CHANGED};

}
app/src/main/java/ru/henridellal/emerald/data/CategoryManager.java
public class CategoryManager {
	public static final String ALL = "All";
	public static final String UNCLASSIFIED = "Unclassified";
	public static final String HIDDEN = "Hidden";
	public static final String HISTORY = "History";
	
	public static final byte PREVIOUS = 0;
	public static final byte NEXT = 1;
	
	private SoftReference<Context> contextRef;
	private String home;
	private String curCategory;
	private ArrayList<String> names;
	private Map<String,Category> categories;
	private SharedPreferences options;
	private ArrayList<String> history;
	private static final int HISTORY_MAX = 10;
	
	public CategoryManager(Context context) {
		contextRef = new SoftReference<Context>(context);
		this.options = PreferenceManager.getDefaultSharedPreferences(context);
		history = new ArrayList<String>();
		loadCategoriesList();
		curCategory = options.getString(Keys.CATEGORY, ALL);
		home = options.getString(Keys.HOME, ALL);
	}

	public void loadCategoriesList() {
		categories = DatabaseHelper.getCategories(contextRef.get());
		names = new ArrayList<String>(categories.keySet());
		sortNames();
	}

	public String getHome() {
		return home;
	}

	public void setHome(String catName) {
		if (haveCategory(catName)) {
			home = catName;
			options.edit().putString(Keys.HOME, home).commit();
		}
	}
	/*Checks if Categories instance has a category named s*/
	public boolean haveCategory(String s) {
		return names.contains(s);
	}
	/*Functions for spinner. Return names of categories
	which are neighbouring to the current*/
	public String getCategory(byte direction) {
		if (!curCategory.equals(HIDDEN)) {
			int result = names.indexOf(curCategory);
			int startPosition = result;
			for (int i = 0; i < names.size(); i++) {
				if (direction == PREVIOUS) {
					result--;
					result = (result > -1) ? result : names.size()-2;
				} else {
					result++;
					result = (result < names.size()-1) ? result : 0;
				}
				if (startPosition == result) {
					return null;
				}
				if (!isHidden(names.get(result))) {
					break;
				}
			}
			return names.get(result);
		} else {
			return null;
		}
	}

	public Category getCategory(String categoryName) {
		if (categories.containsKey(categoryName)) {
			return categories.get(categoryName);
		} else {
			return categories.get(ALL);
		}
	}

	public boolean isHidden(String categoryName) {
		if (HISTORY.equals(categoryName)) {
			return options.getBoolean(Keys.HIDE_HISTORY, false);
		} else if (UNCLASSIFIED.equals(categoryName)) {
			return options.getBoolean(Keys.HIDE_UNCLASSIFIED, false);
		} else if (ALL.equals(categoryName)) {
			return options.getBoolean(Keys.HIDE_ALL, false);
		} else {
			return false;
		}
	}
	
	/*Sets current category and saves its name in preferences*/
	public void setCurCategory(String category) {
		setCurCategory(category, true);
	}
	
	public void setCurCategory(String category, boolean push) {
		if (push)
			pushCategory(curCategory);
		curCategory = category;
		options.edit().putString(Keys.CATEGORY, category).commit();
	}

	//sets previously chosen category as current
	public void prevCategory() {
		String c = popCategory();
		if (c != null) {
			setCurCategory(c, false);
		}
	}
	
	private void pushCategory(String c) {
		if (history.size() > 0 && history.get(history.size()-1).equals(c))
			return;
		if (history.size() >= HISTORY_MAX)
			history.remove(0);
		//Log.v("TinyLaunch", "push "+c);
		history.add(c);
	}

	//returns (pops) previously chosen category from history
	private String popCategory() {
		while (history.size() > 0) {
			String c = history.get(history.size()-1);
			history.remove(history.size()-1);
			if (names.contains(c)) {
				return c;
			}
		}
		return home;
	}

	//return category names
	public ArrayList<String> getCategories() {
		return names;
	}

	//sorts categories in specific order
	//All > custom categories > Unclassified > History > Hidden
	public void sortNames() {
		Collections.sort(names, new Comparator<String>(){
			//private Map<String, Integer> defaultRanks = new Map<String, Integer>();
			@Override
			public int compare(String lhs, String rhs) {
				//int lhsNumber, rhsNumber;
				if (lhs.equals(ALL)) {
					if (rhs.equals(ALL))
						return 0;
					else
						return -1;
				} else if (lhs.equals(HIDDEN)) {
					if (rhs.equals(HIDDEN)) 
						return 0;
					else
						return 1;
				} else if (lhs.equals(UNCLASSIFIED)) {
					if (rhs.equals(UNCLASSIFIED)) 
						return 0;
					else if (rhs.equals(HIDDEN) || rhs.equals(HISTORY))
						return -1;
					else
						return 1;
				} else if (lhs.equals(HISTORY)) {
					if (rhs.equals(HISTORY))
						return 0;
					else if (rhs.equals(HIDDEN))
						return -1;
					else
						return 1;
				} else if (rhs.equals(ALL)) {
					return 1;
				} else if (rhs.equals(HIDDEN) || rhs.equals(UNCLASSIFIED) || rhs.equals(HISTORY)) {
					return -1;
				}
				return lhs.compareToIgnoreCase(rhs);
			}});
	}
	
	public ArrayList<String> getEditableCategories() {
		ArrayList<String> customNames = new ArrayList<String>();
		for (String s : names) {
			if (isEditable(s)) {
				customNames.add(s);
			}
		}
		return customNames;
	}
	//can be renamed and deleted or not
	public static boolean isCustom(String c) {
		return ! c.equals(ALL) && ! c.equals(UNCLASSIFIED) && ! c.equals(HIDDEN) && ! c.equals(HISTORY);
	}
	//can be filled with apps by the user
	public static boolean isEditable(String c) {
		return ! c.equals(ALL) && ! c.equals(UNCLASSIFIED);
	}
	//checks if category has an app
	public boolean in(BaseData item, String cat) {
		return categories.get(cat).getData().contains(item);
	}

	public String getCurCategory() {
		return curCategory;
	}
}
app/src/main/java/ru/henridellal/emerald/listener/OnAppLongClickListener.java
public class OnAppLongClickListener implements View.OnLongClickListener {
	private SoftReference<Apps> appsRef;
	public OnAppLongClickListener(Apps apps) {
		appsRef = new SoftReference<Apps>(apps);
	}
	
	@Override
	public boolean onLongClick(View arg0) {
		if (arg0.getTag() instanceof AppData) {
			appsRef.get().itemContextMenu((AppData)arg0.getTag());
		} else if (arg0.getTag() instanceof ShortcutData) {
			appsRef.get().itemContextMenu((ShortcutData)arg0.getTag());
		}
		return false;
	}
}
app/src/main/java/ru/henridellal/emerald/activity/Apps.java
public class Apps extends Activity
{
	private GestureDetector gestureDetector;
	private CategoryManager categories;
	private ArrayList<BaseData> curCatData;
	private GridView grid;
	public SharedPreferences options;
	public static final String PREF_APPS = "apps";
	public static final String APP_TAG = "ru.henridellal.emerald";
	public static final String ACTION_OPEN_MENU = "ru.henridellal.emerald.open_menu";
	private CustomAdapter adapter;
	public static final int GRID = 0;
	public static final int LIST = 1;
	private boolean lock, searchIsOpened, homeButtonPressed, launcherUpdate;

	private void addMenuShortcut() {
		Intent menuIntent = new Intent(this, Apps.class);
		menuIntent.setAction(ACTION_OPEN_MENU);
		Intent shortcutIntent = new Intent();
		shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, menuIntent);
		shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getResources().getString(R.string.launcher_menu));
		shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(this, R.mipmap.icon));
		shortcutIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
		sendBroadcast(shortcutIntent);
	}

	public void changePrefsOnRotate() {
		Resources res = getResources();
		float density = res.getDisplayMetrics().density;
		int iconSize, textSize, verticalSpacing, columnWidth;
		boolean hideDockInLandscape = !(options.getBoolean(Keys.DOCK_IN_LANDSCAPE, true));

		if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
			//Log.v(APP_TAG, "loadFilteredApps : Portrait orientation");
			iconSize = (int)(options.getInt(Keys.ICON_SIZE, res.getInteger(R.integer.icon_size_default)) * density);
			textSize = (int)(options.getInt(Keys.TEXT_SIZE, res.getInteger(R.integer.text_size_default)) * density);
	    	verticalSpacing = (int)(options.getInt(Keys.VERTICAL_SPACING, res.getInteger(R.integer.vertical_spacing_default)) * density);

	    	if (options.getBoolean(Keys.TILE, true)) {
	    		columnWidth = (int)(options.getInt(Keys.COLUMN_WIDTH, res.getInteger(R.integer.column_width_default)) * density);
	    		grid.setColumnWidth(columnWidth);
			} else {
	    		grid.setNumColumns(1);
	    	}
	    	if (hideDockInLandscape) {
	    		Dock.setAlwaysHide(false);
	    		Dock.unhide();
	    	}
		} else {
			//Log.v(APP_TAG, "loadFilteredApps : orientation");
			iconSize = (int)(options.getInt(Keys.ICON_SIZE_LANDSCAPE, res.getInteger(R.integer.icon_size_land_default)) * density);
			textSize = (int)(options.getInt(Keys.TEXT_SIZE_LANDSCAPE, res.getInteger(R.integer.text_size_land_default)) * density);
			verticalSpacing = (int)(options.getInt(Keys.VERTICAL_SPACING_LANDSCAPE, res.getInteger(R.integer.vertical_spacing_land_default)) * density);
	    	if (options.getBoolean(Keys.TILE, true)) {
				columnWidth = (int)(options.getInt(Keys.COLUMN_WIDTH_LANDSCAPE, res.getInteger(R.integer.column_width_land_default)) * density);
	    	} else {
	    		grid.setNumColumns(2);
	    		columnWidth = -1;
	    	}
			grid.setColumnWidth(columnWidth);
	    	if (hideDockInLandscape) {
	    		Dock.hide();
	    		Dock.setAlwaysHide(true);
	    	}
		}
		adapter.setIconSize(iconSize);
		adapter.setTextSize(textSize);
		grid.setVerticalSpacing(verticalSpacing);
	}

	public void loadFilteredApps() {
		Category curCategory = categories.getCategory(categories.getCurCategory());
		curCatData = DatabaseHelper.getEntries(this, curCategory.getName());
		adapter.update(curCatData);
		if (!options.getBoolean(Keys.HIDE_MAIN_BAR, false)) {
			((Button)findViewById(R.id.category_button)).setText(curCategory.getRepresentName(this));
		}
		/*if (null != mGetAppsThread)
			mGetAppsThread.quit();*/
	}

	//launches app and adds it to history
	public boolean launch(BaseData item) {
		Intent i = item.getLaunchIntent(this);
		if (i == null) {
			return false;
		}
		try {
			startActivity(i);
		} catch (ActivityNotFoundException e) {
			Toast.makeText(this, "Activity is not found", Toast.LENGTH_LONG).show();
			DatabaseHelper.removeApp(this, item.getComponent());
			loadFilteredApps();
		} finally {
			if (searchIsOpened) {
				closeSearch();
			}
		}
		return true;
	}

	
	public static ProgressDialog progress;
	protected boolean icons;
	protected int appShortcut;
	public static Handler handler;
	public static AppsTaskHandler loadAppsHandler;
	public static GetAppsThread mGetAppsThread;

	public static class AppsTaskHandler extends Handler {
		Apps activity;
		AppsTaskHandler(Apps activity) {
			this.activity = activity;
		}
		@Override
		public void handleMessage(Message msg) {
			try {
				progress.dismiss();
			}
			catch (Exception ignored) {}
			activity.loadFilteredApps();
			mGetAppsThread.quit();
		}
	}

	@SuppressWarnings("deprecation")
	public void loadAppsFromSystem(final boolean iconPackChanged) {
		if (null != progress) {
			try {
				if (null != mGetAppsThread)
					mGetAppsThread.quit();
				progress.dismiss();
			} catch (Exception e) {}
		}
		progress = new ProgressDialog(this);
		progress.setCancelable(false);
		progress.setMessage("Getting applications...");
		progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		progress.setIndeterminate(true);
		progress.show();
		//TODO access shared preferences the other way
		appShortcut = Integer.parseInt(options.getString(Keys.APP_SHORTCUT, "3"));
		icons = appShortcut >= CustomAdapter.ICON;
		// delete icons from cache if they aren't used
		if (!icons) {
			Cache.deleteIcons(this);
		}
		handler = new Handler();
		loadAppsHandler = new AppsTaskHandler(this);
		Runnable appsTask;
		if (Build.VERSION.SDK_INT >= 21) {
			appsTask = new AppsTaskLollipop(this, icons, iconPackChanged);
		} else {
			appsTask = new AppsTask(this, icons, iconPackChanged);
		}
		mGetAppsThread = new GetAppsThread("GetAppsThread");
		mGetAppsThread.start();
		mGetAppsThread.prepareHandler();
		mGetAppsThread.postTask(appsTask);
		
	}
	public static void writeIconToFile(File iconFile, Drawable d, String component) {
		try {
			Bitmap bmp;
			IconPackManager ipm = LauncherApp.getInstance().getIconPackManager();
			// get icon from icon pack
			if ((bmp = ipm.getBitmap(component)) == null) {
				// edit drawable to match icon pack
				bmp = ipm.transformDrawable(d);
			}
			// save icon in cache
			FileOutputStream out = new FileOutputStream(iconFile);
			bmp.compress(CompressFormat.PNG, 100, out);
			out.close();
		} catch (Exception e) {
			iconFile.delete();
		}
	}

	private void openCategoriesList() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setCancelable(true);
		categories.loadCategoriesList(); // TODO Remove after migration to 0.6.0
		final ArrayList<String> cats = new ArrayList<String>(categories.getCategories());
		cats.remove(CategoryManager.HIDDEN);
		ArrayList<String> toRemove = new ArrayList<String>();
		for (String category: cats) {
			if (categories.isHidden(category))
				toRemove.add(category);
		}
		cats.removeAll(toRemove);
		toRemove = null;
		final ArrayList<String> categoriesNames = new ArrayList<String>(cats.size());
		for (String category: cats) {
			categoriesNames.add(categories.getCategory(category).getRepresentName(this));
		}
		builder.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, categoriesNames), 
			new DialogInterface.OnClickListener(){
			@Override
			public void onClick(DialogInterface di, int catNum) {
				String newCat = cats.get(catNum);
				if (!newCat.equals(categories.getCurCategory())) {
					categories.setCurCategory(newCat);
					loadFilteredApps();
				}
			}
		});
		if (!isFinishing())
			builder.create().show();
	}
	//shows popup window for editing categories of app or shortcut
	private void itemEdit(final BaseData item) {
		//Log.v(APP_TAG, "Open app edit window");
		
		AlertDialog.Builder builder = new AlertDialog.Builder(this);

		builder.setTitle(item.getName());
		builder.setCancelable(true);

		ArrayList<String> editableCategories =  categories.getEditableCategories();
		final int nCategories = editableCategories.size();
		ArrayList<String> editableCategoriesNames = new ArrayList<String>(nCategories);
		final int position = grid.getFirstVisiblePosition();
		for (String category: editableCategories) {
			editableCategoriesNames.add(categories.getCategory(category).getRepresentName(this));
		}
		if (nCategories > 0) {
			final String[] editableCategoriesArray = new String[nCategories];
			editableCategories.toArray(editableCategoriesArray);
			final String[] editableCategoriesNamesArray = new String[nCategories];
			editableCategoriesNames.toArray(editableCategoriesNamesArray);
			
			final boolean[] checked = new boolean[nCategories];			

			for (int i = 0; i < nCategories ; i++) {
				checked[i] = DatabaseHelper.hasItem(this, item, editableCategoriesArray[i]);
			}

			final boolean[] oldChecked = checked.clone();

			builder.setMultiChoiceItems(editableCategoriesNamesArray, checked, 
				new DialogInterface.OnMultiChoiceClickListener() {							
				@Override
				public void onClick(DialogInterface dialog, int which, boolean isChecked) {
				}
			});
			builder.setPositiveButton("OK", new OnClickListener(){
				@Override
				public void onClick(DialogInterface arg0, int arg1) {
					for (int i = 0 ; i < nCategories ; i++) {
						if (checked[i] && ! oldChecked[i])
							DatabaseHelper.addToCategory(Apps.this, item, editableCategoriesArray[i]);
						else if (!checked[i] && oldChecked[i])
							DatabaseHelper.removeFromCategory(Apps.this, item, editableCategoriesArray[i]);
					}
					loadFilteredApps();
					grid.setSelection(position);
				}});
		}
		if (!isFinishing())
			builder.create().show();
	}
	public void itemContextMenu(final AppData item) {
		//Log.v(APP_TAG, "Open app edit window");
		
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		Resources res = getResources();
		builder.setTitle(item.getName());
		builder.setCancelable(true);
		
		String[] commands = new String[]{
			res.getString(R.string.aboutTitle),
				res.getString(R.string.findInMarket),
			res.getString(R.string.editAppCategories),
			res.getString(R.string.uninstall),
			(Dock.hasApp(item)) ? 
				res.getString(R.string.remove_from_dock):
				res.getString(R.string.add_to_dock),
			res.getString(R.string.change_icon)
		};
		builder.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, commands),
		new DialogInterface.OnClickListener(){
			public void onClick(DialogInterface di, int which) {
				Uri uri;
				switch(which) {
					case 0:
						uri = Uri.parse("package:"+ComponentName.unflattenFromString(
							item.getComponent()).getPackageName());
						startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri));
						break;
					case 1:
						uri = Uri.parse("market://details?id="+ComponentName.unflattenFromString(
							item.getComponent()).getPackageName());
						startActivity(new Intent(Intent.ACTION_VIEW, uri));
						break;
					case 2:
						itemEdit(item);
						break;
					case 3:
						uri = Uri.parse("package:"+ComponentName.unflattenFromString(
							item.getComponent()).getPackageName());
						startActivityForResult(new Intent(Intent.ACTION_DELETE, uri), 0);
						break;
					case 4:
						if (Dock.hasApp(item)) {
							Dock.remove(item);
						} else {
							if (!Dock.isFull()) {
								Dock.add(item);
							} else {
								Toast.makeText(Apps.this, getResources().getString(R.string.dock_is_full), Toast.LENGTH_LONG).show();
							}
						}
						Dock.update();
						break;
					case 5:
						Intent intent = new Intent(Apps.this, ChangeIconActivity.class);
						intent.putExtra(ChangeIconActivity.COMPONENT_NAME, item.getComponent());
						intent.putExtra(ChangeIconActivity.SHORTCUT_NAME, item.getName());
						startActivity(intent);
						break;
				}
			}
		});
		if (!isFinishing())
			builder.create().show();
	}
	
	public void itemContextMenu(final ShortcutData item) {
		//Log.v(APP_TAG, "Open app edit window");
		
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		Resources res = getResources();
		builder.setTitle(item.getName());
		builder.setCancelable(true);
		
		String[] commands = new String[]{
				res.getString(R.string.editCategories),
				res.getString(R.string.uninstall),
			(Dock.hasApp(item)) ?
					res.getString(R.string.remove_from_dock):
					res.getString(R.string.add_to_dock)
		};
		builder.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, commands),
		new DialogInterface.OnClickListener(){
			public void onClick(DialogInterface di, int which) {
				switch(which) {
					case 0:
						itemEdit(item);
						break;
					case 1:
						DatabaseHelper.removeShortcut(Apps.this, item.getUri());
						loadFilteredApps();
						break;
					case 2:
						if (Dock.hasApp(item)) {
							Dock.remove(item);
						} else {
							if (!Dock.isFull()) {
								Dock.add(item);
							} else {
								Toast.makeText(Apps.this, getResources().getString(R.string.dock_is_full), Toast.LENGTH_LONG).show();
							}
						}
						Dock.update();
						break;
				}
			}
		});
		if (!isFinishing())
			builder.create().show();
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		//Log.v(APP_TAG, "Configuration changed");
		super.onConfigurationChanged(newConfig);
		changePrefsOnRotate();
		loadFilteredApps();
	}
	
	private void menu() {
		//Log.v(APP_TAG, "Trying to open menu");
		if (lock) {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			//builder.setTitle("");
			builder.setMessage(getResources().getString(R.string.type_password));
			final EditText inputBox = new EditText(this);
			inputBox.setInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD);
			builder.setView(inputBox);
			builder.setPositiveButton(android.R.string.yes, 
				new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					if (inputBox.getText().toString().equals(options.getString(Keys.PASSWORD, ""))) {
						openOptionsMenu();
					} else {
						Toast.makeText(getApplicationContext(), getResources().getString(R.string.wrong_password), Toast.LENGTH_LONG).show();
					}
			} });
			builder.setCancelable(true);
			if (!isFinishing()) {
				builder.show();
			}
		} else {
			openOptionsMenu();
		}
	}
	private boolean isDefaultLauncher() {
		final Intent intent = new Intent(Intent.ACTION_MAIN);
		intent.addCategory(Intent.CATEGORY_HOME);
		final ResolveInfo resolveInfo =
				getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
        return resolveInfo != null &&
				getPackageName().equals(resolveInfo.activityInfo.packageName);
	}
	
	protected void onMenuButton(View v) {
		menu();
	}
	public void searchInWeb(String text) {
		String site = options.getString(Keys.SEARCH_PROVIDER, "https://duckduckgo.com/?q=");
		String url = site + text;
		Intent intent = new Intent(Intent.ACTION_VIEW);
		intent.setData(Uri.parse(url));
		try {
			startActivity(intent);
		} catch (ActivityNotFoundException e) {
			Toast.makeText(this, "Cannot handle "+ url + " request", Toast.LENGTH_LONG).show();
		}
	}
	public void openSearch() {
		//Log.v(APP_TAG, "Start searching");
		categories.setCurCategory(CategoryManager.ALL);
		loadFilteredApps();
		toggleFullscreen(false);
		if (options.getBoolean(Keys.HIDE_MAIN_BAR, false)) {
			findViewById(R.id.main_bar).setVisibility(View.VISIBLE);
		}
		findViewById(R.id.tabs).setVisibility(View.GONE);
		InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
		final EditText text = (EditText)findViewById(R.id.textField);
		if (imm != null) {
			imm.showSoftInput(grid, InputMethodManager.SHOW_IMPLICIT);
		}
		findViewById(R.id.searchBar).setVisibility(View.VISIBLE);
		text.setVisibility(View.VISIBLE);
		findViewById(R.id.webSearchButton).setVisibility(View.VISIBLE);
		if (Dock.isVisible()) {
			Dock.hide();
		}
		text.addTextChangedListener(new TextWatcher() {
			@Override
			public void afterTextChanged(Editable s) {}
			@Override
			public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
			@Override
			public void onTextChanged(CharSequence s, int start, int count, int after) {
				adapter.filter(s);
			}
		});
		text.setOnKeyListener(new View.OnKeyListener() {
			public boolean onKey(View v, int keyCode, KeyEvent event) {
				if (event.getAction() != KeyEvent.ACTION_DOWN) {
					return false;
				}
				// allow arrow up/down to focus the grid of apps
				if (keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
					grid.requestFocus();
					return true;
				}
				// On enter, search the internet
				if (keyCode == KeyEvent.KEYCODE_ENTER) {
					searchInWeb(text.getText().toString());
					return true;
				}
				return false;
			}
		});
		View.OnClickListener onClick = new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				searchInWeb(text.getText().toString());
			}
		};
		findViewById(R.id.webSearchButton).setOnClickListener(onClick);
		searchIsOpened = true;
		text.requestFocusFromTouch();
	}
	public void closeSearch() {
		//Log.v(APP_TAG, "Quit search");
		EditText text = (EditText)findViewById(R.id.textField);
		text.setText("");
		findViewById(R.id.searchBar).setVisibility(View.GONE);
		findViewById(R.id.webSearchButton).setVisibility(View.GONE);
		text.setVisibility(View.GONE);
		hideMainBarIfNeeded();
		if (!Dock.isEmpty()) {
			Dock.unhide();
		}
		searchIsOpened=false;
		toggleFullscreen(true);
	}
	private void hideMainBarIfNeeded() {
		if (options.getBoolean(Keys.HIDE_MAIN_BAR, false)) {
			findViewById(R.id.main_bar).setVisibility(View.GONE);
		} else {
			findViewById(R.id.tabs).setVisibility(View.VISIBLE);
		}
	}
	public void onMyClick(View v) {
		switch(v.getId()) {
			case R.id.searchButton:
				openSearch();
				break;
			case R.id.category_button:
				openCategoriesList();
				break;
			case R.id.menuButton:
				menu();
				break;
			case R.id.quit_hidden_apps:
				toggleHiddenCategory();
				break;
		}
	}
	
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		gestureDetector.onTouchEvent(event);
		return super.dispatchTouchEvent(event);
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (event.getAction() != KeyEvent.ACTION_DOWN) {
			return super.onKeyDown(keyCode, event);
		}
		if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BUTTON_SELECT) {
			menu();
			return true;
		} else if (keyCode == KeyEvent.KEYCODE_INFO || keyCode == KeyEvent.KEYCODE_BUTTON_Y) {
			BaseData item = getFocusedLaunchItem();
			if (item instanceof AppData) {
				itemContextMenu((AppData) item);
				return true;
			} else if (item instanceof ShortcutData) {
				itemContextMenu((ShortcutData) item);
				return true;
			}
		} else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE
			|| keyCode == KeyEvent.KEYCODE_BUTTON_B) {
			//Log.v(APP_TAG, "BACK pressed");
			if (searchIsOpened) {
				closeSearch();
			} else if (!isDefaultLauncher()) {
				finish();
				return true;
			} else {
				if (categories.getCurCategory().equals(CategoryManager.HIDDEN)) {
					findViewById(R.id.quit_hidden_apps).setVisibility(View.GONE);
					hideMainBarIfNeeded();
				}
				categories.prevCategory();
			}
			loadFilteredApps();
			return true;
		} else if (event.isAltPressed()) {
			if (keyCode >= KeyEvent.KEYCODE_1 && keyCode <= KeyEvent.KEYCODE_9) {
				Object app = Dock.getApp(keyCode-KeyEvent.KEYCODE_1);
				if (app instanceof BaseData && launch((BaseData) app)) {
					return true;
				}
			} else if (keyCode == KeyEvent.KEYCODE_0 && !searchIsOpened) {
				openSearch();
			} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
				categories.setCurCategory(categories.getCategory(CategoryManager.PREVIOUS));
				loadFilteredApps();
			} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
				categories.setCurCategory(categories.getCategory(CategoryManager.NEXT));
				loadFilteredApps();
			} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
				openCategoriesList();
			} else if (keyCode == KeyEvent.KEYCODE_O && !lock) {
				startActivity(new Intent(this, Options.class));
			} else if (keyCode == KeyEvent.KEYCODE_H && !lock) {
				toggleHiddenCategory();
			} else if (keyCode == KeyEvent.KEYCODE_R && !lock) {
				if (null != mGetAppsThread)
					mGetAppsThread.quit();
				loadAppsFromSystem(true);
			}
		} else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER
				||	keyCode == KeyEvent.KEYCODE_BUTTON_A || keyCode == KeyEvent.KEYCODE_BUTTON_1) {
			BaseData item = getFocusedLaunchItem();
			if (item != null && launch(item)) {
				return true;
			}
		} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
			if (options.getBoolean(Keys.VOLUME_BUTTONS, false)) {
				categories.setCurCategory(categories.getCategory(CategoryManager.PREVIOUS));
				loadFilteredApps();
				return true;
			}
		} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
			if (options.getBoolean(Keys.VOLUME_BUTTONS, false)) {
				categories.setCurCategory(categories.getCategory(CategoryManager.NEXT));
				loadFilteredApps();
				return true;
			}
		} else if (keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_L2
				|| keyCode == KeyEvent.KEYCODE_BUTTON_THUMBL|| keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
			categories.setCurCategory(categories.getCategory(CategoryManager.PREVIOUS));
			loadFilteredApps();
			return true;
		} else if (keyCode == KeyEvent.KEYCODE_BUTTON_R1 || keyCode == KeyEvent.KEYCODE_BUTTON_R2
				|| keyCode == KeyEvent.KEYCODE_BUTTON_THUMBR || keyCode == KeyEvent.KEYCODE_PAGE_UP) {
			categories.setCurCategory(categories.getCategory(CategoryManager.NEXT));
			loadFilteredApps();
			return true;
		} else if (event.isPrintingKey() && !searchIsOpened) {
			openSearch(); // search as you type
			EditText text = findViewById(R.id.textField);
			return text.onKeyDown(keyCode, event);
		}
		return super.onKeyDown(keyCode, event);
	}
	
	@Override
	public void onBackPressed() {}
	
	@Override
	protected void onStop() {
		super.onStop();
	}
	
	@Override
	protected void onPause() {
		//Log.v(APP_TAG, "onPause");
		super.onPause();
		if (searchIsOpened) {
			closeSearch();
		}
	}
	@Override
	protected void onDestroy() {
		//Log.v(APP_TAG, "onDestroy");
		grid.setOnScrollListener(null);
		grid.setOnTouchListener(null);
		grid.setOnItemClickListener(null);
		super.onDestroy();
	}
	
	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (resultCode == RESULT_OK) {
			loadFilteredApps();
		}
	}
	
	@Override
	protected void onNewIntent(Intent i) {
		//Log.v(APP_TAG, "onNewIntent");
		String action = i.getAction();
		if (Intent.ACTION_MAIN.equals(action)) {
			handleHomeButtonPress();
		} else if (ACTION_OPEN_MENU.equals(action)) {
			menu();
		}
		
		super.onNewIntent(i);
	}

	private BaseData getFocusedLaunchItem() {
		View view = getCurrentFocus();
		if (view instanceof GridView) {
			view = ((GridView) view).getSelectedView();
		}
		if (view != null) {
			Object viewTag = view.getTag();
			if (viewTag instanceof BaseData) {
				return (AppData)viewTag;
			}
		}
		return null;
	}

	private void handleHomeButtonPress() {
		try {
		homeButtonPressed = true;
		if (categories == null) {
			categories = LauncherApp.getCategoryManager();
		}
		if (categories.getCurCategory().equals(CategoryManager.HIDDEN)) {
			findViewById(R.id.quit_hidden_apps).setVisibility(View.GONE);
			hideMainBarIfNeeded();
			categories.setCurCategory(categories.getHome());
		} else if (categories.getCurCategory().equals(categories.getHome())) {
			String newCategory = options.getString(Keys.HOME_BUTTON, "");
			if (newCategory.length() > 0) {
				categories.setCurCategory(newCategory);
			} else {
				categories.setCurCategory(categories.getHome());
			}
		} else {
			categories.setCurCategory(categories.getHome());
		}
		loadFilteredApps();
		} catch (Exception e) {
			Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
		}
	}

	private void toggleFullscreen(boolean enableFullscreen) {
		if (options.getBoolean(Keys.FULLSCREEN, false)) {
			if (enableFullscreen) {
				getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
			} else {
				getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
			}
		}
	}

	private void toggleHiddenCategory() {
		if (CategoryManager.HIDDEN.equals(categories.getCurCategory())) {
			categories.setCurCategory(categories.getHome());
			findViewById(R.id.quit_hidden_apps).setVisibility(View.GONE);
			hideMainBarIfNeeded();
		} else {
			categories.setCurCategory(CategoryManager.HIDDEN);
			findViewById(R.id.searchBar).setVisibility(View.GONE);
			findViewById(R.id.tabs).setVisibility(View.GONE);
			if (options.getBoolean(Keys.HIDE_MAIN_BAR, false)) {
				findViewById(R.id.main_bar).setVisibility(View.VISIBLE);
			}
			findViewById(R.id.quit_hidden_apps).setVisibility(View.VISIBLE);
		}
		if (searchIsOpened) {
			closeSearch();
		}
		loadFilteredApps();
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		//Log.v(APP_TAG, "onCreate");
		options = PreferenceManager.getDefaultSharedPreferences(this);
		if (options.getBoolean(Keys.FULLSCREEN, false)) {
			requestWindowFeature(Window.FEATURE_NO_TITLE);
		}
		categories = LauncherApp.getCategoryManager();
		super.onCreate(savedInstanceState);
		if (Build.VERSION.SDK_INT >= 11) {
			Themer.applyTheme(this, options);
			if (options.getBoolean(Keys.KEEP_IN_MEMORY, false)) {
				PersistentNotification.setNotification(this);
			}
		}
		if (options.getBoolean(Keys.SHOW_TUTORIAL, true)) {
			startActivity(new Intent(this, TutorialActivity.class));
		}
		if (!DatabaseHelper.hasMenuShortcut(this) && Build.VERSION.SDK_INT < 26) {
			addMenuShortcut();
		}
		setRequestedOrientation(Integer.parseInt(options.getString(Keys.ORIENTATION, "2")));
		if (Build.VERSION.SDK_INT >= 21) {
			Themer.setWindowDecorations(this, options);
		}
		toggleFullscreen(true);
		setContentView(MainLayout.get(this, options));
		grid = (GridView) findViewById(R.id.appsGrid);
		adapter = new CustomAdapter(this);
		grid.setAdapter(adapter);
		options.edit().putBoolean(Keys.MESSAGE_SHOWN, true).commit();
		if (options.getBoolean(Keys.ICON_PACK_CHANGED, false)) {
			loadAppsFromSystem(true);
			options.edit().putBoolean(Keys.ICON_PACK_CHANGED, false).commit();
		}
		Dock.init(this);
		changePrefsOnRotate();
		gestureDetector = new GestureDetector(this, new SwipeListener(this));
		grid.setOnTouchListener(new View.OnTouchListener() {
			public boolean onTouch(View view, MotionEvent event) {
				return gestureDetector.onTouchEvent(event);
			}
		});
		grid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
				view.callOnClick();
			}
		});
	}
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		//Log.v(APP_TAG, "Menu item is selected");
		switch(item.getItemId()) {
		case R.id.full_scan:
			if (null != mGetAppsThread)
				mGetAppsThread.quit();
			loadAppsFromSystem(true);
			return true;
		case R.id.change_wallpaper:
			startActivity(new Intent(Intent.ACTION_SET_WALLPAPER));
			return true;
		case R.id.options:
			startActivity(new Intent(this, Options.class));
			return true;
		case R.id.access_hidden:
			toggleHiddenCategory();
			return true;
		default:
			return false;
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	protected void onResume() {
		super.onResume();
		//Log.v(APP_TAG, "onResume");
		
	    lock = options.getString(Keys.PASSWORD, "").length() > 0;
	    /*if (!homeButtonPressed) {
	    	try {
	    		//loadList(false);
	    	} catch (Exception e) {
	    		Toast.makeText(Apps.this, e.toString(), Toast.LENGTH_LONG).show();
	    	}
	    } else {
	    	homeButtonPressed = false;
	    }*/
	    if (homeButtonPressed) {
	    	homeButtonPressed = false;
	    }
	    
		if (DatabaseHelper.isDatabaseEmpty(this) && !launcherUpdate) {
			loadAppsFromSystem(true);
		} else {
			loadFilteredApps();
		}
		
		Dock.initApps();
		launcherUpdate = false;
	}
	@Override
	protected void onPostResume() {
		super.onPostResume();
		if (options.getBoolean(Keys.SHOW_KEYBOARD_ON_START, false)) {
			grid.postDelayed(new Runnable() {
				public void run() {
					openSearch();
				}
			}, 100);
		}
	}
}
app/src/main/java/ru/henridellal/emerald/data/BaseData.java
public abstract class BaseData implements Comparable<BaseData> {
	//component is a package name
	protected String component;
	
	//name is app name
	protected String name;

	public String getId() {
		return component;
	}
	public static final Comparator<BaseData> NameComparator = 
		new Comparator<BaseData>() {

		public int compare(BaseData a, BaseData b) {
			return a.name.compareToIgnoreCase(b.name);
		}
	};
	
	@Override
	public boolean equals(Object a) {
		if (! (a instanceof BaseData))
			return false;
		if (component == null) {
			return a == null || ((BaseData)a).component == null;
		}
		return component.equals( ((BaseData)a).component );
	}
	
	public BaseData() {}
	public BaseData(String component, String name) {
		this.component = component;
		this.name = name;
	}

	public String getComponent() {
		return this.component;
	}

	public String getName() {
		return this.name;
	}

	public void read(BufferedReader reader, String firstLineOfData) {}

	public void write(BufferedWriter reader) throws IOException {
		throw new IOException();
	}

	protected String readLine(BufferedReader reader, String key) throws IOException {
		String line = reader.readLine();
		if (line == null || !line.startsWith(key))
			throw new IOException();
		return line;
	}
	
	@Override
	public int compareTo(BaseData arg0) {
		return arg0.name.compareToIgnoreCase(this.name);
	}

	abstract public Intent getLaunchIntent(Context context);
	
	@Override
	public int hashCode() {
		if (component == null)
			return "NULL null NULL".hashCode();
		else
			return (component).hashCode();
	}
}


Question: import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Typeface;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.SectionIndexer;
import android.widget.TextView;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Set;
import ru.henridellal.emerald.activity.Apps;
import ru.henridellal.emerald.data.BaseData;
import ru.henridellal.emerald.data.CategoryManager;
import ru.henridellal.emerald.util.IconPackManager;
import ru.henridellal.emerald.preference.Keys;
import ru.henridellal.emerald.LauncherApp;
import ru.henridellal.emerald.listener.OnAppClickListener;
import ru.henridellal.emerald.listener.OnAppLongClickListener;
import ru.henridellal.emerald.listener.OnAppUnlockLongClickListener;
import ru.henridellal.emerald.R;
import ru.henridellal.emerald.ui.Themer;
				sections = new String[0];
				return;
			} else {
				String ch;
				int sectionIndex = 0;
				for (int i = 0; i < toDisplay.size(); i++) {
					BaseData a = toDisplay.get(i);
					ch = (a.getName().length() > 1) ? a.getName().substring(0,1).toUpperCase() : a.getName();
					if (!indexData.containsKey(ch)) {
						indexData.put(ch, sectionIndex);
						sectionIndex++;
					}
				}
				Set<String> sectionsSet = indexData.keySet();
				ArrayList<String> sectionsList = new ArrayList<String>(sectionsSet);
				Collections.sort(sectionsList);
				sections = new String[sectionsList.size()];
				sectionsList.toArray(sections);
			}
		}
	}
	
	@Override
	public boolean isEnabled(int position) {
		return true;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		View view;
		ImageView img;
		TextView tv;
		BaseData a = toDisplay.get(position);
		boolean isEmptyView = (convertView == null);
		if (isEmptyView) {
			LayoutInflater inflater = (LayoutInflater)contextRef.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			view = inflater.inflate(inflatedLayoutId, parent, false);
			img = (ImageView) view.findViewById(R.id.icon);
			tv = (TextView) view.findViewById(R.id.text);
			switch (appShortcut) {
				case TEXT:
					img.setVisibility(View.GONE);
					setTextViewParams(tv);
					break;
				case ICON:
					setImageViewLayoutParams(img);
					tv.setVisibility(View.GONE);
					break;
				default:
					setImageViewLayoutParams(img);
					setTextViewParams(tv);
			}
			view.setOnClickListener(onClickListener);
			view.setOnLongClickListener(onLongClickListener);
		} else {
			view = convertView;
			img = (ImageView) view.findViewById(R.id.icon);
			tv = (TextView) view.findViewById(R.id.text);	
		}
		// app shortcut
		view.setTag(a);
		if (appShortcut != ICON) {
			tv.setText(a.getName());
			tv.setTextSize(textSize);
		}
		if (appShortcut >= ICON) {
			IconPackManager.setIcon(contextRef.get(), img, a);
		}
		return view;
	}

	public void setTextViewParams(TextView tv) {
		tv.setTextColor(textColor);
		tv.setTypeface(Typeface.DEFAULT, fontStyle);
	}

	public void setImageViewLayoutParams(ImageView img) {
		if (null == imageViewLayoutParams) {
			imageViewLayoutParams = img.getLayoutParams();
			imageViewLayoutParams.width = iconSize;
			imageViewLayoutParams.height = iconSize;
		}
		img.setLayoutParams(imageViewLayoutParams);
	}
		
	public int getPositionForSection(int sectionIndex) {
		return indexData.get(sections[sectionIndex]);
	}
	public int getSectionForPosition(int position) {
		return 0;
	}
	public Object[] getSections() {
		return sections;
	}
		
	@Override
	public int getCount() {
		return toDisplay.size();
	}

	@Override
	public Object getItem(int position) {
		return null;
	}
	
	@Override
	public long getItemId(int position) {
		return 0;
	}

	public void update(ArrayList<BaseData> curCatData) {
		categoryData = curCatData;
		toDisplay = new ArrayList<BaseData>(categoryData);
		if (!CategoryManager.HISTORY.equals(LauncherApp.getCategoryManager().getCurCategory()))
			Collections.sort(toDisplay, comparator);
		setSections();
		notifyDataSetChanged();
	}
	
	private void setTextColor() {
Answer: 		int theme = Themer.theme;