/*
 * Part of Phonk http://www.phonk.io
 * A prototyping platform for Android devices
 *
 * Copyright (C) 2013 - 2017 Victor Diaz Barrales @victordiaz (Protocoder)
 * Copyright (C) 2017 - Victor Diaz Barrales @victordiaz (Phonk)
 *
 * Phonk is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Phonk is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Phonk. If not, see <http://www.gnu.org/licenses/>.
 *
 */

package io.phonk.runner.apprunner.api;

import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.drawable.BitmapDrawable;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Handler;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.telephony.SmsManager;
import android.util.DisplayMetrics;
import android.view.InputDevice;
import android.view.KeyEvent;

import androidx.core.app.NotificationManagerCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import io.phonk.runner.AppRunnerFragment;
import io.phonk.runner.apidoc.annotation.PhonkMethod;
import io.phonk.runner.apidoc.annotation.PhonkObject;
import io.phonk.runner.apprunner.AppRunner;
import io.phonk.runner.apprunner.api.common.ReturnInterface;
import io.phonk.runner.apprunner.api.common.ReturnObject;
import io.phonk.runner.apprunner.api.other.ApplicationInfo;
import io.phonk.runner.base.utils.AndroidUtils;
import io.phonk.runner.base.utils.Intents;
import io.phonk.runner.base.utils.MLog;

@PhonkObject
public class PDevice extends ProtoBase {

    private BroadcastReceiver batteryReceiver;
    private BroadcastReceiver onNotification;
    private BroadcastReceiver smsReceiver;

    private ReturnInterface mOnKeyDownfn;
    private ReturnInterface mOnKeyUpfn;
    private ReturnInterface mOnKeyEventfn;

    private boolean isKeyPressInit = false;

    public String deviceId;


    /**
     * Interface for key up / down
     */
    public interface onKeyListener {
        void onKeyDown(KeyEvent event);

        void onKeyUp(KeyEvent event);

        void onKeyEvent(KeyEvent event);
    }

    public PDevice(AppRunner appRunner) {
        super(appRunner);
    }

    @Override
    public void initForParentFragment(AppRunnerFragment fragment) {
        super.initForParentFragment(fragment);
    }


    @TargetApi(Build.VERSION_CODES.KITKAT)
    public void getInputDevices() {
        InputManager inputManager = (InputManager) getAppRunner().getAppContext().getSystemService(Context.INPUT_SERVICE);
        int[] devicesId = inputManager.getInputDeviceIds();

        MLog.d(TAG, "" + devicesId.length);

        ArrayList gameControllerDeviceIds = new ArrayList();


        for (int i = 0; i < devicesId.length; i++) {
            InputDevice device = inputManager.getInputDevice(devicesId[i]);

            MLog.d(TAG, "controller number: " + device.getControllerNumber());
            MLog.d(TAG, "keyboard type: " + device.getKeyboardType());
            MLog.d(TAG, "product id: " + device.getProductId());
            MLog.d(TAG, "name: " + device.getName());
            MLog.d(TAG, "descriptor: " + device.getDescriptor());

            int sources = device.getSources();

            // Verify that the device has gamepad buttons, control sticks, or both.
            if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) {
                // This device is a game controller. Store its device ID.
                if (!gameControllerDeviceIds.contains(devicesId[i])) {
                    gameControllerDeviceIds.add(devicesId[i]);
                }
            }
        }

