/*
 * Copyright (C) 2013 Peter Gregus for GravityBox Project (C3C076@xda)
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.wrbug.gravitybox.nougat.preference;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import com.wrbug.gravitybox.nougat.R;
import com.wrbug.gravitybox.nougat.Utils;
import com.wrbug.gravitybox.nougat.GravityBoxSettings;
import com.wrbug.gravitybox.nougat.GravityBoxSettings.PrefsFragment;
import com.wrbug.gravitybox.nougat.GravityBoxSettings.PrefsFragment.IconPickHandler;
import com.wrbug.gravitybox.nougat.GravityBoxSettings.PrefsFragment.ShortcutHandler;
import com.wrbug.gravitybox.nougat.adapters.BasicIconListItem;
import com.wrbug.gravitybox.nougat.adapters.IIconListAdapterItem;
import com.wrbug.gravitybox.nougat.adapters.IconListAdapter;
import com.wrbug.gravitybox.nougat.shortcuts.ShortcutActivity;
import com.wrbug.gravitybox.nougat.util.SharedPreferencesUtils;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.DialogPreference;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.util.LruCache;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.Toast;

public class AppPickerPreference extends DialogPreference
        implements OnItemClickListener,
        OnItemSelectedListener,
        View.OnClickListener,
        View.OnLongClickListener {
    private static final String TAG = "GB:AppPickerPreference";
    public static final String SEPARATOR = "#C3C0#";

    public static final int MODE_APP = 0;
    public static final int MODE_SHORTCUT = 1;

    public static PrefsFragment sPrefsFragment;
    private static IconListAdapter sIconPickerAdapter;

    private Context mContext;
    private ListView mListView;
    private EditText mSearch;
    private ProgressBar mProgressBar;
    private AsyncTask<Void, Void, ArrayList<IIconListAdapterItem>> mAsyncTask;
    private String mDefaultSummaryText;
    private int mAppIconSizePx;
    private PackageManager mPackageManager;
    private Resources mResources;
    private int mMode;
    private Spinner mModeSpinner;
    private ImageButton mBtnAppIcon;
    private AppInfo mAppInfo;
    private int mAppIconPreviewSizePx;
    private Dialog mIconPickerDialog;
    private boolean mIconPickerEnabled = true;
    private int mIconPickSizePx;
    private boolean mNullItemEnabled = true;
    private String mValue;
    private boolean mAllowUnlockAction;
    private boolean mLaunchesFromLockscreen;
    private boolean mForceCustomIcon;
    private boolean mAllowGravityBoxActions;
    private Bundle mExtraData;

    private static LruCache<String, BitmapDrawable> sAppIconCache;

    static {
        final int cacheSize = Math.min((int) Runtime.getRuntime().maxMemory() / 6, 4194304);
        sAppIconCache = new LruCache<String, BitmapDrawable>(cacheSize) {
            @Override
            protected int sizeOf(String key, BitmapDrawable d) {
                return d.getBitmap().getByteCount();
            }
        };
    }

    public static void cleanupAsync(final Context context) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    List<String> usedFileNameList = new ArrayList<String>();
                    final String prefsName = context.getPackageName() + "_preferences";
                    SharedPreferences prefs = SharedPreferencesUtils.getSharedPreferences(context, prefsName);
                    // populate list of used icon files
                    Map<String, ?> keys = prefs.getAll();
                    for (Map.Entry<String, ?> entry : keys.entrySet()) {
                        Object val = entry.getValue();
                        if (entry.getKey().startsWith(GravityBoxSettings.PREF_KEY_FINGERPRINT_LAUNCHER_FINGER)) {
                            Set<String> set = (Set<String>) entry.getValue();
                            for (String item : set) {
                                if (item.startsWith("app:")) {
                                    val = item.split(":", 2)[1];
                                }
                            }
                        }
                        if ((val instanceof String) && ((String) val).contains("#Intent")) {
                            try {
                                Intent intent = Intent.parseUri((String) val, 0);
                                String fileName = intent.getStringExtra("icon");
                                if (fileName != null) {
                                    File iconFile = new File(fileName);
                                    if (iconFile.exists()) {
                                        usedFileNameList.add(iconFile.getName());
                                    }
                                }
                            } catch (URISyntaxException ue) {
                                continue;
                            }
                        }
                    }
                    // delete all unused icon files
                    File appPickerDir = new File(context.getFilesDir() + "/app_picker");
                    if (appPickerDir.exists() && appPickerDir.isDirectory()) {
                        File[] files = appPickerDir.listFiles();
                        for (File f : files) {
                            if (!usedFileNameList.contains(f.getName())) {
                                f.delete();
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.execute();
    }

    class AppInfo {
        String name;
        Drawable icon;

        public AppInfo() {
            name = mDefaultSummaryText;
        }
    }

    public AppPickerPreference(Context context, AttributeSet attrs) {
        super(context, attrs);

        mContext = context;
        mResources = mContext.getResources();
        mDefaultSummaryText = (String) getSummary();
        mAppIconSizePx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40,
                mResources.getDisplayMetrics());
        mAppIconPreviewSizePx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60,
                mResources.getDisplayMetrics());
        mIconPickSizePx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50,
                mResources.getDisplayMetrics());
        mPackageManager = mContext.getPackageManager();
        mMode = MODE_APP;
        mAppInfo = new AppInfo();
        mAllowGravityBoxActions = true;

        if (attrs != null) {
            mIconPickerEnabled = attrs.getAttributeBooleanValue(null, "iconPickerEnabled", true);
            mNullItemEnabled = attrs.getAttributeBooleanValue(null, "nullItemEnabled", true);
            mForceCustomIcon = attrs.getAttributeBooleanValue(null, "forceCustomIcon", false);
            mAllowGravityBoxActions = attrs.getAttributeBooleanValue(null, "allowGravityBoxActions", true);
        }

        setDialogLayoutResource(R.layout.app_picker_preference);
        setPositiveButtonText(null);

        if (sIconPickerAdapter == null) {
            initializeIconPickerAdapter();
        }
    }

    private void initializeIconPickerAdapter() {
        String[] labels = mResources.getStringArray(R.array.shortcut_icon_picker_labels);
        TypedArray icons = mResources.obtainTypedArray(R.array.shortcut_icon_picker_icons);
        if (labels.length != icons.length()) {
            icons.recycle();
            return;
        }

        ArrayList<IIconListAdapterItem> list = new ArrayList<IIconListAdapterItem>(labels.length);
        for (int i = 0; i < labels.length; i++) {
            BasicIconListItem item = new BasicIconListItem(labels[i], null,
                    icons.getResourceId(i, 0), 0, mResources);
            list.add(item);
        }
        sIconPickerAdapter = new IconListAdapter(mContext, list);
        sIconPickerAdapter.setAutoTintIcons(true);
        icons.recycle();
    }

    @Override
    protected void onBindView(View view) {
        super.onBindView(view);

        LinearLayout widgetFrameView = ((LinearLayout) view.findViewById(android.R.id.widget_frame));
        mBtnAppIcon = new ImageButton(mContext);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(mAppIconPreviewSizePx, mAppIconPreviewSizePx);
        lp.gravity = Gravity.CENTER;
        mBtnAppIcon.setLayoutParams(lp);
        mBtnAppIcon.setScaleType(ScaleType.CENTER_CROP);
        mBtnAppIcon.setImageDrawable(mAppInfo.icon);
        mBtnAppIcon.setFocusable(false);
        if (mIconPickerEnabled) {
            mBtnAppIcon.setOnClickListener(this);
            mBtnAppIcon.setOnLongClickListener(this);
        } else {
            mBtnAppIcon.setEnabled(false);
        }
        widgetFrameView.addView(mBtnAppIcon);
        widgetFrameView.setVisibility(View.VISIBLE);
    }

    @Override
    protected void onBindDialogView(View view) {
        super.onBindDialogView(view);

        mListView = (ListView) view.findViewById(R.id.icon_list);
        mListView.setOnItemClickListener(this);

        mSearch = (EditText) view.findViewById(R.id.input_search);
        mSearch.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable arg0) {
            }

            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1,
                                          int arg2, int arg3) {
            }

            @Override
            public void onTextChanged(CharSequence arg0, int arg1, int arg2,
                                      int arg3) {
                if (mListView.getAdapter() == null)
                    return;

                ((IconListAdapter) mListView.getAdapter()).getFilter().filter(arg0);
            }
        });

        mProgressBar = (ProgressBar) view.findViewById(R.id.progress_bar);

        mModeSpinner = (Spinner) view.findViewById(R.id.mode_spinner);
        ArrayAdapter<String> mModeSpinnerAdapter = new ArrayAdapter<String>(
                getContext(), android.R.layout.simple_spinner_item,
                new ArrayList<String>(Arrays.asList(
                        mContext.getString(R.string.app_picker_applications),
                        mContext.getString(R.string.app_picker_shortcuts))));
        mModeSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mModeSpinner.setAdapter(mModeSpinnerAdapter);
        mModeSpinner.setOnItemSelectedListener(this);
        mMode = mModeSpinner.getSelectedItemPosition();

        setData();
    }

    public void show() {
        showDialog(null);
    }

    public void setAllowUnlockAction(boolean allow) {
        mAllowUnlockAction = allow;
    }

    public void setLaunchesFromLockscreen(boolean value) {
        mLaunchesFromLockscreen = value;
    }

    public void setIconPickerEnabled(boolean value) {
        mIconPickerEnabled = value;
    }

    public Bundle getExtraData() {
        if (mExtraData == null) {
            mExtraData = new Bundle();
        }
        return mExtraData;
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        if (mAsyncTask != null && mAsyncTask.getStatus() == AsyncTask.Status.RUNNING) {
            mAsyncTask.cancel(true);
        }
        mAsyncTask = null;

        if (mIconPickerDialog != null && mIconPickerDialog.isShowing()) {
            mIconPickerDialog.dismiss();
        }
        mIconPickerDialog = null;
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index) {
        return a.getString(index);
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        if (restoreValue) {
            String value = getPersistedString(null);
            if (value != null && value.contains(SEPARATOR)) {
                value = convertOldValueFormat(value);
            }
            mAppInfo = getAppInfoFromValue(value);
            setSummary(mAppInfo.name);
        } else {
            setValue(null);
            setSummary(mDefaultSummaryText);
        }
    }

    @Override
    public void onClick(View v) {
        if (v != mBtnAppIcon ||
                sIconPickerAdapter == null ||
                getPersistedString(null) == null) return;

        if (mIconPickerDialog == null) {
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
                    .setTitle(R.string.icon_picker_choose_icon_title)
                    .setAdapter(sIconPickerAdapter, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                            try {
                                BasicIconListItem item = (BasicIconListItem) sIconPickerAdapter.getItem(which);
                                Intent intent = Intent.parseUri(getPersistedString(null), 0);
                                if (intent.hasExtra("icon")) {
                                    intent.removeExtra("icon");
                                }
                                intent.putExtra("iconResName", mResources.getResourceEntryName(
                                        item.getIconLeftId()));
                                setValue(intent.toUri(0));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

                        }
                    })
                    .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
            mIconPickerDialog = builder.create();
        }

        mIconPickerDialog.show();
    }

    @Override
    public boolean onLongClick(View v) {
        if (v != mBtnAppIcon || sPrefsFragment == null ||
                getPersistedString(null) == null) return true;

        sPrefsFragment.pickIcon(mIconPickSizePx, new IconPickHandler() {
            @Override
            public void onIconPicked(Bitmap icon) {
                try {
                    Intent intent = Intent.parseUri(getPersistedString(null), 0);
                    final Context context = AppPickerPreference.this.mContext;
                    final String dir = context.getFilesDir() + "/app_picker";
                    final String fileName = dir + "/" + UUID.randomUUID().toString();
                    File d = new File(dir);
                    d.mkdirs();
                    d.setReadable(true, false);
                    d.setExecutable(true, false);
                    File f = new File(fileName);
                    FileOutputStream fos = new FileOutputStream(f);
                    if (icon.compress(CompressFormat.PNG, 100, fos)) {
                        if (intent.hasExtra("iconResName")) {
                            intent.removeExtra("iconResName");
                        }
                        intent.putExtra("icon", f.getAbsolutePath());
                        f.setReadable(true, false);
                    }
                    fos.close();
                    setValue(intent.toUri(0));
                    sPrefsFragment.onSharedPreferenceChanged(getSharedPreferences(), getKey());
                } catch (Exception e) {
                    Toast.makeText(mContext, e.getMessage(), Toast.LENGTH_SHORT).show();
                    e.printStackTrace();
                }
            }

            @Override
            public void onIconPickCancelled() {
                Toast.makeText(mContext, R.string.app_picker_icon_pick_cancelled, Toast.LENGTH_SHORT).show();
            }
        });

        return true;
    }

    private String convertOldValueFormat(String oldValue) {
        try {
            String[] splitValue = oldValue.split(SEPARATOR);
            ComponentName cn = new ComponentName(splitValue[0], splitValue[1]);
            Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.addCategory(Intent.CATEGORY_LAUNCHER);
            intent.setComponent(cn);
            intent.putExtra("mode", MODE_APP);
            String newValue = intent.toUri(0);
            setValue(newValue);
            Log.d(TAG, "Converted old AppPickerPreference value: " + newValue);
            return newValue;
        } catch (Exception e) {
            Log.e(TAG, "Error converting old AppPickerPreference value: " + e.getMessage());
            return null;
        }
    }

    public void setDefaultSummary(String summary) {
        mDefaultSummaryText = summary;
    }

    private void setData() {
        mAsyncTask = new AsyncTask<Void, Void, ArrayList<IIconListAdapterItem>>() {
            @Override
            protected void onPreExecute() {
                super.onPreExecute();

                mListView.setVisibility(View.INVISIBLE);
                mSearch.setVisibility(View.GONE);
                mProgressBar.setVisibility(View.VISIBLE);
            }

            @Override
            protected ArrayList<IIconListAdapterItem> doInBackground(Void... arg0) {
                ArrayList<IIconListAdapterItem> itemList = new ArrayList<IIconListAdapterItem>();
                List<ResolveInfo> appList = new ArrayList<ResolveInfo>();

                List<PackageInfo> packages = mPackageManager.getInstalledPackages(0);
                Intent mainIntent = new Intent();
                if (mMode == MODE_APP) {
                    mainIntent.setAction(Intent.ACTION_MAIN);
                    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
                } else if (mMode == MODE_SHORTCUT) {
                    mainIntent.setAction(Intent.ACTION_CREATE_SHORTCUT);
                }
                for (PackageInfo pi : packages) {
                    if (this.isCancelled()) break;
                    if (mMode == MODE_SHORTCUT &&
                            pi.packageName.equals(mContext.getPackageName()) &&
                            !mAllowGravityBoxActions)
                        continue;
                    mainIntent.setPackage(pi.packageName);
                    List<ResolveInfo> activityList = mPackageManager.queryIntentActivities(mainIntent, 0);
                    for (ResolveInfo ri : activityList) {
                        appList.add(ri);
                    }
                }

                Collections.sort(appList, new ResolveInfo.DisplayNameComparator(mPackageManager));
                if (mNullItemEnabled) {
                    itemList.add(mMode == MODE_SHORTCUT ?
                            new ShortcutItem(mContext.getString(R.string.app_picker_none), null) :
                            new AppItem(mContext.getString(R.string.app_picker_none), null));
                }
                for (ResolveInfo ri : appList) {
                    if (this.isCancelled()) break;
                    String appName = ri.loadLabel(mPackageManager).toString();
                    IIconListAdapterItem ai = mMode == MODE_SHORTCUT ?
                            new ShortcutItem(appName, ri) : new AppItem(appName, ri);
                    itemList.add(ai);
                }

                return itemList;
            }

            @Override
            protected void onPostExecute(ArrayList<IIconListAdapterItem> result) {
                mProgressBar.setVisibility(View.GONE);
                mSearch.setVisibility(View.VISIBLE);
                mListView.setAdapter(new IconListAdapter(mContext, result));
                ((IconListAdapter) mListView.getAdapter()).notifyDataSetChanged();
                mListView.setVisibility(View.VISIBLE);
            }
        }.execute();
    }

    public void setValue(String value) {
        if (!callChangeListener(value))
            return;

        mValue = value;
        mAppInfo = getAppInfoFromValue(value);
        setSummary(mAppInfo.name);
        if (mBtnAppIcon != null) {
            mBtnAppIcon.setImageDrawable(mAppInfo.icon);
        }
        persistString(value);
    }

    public String getValue() {
        return mValue;
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        IIconListAdapterItem item = (IIconListAdapterItem) parent.getItemAtPosition(position);
        if (mMode == MODE_APP) {
            AppItem ai = (AppItem) item;
            setValue(ai.getValue());
            getDialog().dismiss();
        } else if (mMode == MODE_SHORTCUT) {
            ShortcutItem si = (ShortcutItem) item;
            if (si.getCreateShortcutIntent() == null) {
                setValue(null);
                getDialog().dismiss();
            } else {
                si.setShortcutCreatedListener(new ShortcutCreatedListener() {
                    @Override
                    public void onShortcutCreated(ShortcutItem sir) {
                        setValue(sir.getValue());
                        // we have to call this explicitly for some yet unknown reason...
                        sPrefsFragment.onSharedPreferenceChanged(getSharedPreferences(), getKey());
                        getDialog().dismiss();
                    }
                });
                sPrefsFragment.obtainShortcut((ShortcutItem) item);
            }
        }
    }

    private AppInfo getAppInfoFromValue(String value) {
        AppInfo appInfo = new AppInfo();
        if (value == null) return appInfo;

        try {
            Intent intent = Intent.parseUri(value, 0);

            int iconResId = intent.getStringExtra("iconResName") != null ?
                    mResources.getIdentifier(intent.getStringExtra("iconResName"),
                            "drawable", mContext.getPackageName()) : 0;
            if (iconResId != 0) {
                appInfo.icon = mResources.getDrawable(iconResId);
            } else if (intent.hasExtra("icon")) {
                final String appIconPath = intent.getStringExtra("icon");
                if (appIconPath != null) {
                    File f = new File(appIconPath);
                    if (f.exists() && f.canRead()) {
                        FileInputStream fis = new FileInputStream(f);
                        appInfo.icon = new BitmapDrawable(mResources, BitmapFactory.decodeStream(fis));
                        fis.close();
                    }
                }
            }

            int mode = intent.getIntExtra("mode", MODE_APP);
            if (mode == MODE_APP) {
                ComponentName cn = intent.getComponent();
                ActivityInfo ai = mPackageManager.getActivityInfo(cn, 0);
                appInfo.name = (ai.loadLabel(mPackageManager).toString());
                if (appInfo.icon == null) {
                    appInfo.icon = ai.loadIcon(mPackageManager);
                }
            } else if (mode == MODE_SHORTCUT) {
                appInfo.name = intent.getStringExtra("prefLabel");
            }
            return appInfo;
        } catch (Exception e) {
            e.printStackTrace();
            return appInfo;
        }
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        mMode = position;
        setData();
    }

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

    class AppItem implements IIconListAdapterItem {
        protected String mAppName;
        protected BitmapDrawable mAppIcon;
        protected ResolveInfo mResolveInfo;
        protected Intent mIntent;

        private AppItem() {
        }

        public AppItem(String appName, ResolveInfo ri) {
            mAppName = appName;
            mResolveInfo = ri;
            if (mResolveInfo != null) {
                mIntent = new Intent(Intent.ACTION_MAIN);
                mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
                ComponentName cn = new ComponentName(mResolveInfo.activityInfo.packageName,
                        mResolveInfo.activityInfo.name);
                mIntent.setComponent(cn);
                mIntent.putExtra("mode", MODE_APP);
                if (mForceCustomIcon) {
                    mIntent.putExtra("iconResName", "ic_shortcut_help");
                }
            }
        }

        public String getAppName() {
            return mAppName;
        }

        public String getValue() {
            return (mIntent == null ? null : mIntent.toUri(0));
        }

        public Intent getIntent() {
            return mIntent;
        }

        @Override
        public String getText() {
            return mAppName;
        }

        @Override
        public String getSubText() {
            return null;
        }

        protected String getKey() {
            return getValue();
        }

        @Override
        public Drawable getIconLeft() {
            if (mResolveInfo == null) return null;

            if (mAppIcon == null) {
                final String key = getKey();
                mAppIcon = sAppIconCache.get(key);
                if (mAppIcon == null) {
                    Bitmap bitmap = Utils.drawableToBitmap(mResolveInfo.loadIcon(mPackageManager));
                    bitmap = Bitmap.createScaledBitmap(bitmap, mAppIconSizePx, mAppIconSizePx, false);
                    mAppIcon = new BitmapDrawable(mResources, bitmap);
                    sAppIconCache.put(key, mAppIcon);
                }
            }
            return mAppIcon;
        }

        @Override
        public Drawable getIconRight() {
            return null;
        }
    }

    interface ShortcutCreatedListener {
        void onShortcutCreated(ShortcutItem item);
    }

    class ShortcutItem extends AppItem implements ShortcutHandler {
        private Intent mCreateShortcutIntent;
        private ShortcutCreatedListener mShortcutCreatedListener;

        public ShortcutItem(String appName, ResolveInfo ri) {
            mAppName = appName;
            mResolveInfo = ri;
            if (mResolveInfo != null) {
                mCreateShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
                ComponentName cn = new ComponentName(mResolveInfo.activityInfo.packageName,
                        mResolveInfo.activityInfo.name);
                mCreateShortcutIntent.setComponent(cn);
                // mark intent so we can later identify it comes from GB
                mCreateShortcutIntent.putExtra("gravitybox", true);
                if (mAllowUnlockAction) {
                    mCreateShortcutIntent.putExtra(ShortcutActivity.EXTRA_ALLOW_UNLOCK_ACTION, true);
                }
                if (mLaunchesFromLockscreen) {
                    mCreateShortcutIntent.putExtra(ShortcutActivity.EXTRA_LAUNCHES_FROM_LOCKSCREEN, true);
                }
            }
        }

        public void setShortcutCreatedListener(ShortcutCreatedListener listener) {
            mShortcutCreatedListener = listener;
        }

        @Override
        protected String getKey() {
            return mCreateShortcutIntent.toUri(0);
        }

        @Override
        public Intent getCreateShortcutIntent() {
            return mCreateShortcutIntent;
        }

        @Override
        public void onHandleShortcut(Intent intent, String name, String localIconResName, Bitmap icon) {
            if (intent == null) {
                Toast.makeText(mContext, R.string.app_picker_shortcut_null_intent, Toast.LENGTH_LONG).show();
                return;
            }

            mIntent = intent;
            mIntent.putExtra("mode", MODE_SHORTCUT);

            // generate label
            if (name != null) {
                mIntent.putExtra("label", name);
                mIntent.putExtra("prefLabel", mAppName + ": " + name);
            } else {
                mIntent.putExtra("label", mAppName);
                mIntent.putExtra("prefLabel", mAppName);
            }

            // process icon
            if (mForceCustomIcon) {
                mIntent.putExtra("iconResName", "ic_shortcut_help");
            } else if (localIconResName != null) {
                mIntent.putExtra("iconResName", localIconResName);
            } else if (icon != null) {
                try {
                    final Context context = AppPickerPreference.this.mContext;
                    final String dir = context.getFilesDir() + "/app_picker";
                    final String fileName = dir + "/" + UUID.randomUUID().toString();
                    File d = new File(dir);
                    d.mkdirs();
                    d.setReadable(true, false);
                    d.setExecutable(true, false);
                    File f = new File(fileName);
                    FileOutputStream fos = new FileOutputStream(f);
                    final boolean iconSaved = icon.compress(CompressFormat.PNG, 100, fos);
                    if (iconSaved) {
                        mIntent.putExtra("icon", f.getAbsolutePath());
                        f.setReadable(true, false);
                    }
                    fos.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            // callback to shortcut created listener if set
            if (mShortcutCreatedListener != null) {
                mShortcutCreatedListener.onShortcutCreated(this);
            }
        }

        @Override
        public void onShortcutCancelled() {
            Toast.makeText(mContext, R.string.app_picker_shortcut_cancelled, Toast.LENGTH_SHORT).show();
        }
    }
}