        Handler handler = new Handler();
        inputManager.registerInputDeviceListener(new InputManager.InputDeviceListener() {
            @Override
            public void onInputDeviceAdded(int deviceId) {
                MLog.d(TAG, "added " + deviceId);
            }

            @Override
            public void onInputDeviceRemoved(int deviceId) {
                MLog.d(TAG, "removed " + deviceId);
            }

            @Override
            public void onInputDeviceChanged(int deviceId) {
                MLog.d(TAG, "removed " + deviceId);
            }
        }, handler);

    }


    private ReturnObject keyEventToJs(KeyEvent event) {
        ReturnObject o = new ReturnObject();
        o.put("key", event.getKeyCode());
        o.put("id", event.getDeviceId());

        String action = "unknown";
        switch (event.getAction()) {
            case KeyEvent.ACTION_DOWN:
                action = "down";
                break;

            case KeyEvent.ACTION_UP:
                action = "up";
                break;

            case KeyEvent.ACTION_MULTIPLE:
                action = "multiple";
                break;
        }

        o.put("action", action);

        o.put("alt", event.isAltPressed());
        o.put("ctrl", event.isCtrlPressed());
        o.put("fn", event.isFunctionPressed());
        o.put("meta", event.isMetaPressed());
        o.put("chars", event.getCharacters());
        o.put("number", event.getNumber());

        return o;
    }

    private void keyInit() {
        if (isKeyPressInit) return;
        isKeyPressInit = true;

        (getActivity()).addOnKeyListener(new onKeyListener() {
            @Override
            public void onKeyUp(KeyEvent event) {
                if (mOnKeyUpfn != null) mOnKeyUpfn.event(keyEventToJs(event));
            }

            @Override
            public void onKeyDown(KeyEvent event) {
                if (mOnKeyDownfn != null) mOnKeyDownfn.event(keyEventToJs(event));
            }

            @Override
            public void onKeyEvent(KeyEvent event) {
                if (mOnKeyEventfn != null) mOnKeyEventfn.event(keyEventToJs(event));
            }
        });
    }

    /**
     * Tells which software / hardware / gamepad button is pressed
     *
     * @param fn
     * @status TOREVIEW
     */
    @PhonkMethod
    public void onKeyDown(ReturnInterface fn) {
        keyInit();
        mOnKeyDownfn = fn;
    }

    /**
     * Tells which software / hardware / gamepad button is released
     *
     * @param fn
     * @status TOREVIEW
     */
    @PhonkMethod
    public void onKeyUp(ReturnInterface fn) {
        keyInit();
        mOnKeyUpfn = fn;
    }

    /**
     * Gives a key event when of a software / hardware / gamepad button
     *
     * @param fn
     * @status TOREVIEW
     */
    @PhonkMethod
    public void onKeyEvent(ReturnInterface fn) {
        keyInit();
        mOnKeyEventfn = fn;
    }


    /**
     * Ignores the volume key functionally
     *
     * @param b
     * @status TODO
     */
    @PhonkMethod
    public void ignoreVolumeKeys(boolean b) {
        getActivity().ignoreVolumeEnabled = b;
    }

    /**
     * Ignores the back key
     *
     * @param b
     * @status TODO
     */
    @PhonkMethod
    public void ignoreBackKey(boolean b) {
        getActivity().ignoreBackEnabled = b;
    }

    /**
     * Makes the device vibrate
     *
     * @param duration Duration in milliseconds
     * @status TODO_EXAMPLE
     */
    @PhonkMethod
    public void vibrate(int duration) {
        Vibrator v = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
        v.vibrate(duration);
    }

    /**
     * Makes the device vibrate
     *
     * [100, 200, 300, 100, 0, 100]
     *
     * @param pattern Duration pattern in milliseconds
     * @param repeat Number of times that the pattern will repeat
     *
     * @status TODO_EXAMPLE
     */
     @PhonkMethod public void vibrate(long[] pattern, int repeat) {
         Vibrator v = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
         v.vibrate(pattern, repeat);
    }


    /**
     * Sends a SMS to a given phone number.
     * Please, be careful, since this method might cost you MONEY depending on your phone plan!
     *
     * @param number Phone number
     * @param msg    Text message
     * @status TODO_EXAMPLE
     */
    @PhonkMethod
    public void smsSend(String number, String msg) {
        SmsManager sm = SmsManager.getDefault();
        sm.sendTextMessage(number, null, msg, null, null);
    }

    /**
     * This method will be triggered when an SMS is received
     *
     * @param callback
     * @status TODO_EXAMPLE
     */
    @PhonkMethod
    public void onSmsReceived(final ReturnInterface callback) {

        // SMS receive
        IntentFilter intentFilter = new IntentFilter("SmsMessage.intent.MAIN");
        smsReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                String msg = intent.getStringExtra("get_msg");

                // Process the sms format and extract body and phone number
                msg = msg.replace("\n", "");
                String body = msg.substring(msg.lastIndexOf(":") + 1);
                String pNumber = msg.substring(0, msg.lastIndexOf(":"));

                ReturnObject ret = new ReturnObject();
                ret.put("from", pNumber);
                ret.put("message", "body");

                callback.event(ret);
            }
        };
        getContext().registerReceiver(smsReceiver, intentFilter);
    }

    /**
     * Get the current brightness
     *
     * @return brightness value
     * @status TOREVIEW
     */
    @PhonkMethod
    public float brightness() {
        int brightness = -1;

        try {
            brightness = Settings.System.getInt(getContext().getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }

        return brightness;
    }

    /**
     * Set brightness
     *
     * @param value
     * @status TOREVIEW
     */
    @PhonkMethod
    public void brightness(float value) {
        getActivity().setBrightness(value);
    }

    /**
     * Set the global brightness
     *
     * @param value from 0 to 255
     * @status TOREVIEW
     */
    @PhonkMethod
    public void globalBrightness(int value) {
        AndroidUtils.setGlobalBrightness(getContext(), value);
    }

    /**
     * Set the screen always on
     *
     * @param b
     * @status TOREVIEW
     */
    @PhonkMethod
    public void screenAlwaysOn(boolean b) {
        getActivity().setScreenAlwaysOn(b);
    }

    /**
     * Check if the screen is on
     *
     * @return status (true / false)
     * @status TOREVIEW
     */
    @PhonkMethod
    public boolean isScreenOn() {
        return AndroidUtils.isScreenOn(getContext());
    }

    /**
     * Sets the screen off
     *
     * @return
     * @status TODO
     */
    public boolean screenOff() {
        return AndroidUtils.isScreenOn(getContext());
    }

    //
    // @APIMethod(description = "", example = "")
    //public void goToSleep() {
    //	AndroidUtils.goToSleep(mContext);
    //}

    /**
     * Sets the screen timeout
     *
     * @param time in milliseconds
     * @status TOREVIEW
     */
    @PhonkMethod
    public void screenTimeout(int time) {
        AndroidUtils.setScreenTimeout(getContext(), time);
    }

    /**
     * Check if the device is in airplane cornerMode
     *
     * @return
     * @status TOREVIEW
     */
    @PhonkMethod
    public boolean isAirplaneMode() {
        return AndroidUtils.isAirplaneMode(getContext());
    }

    /**
     * Prevent the device suspend at any time. Good for long living operations
     *
     * @param b
     * @status TOREVIEW
     */
    @PhonkMethod
    public void wakeLock(boolean b) {
        AndroidUtils.setWakeLock(getContext(), b);
    }

    /**
     * Launch an intent
     *
     * @param intent
     * @status TOREVIEW
     */
    @PhonkMethod
    public void launchIntent(String intent) {
        Intent market_intent = new Intent(intent);
        getContext().startActivity(market_intent);
    }

    /**
     * Creates a new mail using the default e-mail app.
     * Note: It won't be automatically send
     *
     * @param recipient
     * @param subject
     * @param msg
     * @status OK
     */
    @PhonkMethod
    public void openEmailApp(String recipient, String subject, String msg) {
        Intents.sendEmail(getContext(), recipient, subject, msg);
    }

    /**
     * Open the default Map app
     *
     * @param longitude
     * @param latitude
     * @status OK
     */
    @PhonkMethod
    public void openMapApp(double longitude, double latitude) {
        Intents.openMap(getContext(), longitude, latitude);
    }

    /**
     * Open the system's phone dial screen
     *
     * @status OK
     */
    @PhonkMethod
    public void openDial() {
        Intents.openDial(getContext());
    }

    /**
     * Call a given phone number using the device's call manager
     *
     * @param number
     * @status OK
     */
    @PhonkMethod
    public void call(String number) {
        Intents.call(getActivity(), number);
    }

    /**
     * Open the default web browser with a given Url
     *
     * @param url
     * @status OK
     */
    @PhonkMethod
    public void openWebApp(String url) {
        Intents.openWeb(getActivity(), url);
    }

    /**
     * Open the search app with the given text
     *
     * @param text
     * @status OK
     */
    @PhonkMethod
    public void openWebSearch(String text) {
        Intents.webSearch(getActivity(), text);
    }


    /**
     * Opens a file with a given app provided as package name
     *
     * @param src
     * @param packageName
     * @status TODO
     */
    public void runApp(final String src, String packageName) {
        final String projectPath = null; //ProjectManager.getInstance().getCurrentProject().getStoragePath();

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.parse("file://" + projectPath + "/" + src), packageName);
        //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        getContext().startActivity(intent);
    }


    /**
     * Copy a given text into the clipboard.
     *
     * @param label
     * @param text
     * @status TOREVIEW
     */
    @PhonkMethod
    public void copyToClipboard(String label, String text) {
        ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
        clipboard.setPrimaryClip(ClipData.newPlainText(label, text));
    }

    /**
     * Get the content from the clipboard
     *
     * @param label
     * @param text
     * @return
     * @status TOREVIEW
     */
    @PhonkMethod
    public String getFromClipboard(String label, String text) {
        ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
        return clipboard.getPrimaryClip().getItemAt(clipboard.getPrimaryClip().getItemCount()).getText().toString();
    }

    /**
     * Gets a callback each time there is a change in the battery status
     *
     * @param callback
     * @status TODO_EXAMPLE
     */
    @PhonkMethod
    public void battery(final ReturnInterface callback) {
        batteryReceiver = new BroadcastReceiver() {
            int scale = -1;
            int level = -1;
            int voltage = -1;
            int temp = -1;
            boolean isConnected = false;
            private int status;
            private final boolean alreadyKilled = false;

            @Override
            public void onReceive(Context context, Intent intent) {
                level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
                scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
                temp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
                voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
                // isCharging =
                // intent.getBooleanExtra(BatteryManager.EXTRA_PLUGGED, false);
                // status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
                status = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);

                if (status == BatteryManager.BATTERY_PLUGGED_AC) {
                    isConnected = true;
                } else isConnected = status == BatteryManager.BATTERY_PLUGGED_USB;

                ReturnObject o = new ReturnObject();

                o.put("level", level);
                o.put("temperature", temp);
                o.put("connected", isConnected);
                o.put("scale", scale);
                o.put("temperature", temp);
                o.put("voltage", voltage);

                callback.event(o);
            }
        };

        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        getContext().registerReceiver(batteryReceiver, filter);
    }

    /**
     * Get the current device battery level
     *
     * @return
     * @status OK
     */
    @PhonkMethod
    public float battery() {
        Intent batteryIntent = getContext().registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);

        // Error checking that probably isn't needed but I added just in case.
        if (level == -1 || scale == -1) {
            return 50.0f;
        }

        return ((float) level / (float) scale) * 100.0f;
    }

    /**
     * Gets the current device orientation"
     *
     * @return
     * @status TOREVIEW
     */
    @PhonkMethod
    public String orientation() {
        int orientation = getContext().getResources().getConfiguration().orientation;
        String orientationStr = "";

        switch (orientation) {
            case 1:
                orientationStr = "portrait";
                break;
            case 2:
                orientationStr = "landscape";
                break;
            default:
                orientationStr = "unknown";
        }

        return orientationStr;
    }


    /**
     * Get some device information
     *
     * @return
     * @status TOREVIEW
     */
    @PhonkMethod
    public ReturnObject info() {
        ReturnObject ret = new ReturnObject();

        // density dpi
        DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
        ret.put("screenDpi", metrics.densityDpi);
        ret.put("screenWidth", metrics.widthPixels);
        ret.put("screenHeight", metrics.heightPixels);

        // id
        ret.put("androidId", Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID));

        // imei
        // deviceInfo.imei = ((TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
        ret.put("manufacturer", Build.MANUFACTURER);
        ret.put("model", Build.MODEL);
        ret.put("display", Build.DISPLAY);
        ret.put("versionRelease", Build.VERSION.RELEASE);

        String type = "";
        if (AndroidUtils.isTablet(getContext())) type = "tablet";
        else type = "phone";
        ret.put("type", type);

        String os = "";
        if (AndroidUtils.isVersionMarshmallow()) {
            os = Build.VERSION.BASE_OS;
        }
        ret.put("os", os);

        ret.put("board", Build.BOARD);
        ret.put("brand", Build.BRAND);
        ret.put("device", Build.DEVICE);
        ret.put("fingerPrint", Build.FINGERPRINT);
        ret.put("host", Build.HOST);
        ret.put("id", Build.ID);
        ret.put("keyboardPresent", getContext().getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS);

        ret.put("totalMem", Runtime.getRuntime().totalMemory());
        ret.put("usedMem", Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
        ret.put("maxMem", Runtime.getRuntime().maxMemory());

        PackageManager pm = getContext().getPackageManager();
        ret.put("backCamera", pm.hasSystemFeature(PackageManager.FEATURE_CAMERA));
        ret.put("frontCamera", pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
        ret.put("cameraFlash", pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH));
        ret.put("bluetooth", pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH));
        ret.put("bluetoothLE", pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE));
        ret.put("microphone", pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE));
        ret.put("wifi", pm.hasSystemFeature(PackageManager.FEATURE_WIFI));
        ret.put("telephony", pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
        ret.put("microphone", pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE));
        ret.put("nfc", pm.hasSystemFeature(PackageManager.FEATURE_NFC));
        ret.put("usbHost", pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST));
        ret.put("usbAccesory", pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY));
        ret.put("webview", pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW));
        ret.put("wifi", pm.hasSystemFeature(PackageManager.FEATURE_WIFI));
        ret.put("wifiDirect", pm.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT));
        ret.put("touchscreen", pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN));
        ret.put("multitouch", pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH));

        return ret;
    }


    public boolean areNotificationsEnabled() {
        return NotificationManagerCompat.from(getContext()).areNotificationsEnabled();
    }

    private void showNotificationsManager() {
        if (AndroidUtils.isVersionMarshmallow()) {
            getActivity().startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
        } else {
            getActivity().startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
        }

    }

    private boolean isNotificationServiceRunning() {
        ContentResolver contentResolver = getContext().getContentResolver();
        String enabledNotificationListeners = Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
        String packageName = getContext().getPackageName();
        return enabledNotificationListeners != null && enabledNotificationListeners.contains(packageName);
    }

    /**
     * Gets notifications from other apps.
     * In order to work to must enable the access of notifications in your device settings
     *
     * @param callback
     * @status TOREVIEW
     */
    @PhonkMethod
    public void onNewNotification(final ReturnInterface callback) {
        if (!isNotificationServiceRunning()) {
            showNotificationsManager();
        }

        onNotification = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                ReturnObject ret = new ReturnObject();

                ret.put("package", intent.getStringExtra("package"));
                ret.put("title", intent.getStringExtra("title"));
                ret.put("text", intent.getStringExtra("text"));

                callback.event(ret);
            }
        };

        LocalBroadcastManager.getInstance(getContext()).registerReceiver(onNotification, new IntentFilter("Msg"));
    }


    /**
     * Loads the list of installed applications in mApplications.
     *
     * @return
     * @status TOREVIEW
     */
    @PhonkMethod
    public List listInstalledApps() {
        ArrayList<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();

        // get installed apps
        PackageManager pm = getContext().getPackageManager();
        Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
        final List<ResolveInfo> apps = pm.queryIntentActivities(mainIntent, 0);
        Collections.sort(apps, new ResolveInfo.DisplayNameComparator(pm));

        if (apps != null) {
            int count = apps.size();

            for (int i = 0; i < count; i++) {
                ApplicationInfo application = new ApplicationInfo();
                ResolveInfo info = apps.get(i);

                application.title = info.loadLabel(pm);
                application.packageName = info.activityInfo.packageName;
                application.setActivity(new ComponentName(info.activityInfo.applicationInfo.packageName,
                                info.activityInfo.name),
                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                application.iconDrawable = info.activityInfo.loadIcon(pm);
                application.permission = info.activityInfo.permission;

                MLog.d(TAG, application.title + " " + application.packageName); // " " + application.iconURL);

                application.iconBitmap = ((BitmapDrawable) application.iconDrawable).getBitmap();

                // Bitmap icon =
                // BitmapFactory.decodeResource(this.getResources(),
                // application.icon);


                /*
                save icon in path
                 */
                /*
                String path = Environment.getExternalStorageDirectory().toString();
                application.iconURL = path + "/" + application.packageName + ".png";

                try {
                    FileOutputStream out = new FileOutputStream(application.iconURL);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
                } catch (Exception e) {
                    e.printStackTrace();
                }*/

                mApplications.add(application);
            }
        }

        return mApplications;
    }

    @Override
    public void __stop() {
        getContext().unregisterReceiver(batteryReceiver);
        getContext().unregisterReceiver(onNotification);
        getContext().unregisterReceiver(smsReceiver);

        batteryReceiver = null;
        onNotification = null;
    }

}