<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/xposed/MacroExpandApplication.java<|end_filename|>
package net.mitchtech.xposed;

import android.app.Application;

import com.tsengvn.typekit.Typekit;

public class MacroExpandApplication extends Application {

    private static String TAG = MacroExpandApplication.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();
        Typekit.getInstance()
                .addNormal(Typekit.createFromAsset(this, "Roboto-Light.ttf"))
                .addBold(Typekit.createFromAsset(this, "Roboto-Regular.ttf"))
                .addItalic(Typekit.createFromAsset(this, "Roboto-LightItalic.ttf"))
                .addBoldItalic(Typekit.createFromAsset(this, "Roboto-BoldItalic.ttf"));
    }
}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/utils/DateTimeUtils.java<|end_filename|>
package net.mitchtech.utils;

import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

public class DateTimeUtils {

    private static final String TAG = AppUtils.class.getSimpleName();

    public static String getCurrentTime() {
        Calendar now = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss a", Locale.getDefault());
        String time = simpleDateFormat.format(now.getTime());
        Log.i(TAG, time);
        return time;
    }

    public static String getDate() {
        Calendar now = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/dd/yyyy", Locale.getDefault());
        String date = simpleDateFormat.format(now.getTime());
        Log.i(TAG, date);
        return date;
    }

    public static String getDayOfWeek() {
        Calendar now = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEE", Locale.getDefault());
        String day = simpleDateFormat.format(now.getTime());
        Log.i(TAG, day);
        return day;
    }
}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/utils/PowerUtils.java<|end_filename|>
package net.mitchtech.utils;

import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;

public class PowerUtils {

    private static final String TAG = PowerUtils.class.getSimpleName();

    public static float getBatteryLevel(Context context) {
        Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        return ((float) level / (float) scale) * 100.0f;
    }

    public static int getBatteryState(Context context) {
        if (isBatteryCharging(context)) {
            return 1;
        } else {
            return 0;
        }
    }

    public static boolean isBatteryCharging(Context context) {
        Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
        boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
        boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
        boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                status == BatteryManager.BATTERY_STATUS_FULL;
        return isCharging;
    }
}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/xposed/BaseActivity.java<|end_filename|>
package net.mitchtech.xposed;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.View;
import android.webkit.WebView;
import android.widget.AdapterView;

import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.iconics.typeface.FontAwesome;
import com.mikepenz.materialdrawer.Drawer;
import com.mikepenz.materialdrawer.DrawerBuilder;
import com.mikepenz.materialdrawer.model.DividerDrawerItem;
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem;
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
import com.tsengvn.typekit.TypekitContextWrapper;

import net.mitchtech.xposed.macroexpand.R;

/**
 * Created by michael on 7/15/15.
 */
public class BaseActivity extends AppCompatActivity {

    private static final String TAG = BaseActivity.class.getSimpleName();
    protected static final String PKG_NAME = "net.mitchtech.xposed.macroexpand";

    public static final int MENU_ABOUT = Menu.FIRST + 1;
    public static final int MENU_HELP = Menu.FIRST + 2;
    public static final int MENU_SETTINGS = Menu.FIRST + 3;
    public static final int MENU_THEME = Menu.FIRST + 4;
    public static final int MENU_UPGRADE = Menu.FIRST + 5;
    public static final int MENU_DONATE = Menu.FIRST + 6;
    public static final int MENU_SHARE = Menu.FIRST + 7;
    public static final int MENU_RATE = Menu.FIRST + 8;
    public static final int MENU_MORE_APPS = Menu.FIRST + 9;
    public static final int MENU_SUPPORT_URL = Menu.FIRST + 10;
    public static final int MENU_CONTACT = Menu.FIRST + 11;
    public static final int MENU_SOURCE_CODE = Menu.FIRST + 12;
    public static final int MENU_LICENSE = Menu.FIRST + 13;
    public static final int MENU_PRIVACY = Menu.FIRST + 14;
    public static final int MENU_CHANGE_LOG = Menu.FIRST + 15;
    public static final int MENU_EXIT = Menu.FIRST + 16;
    public static final int MENU_DYNAMIC_MACROS = Menu.FIRST + 20;

    public static final String MENU_TEXT_ABOUT = "About";
    public static final String MENU_TEXT_HELP = "Help";
    public static final String MENU_TEXT_SETTINGS = "Settings";
    public static final String MENU_TEXT_THEME = "Theme";
    public static final String MENU_TEXT_UPGRADE = "Upgrade";
    public static final String MENU_TEXT_DONATE = "Donate";
    public static final String MENU_TEXT_SHARE = "Share App";
    public static final String MENU_TEXT_RATE = "Rate App";
    public static final String MENU_TEXT_MORE_APPS = "More Apps";
    public static final String MENU_TEXT_SUPPORT_URL = "Support URL";
    public static final String MENU_TEXT_CONTACT = "Contact Developer";
    public static final String MENU_TEXT_SOURCE_CODE = "Source Code";
    public static final String MENU_TEXT_LICENSE = "Software License";
    public static final String MENU_TEXT_PRIVACY = "Privacy Policy";
    public static final String MENU_TEXT_CHANGE_LOG = "Change Log";
    public static final String MENU_TEXT_EXIT = "Exit";
    public static final String MENU_TEXT_DYNAMIC_MACROS = "Dynamic Macros";

    protected SharedPreferences mPrefs;
    protected static Drawer mDrawer = null;
    protected static Toolbar mToolbar = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_base);
        mPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(TypekitContextWrapper.wrap(newBase));
    }

    protected void initToolbar() {
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
    }

    protected void initDrawer() {
        mDrawer = new DrawerBuilder()
                .withActivity(this)
                .withToolbar(mToolbar)
                .withTranslucentActionBarCompatibility(true)
                .withCloseOnClick(true)
                .withAnimateDrawerItems(true)
                .withSelectedItem(-1)
                .addDrawerItems(
                        new PrimaryDrawerItem().withName(MENU_TEXT_DYNAMIC_MACROS).withIcon(FontAwesome.Icon.faw_android).withIdentifier(MENU_DYNAMIC_MACROS),
                        new PrimaryDrawerItem().withName(MENU_TEXT_SETTINGS).withIcon(FontAwesome.Icon.faw_gear).withIdentifier(MENU_SETTINGS),
                        new PrimaryDrawerItem().withName(MENU_TEXT_ABOUT).withIcon(FontAwesome.Icon.faw_info).withIdentifier(MENU_ABOUT),
                        new PrimaryDrawerItem().withName(MENU_TEXT_HELP).withIcon(FontAwesome.Icon.faw_question).withIdentifier(MENU_HELP),
                        new DividerDrawerItem(),
                        new SecondaryDrawerItem().withName(MENU_TEXT_THEME).withIcon(FontAwesome.Icon.faw_paint_brush).withIdentifier(MENU_THEME),
                        new SecondaryDrawerItem().withName(MENU_TEXT_UPGRADE).withIcon(FontAwesome.Icon.faw_shopping_cart).withIdentifier(MENU_UPGRADE),
                        new SecondaryDrawerItem().withName(MENU_TEXT_DONATE).withIcon(FontAwesome.Icon.faw_dollar).withIdentifier(MENU_DONATE),
                        new SecondaryDrawerItem().withName(MENU_TEXT_SHARE).withIcon(FontAwesome.Icon.faw_share).withIdentifier(MENU_SHARE),
                        new SecondaryDrawerItem().withName(MENU_TEXT_RATE).withIcon(FontAwesome.Icon.faw_star).withIdentifier(MENU_RATE),
                        new SecondaryDrawerItem().withName(MENU_TEXT_MORE_APPS).withIcon(FontAwesome.Icon.faw_image).withIdentifier(MENU_MORE_APPS),
                        new SecondaryDrawerItem().withName(MENU_TEXT_SUPPORT_URL).withIcon(FontAwesome.Icon.faw_bookmark).withIdentifier(MENU_SUPPORT_URL),
                        new SecondaryDrawerItem().withName(MENU_TEXT_CONTACT).withIcon(FontAwesome.Icon.faw_bullhorn).withIdentifier(MENU_CONTACT),
                        new SecondaryDrawerItem().withName(MENU_TEXT_SOURCE_CODE).withIcon(FontAwesome.Icon.faw_github).withIdentifier(MENU_SOURCE_CODE),
                        new SecondaryDrawerItem().withName(MENU_TEXT_LICENSE).withIcon(FontAwesome.Icon.faw_copyright).withIdentifier(MENU_LICENSE),
                        new SecondaryDrawerItem().withName(MENU_TEXT_PRIVACY).withIcon(FontAwesome.Icon.faw_video_camera).withIdentifier(MENU_PRIVACY),
                        new SecondaryDrawerItem().withName(MENU_TEXT_CHANGE_LOG).withIcon(FontAwesome.Icon.faw_list).withIdentifier(MENU_CHANGE_LOG),
                        new SecondaryDrawerItem().withName(MENU_TEXT_EXIT).withIcon(FontAwesome.Icon.faw_power_off).withIdentifier(MENU_EXIT)
                )
                .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                    @Override
                    public boolean onItemClick(AdapterView<?> parent, View view, int position, long id, IDrawerItem drawerItem) {
                        if (drawerItem != null) {
                            performAction(drawerItem.getIdentifier());
                            return true;
                        }
                        return false;
                    }
                })
                .withActionBarDrawerToggle(true)
                .withShowDrawerOnFirstLaunch(true)
                .build();
    }

    public void performAction(int itemId) {
        // http://stackoverflow.com/questions/9092712/switch-case-statement-error-case-expressions-must-be-constant-expression
        Intent intent = null;
        if (itemId == MENU_ABOUT) {
        } else if (itemId == MENU_HELP) {
        } else if (itemId == MENU_DYNAMIC_MACROS) {
            intent = new Intent(this, DynamicMacroPreferenceActivity.class);
        } else if (itemId == MENU_SETTINGS) {
            intent = new Intent(this, MacroPreferenceActivity.class);
        } else if (itemId == MENU_THEME) {
        } else if (itemId == MENU_UPGRADE) {
        } else if (itemId == MENU_DONATE) {
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_paypal)));
        } else if (itemId == MENU_SHARE) {
            intent = new Intent(Intent.ACTION_SEND);
            intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.url_github));
            intent.setType("text/plain");
            intent = Intent.createChooser(intent, "Share");
        } else if (itemId == MENU_RATE) {
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.market_uri_app)));
            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
        } else if (itemId == MENU_MORE_APPS) {
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.market_uri_mitchtech)));
        } else if (itemId == MENU_SUPPORT_URL) {
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_xda)));
        } else if (itemId == MENU_CONTACT) {
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.mailto)));
        } else if (itemId == MENU_SOURCE_CODE) {
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_github)));
        } else if (itemId == MENU_LICENSE) {
        } else if (itemId == MENU_PRIVACY) {
        } else if (itemId == MENU_CHANGE_LOG) {
            showChangeLogDialog();
        } else if (itemId == MENU_EXIT) {
            this.finish();
        }

//        mDrawer.resetDrawerContent();
//        mDrawer.closeDrawer();
//        mDrawer.setSelection(-1);

        if (intent != null) {
            try {
                startActivity(intent);
            } catch (ActivityNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    protected void showChangeLogDialog() {
        WebView webView = new WebView(this);
        webView.loadUrl("file:///android_asset/changelog.html");
        new MaterialDialog.Builder(this)
                .title("Change Log")
                .customView(webView, false)
                .cancelable(false)
                .positiveText("OK")
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        super.onPositive(dialog);
                    }
                }).show();
    }

}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/utils/ProcUtils.java<|end_filename|>
package net.mitchtech.utils;

import android.util.Log;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class ProcUtils {

    private static final String TAG = ProcUtils.class.getSimpleName();

    public static String getVersion() {
        return readFile("proc/version");
    }

    public static String getUptime() {
        return readFile("proc/uptime");
    }

    public static String getMemInfo() {
        return readFile("proc/meminfo");
    }

    public static String getCpuInfo() {
        return readFile("proc/cpuinfo");
    }

    public static String getCmdLine() {
        return readFile("proc/cmdline");
    }

    public static long getCpuTime() {
        String cpustat = readFile("proc/stat");
        if (cpustat == null) {
            return 0;
        }
        String[] segs = cpustat.split("[ ]+");
        return Long.parseLong(segs[1]) + Long.parseLong(segs[2])
                + Long.parseLong(segs[3]) + Long.parseLong(segs[4]);
    }

    private static String readFile(String filename) {
        FileReader fstream;
        try {
            fstream = new FileReader(filename);
        } catch (FileNotFoundException e) {
            Log.i(TAG, "Read error on " + filename);
            return "?";
        }

        BufferedReader in = new BufferedReader(fstream, 500);
        try {
            return in.readLine();
        } catch (IOException e) {
            Log.i(TAG, "Read error on " + filename);
            return "?";
        }
    }

}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/utils/MacroUtils.java<|end_filename|>
package net.mitchtech.utils;

import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import net.mitchtech.xposed.MacroEntry;

import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.List;

public class MacroUtils {
    
    private static final String TAG = MacroUtils.class.getSimpleName();
    public static final String DYNAMIC_MACRO_WILDCARD = "@";

    // Date and time dynamic macros
    public static final int MACRO_DATE = 0;
    public static final int MACRO_TIME = 1;
    public static final int MACRO_WEEKDAY = 2;
    public static String MACRO_DATE_TEXT = "@date";
    public static String MACRO_TIME_TEXT = "@time";
    public static String MACRO_WEEKDAY_TEXT = "@day";

    // Network dynamic macros
    public static final int MACRO_MAC_ADDRESS = 10;
    public static final int MACRO_LAN_IP_ADDRESS = 11;
    public static final int MACRO_WAN_IP_ADDRESS = 12;
    public static final int MACRO_SSID = 13;
    public static String MACRO_MAC_ADDRESS_TEXT = "@macaddr";
    public static String MACRO_LAN_IP_ADDRESS_TEXT = "@lanip";
    public static String MACRO_WAN_IP_ADDRESS_TEXT = "@wanip";
    public static String MACRO_SSID_TEXT = "@ssid";

    // Battery and power dynamic macros
    public static final int MACRO_BATTERY_LEVEL = 20;
    public static final int MACRO_BATTERY_CHARGING = 21;
    public static String MACRO_BATTERY_LEVEL_TEXT = "@batt";
    public static String MACRO_BATTERY_CHARGING_TEXT = "@plugged";

    // Storage dynamic macros
    public static final int MACRO_INTERNAL_MB_FREE = 30;
    public static final int MACRO_EXTERNAL_MB_FREE = 31;
    public static String MACRO_INTERNAL_MB_FREE_TEXT = "@imbfree";
    public static String MACRO_EXTERNAL_MB_FREE_TEXT = "@embfree";


    public static ArrayList<MacroEntry> jsonToMacroArrayList(String json) {
        ArrayList<MacroEntry> macroList = new ArrayList<MacroEntry>();
        Type type = new TypeToken<List<MacroEntry>>() { }.getType();
        macroList = new Gson().fromJson(json, type);
        return macroList;
    }
    
    public static String macroArrayListToJson(ArrayList<MacroEntry> macroList) {
        return new Gson().toJson(macroList);
    }
    
    public static void saveMacroList(ArrayList<MacroEntry> macroList, SharedPreferences prefs) {
        Editor prefsEditor = prefs.edit();
        prefsEditor.putString("json", macroArrayListToJson(macroList));
        prefsEditor.commit();
    }
    
    public static ArrayList<MacroEntry> loadMacroList(SharedPreferences prefs) {
      String json = prefs.getString("json", "");
      Type type = new TypeToken<List<MacroEntry>>() { }.getType();
      ArrayList<MacroEntry> macroList = new Gson().fromJson(json, type);
      if (macroList == null) {
          macroList = new ArrayList<MacroEntry>();
      }
      return macroList;
    }

    public static ArrayList<MacroEntry> loadDynamicMacroList(SharedPreferences prefs) {
        ArrayList<MacroEntry> macroList = new ArrayList<MacroEntry>();

        if (prefs.getBoolean("prefDynamicDate", false))
            macroList.add(new MacroEntry(prefs.getString("prefDynamicDateKeyword", MACRO_DATE_TEXT), "" + MACRO_DATE));
        if (prefs.getBoolean("prefDynamicTime", false))
            macroList.add(new MacroEntry(prefs.getString("prefDynamicTimeKeyword", MACRO_TIME_TEXT), "" + MACRO_TIME));
        if (prefs.getBoolean("prefDynamicWeekday", false))
            macroList.add(new MacroEntry(prefs.getString("prefDynamicWeekdayKeyword", MACRO_WEEKDAY_TEXT), "" + MACRO_WEEKDAY));

        if (prefs.getBoolean(MACRO_MAC_ADDRESS_TEXT, true))
            macroList.add(new MacroEntry(MACRO_MAC_ADDRESS_TEXT, "" + MACRO_MAC_ADDRESS));
        if (prefs.getBoolean(MACRO_LAN_IP_ADDRESS_TEXT, true))
            macroList.add(new MacroEntry(MACRO_LAN_IP_ADDRESS_TEXT, "" + MACRO_LAN_IP_ADDRESS));
        if (prefs.getBoolean(MACRO_WAN_IP_ADDRESS_TEXT, true))
            macroList.add(new MacroEntry(MACRO_WAN_IP_ADDRESS_TEXT, "" + MACRO_WAN_IP_ADDRESS));
        if (prefs.getBoolean(MACRO_SSID_TEXT, true))
            macroList.add(new MacroEntry(MACRO_SSID_TEXT, "" + MACRO_SSID));

        if (prefs.getBoolean("prefDynamicBatteryLevel", false))
            macroList.add(new MacroEntry(prefs.getString("prefDynamicBatteryLevelKeyword", MACRO_BATTERY_LEVEL_TEXT), "" + MACRO_BATTERY_LEVEL));
        if (prefs.getBoolean("prefDynamicBatteryState", false))
            macroList.add(new MacroEntry(prefs.getString("prefDynamicBatteryStateKeyword", MACRO_BATTERY_CHARGING_TEXT), "" + MACRO_BATTERY_CHARGING));

        if (prefs.getBoolean("prefDynamicIntStorage", true))
            macroList.add(new MacroEntry(prefs.getString("prefDynamicIntStorageKeyword", MACRO_INTERNAL_MB_FREE_TEXT), "" + MACRO_INTERNAL_MB_FREE));
        if (prefs.getBoolean("prefDynamicExtStorage", true))
            macroList.add(new MacroEntry(prefs.getString("prefDynamicExtStorageStorage", MACRO_EXTERNAL_MB_FREE_TEXT), "" + MACRO_EXTERNAL_MB_FREE));
        return macroList;
    }
    
    // or "ISO-8859-1" for ISO Latin 1
    public static CharsetEncoder asciiEncoder = Charset.forName("US-ASCII").newEncoder();

    public static boolean isPureAscii(String string) {
        return asciiEncoder.canEncode(string);
    }
          
    // check for regex in text ($, ^, +, *, ., !, ?, |, \, (), {}, [])
    public static boolean isTextRegexFree(String text) {
        if (text.contains("$") || text.contains("^") || text.contains("+") || text.contains("*")
                || text.contains(".") || text.contains("!") || text.contains("?")
                || text.contains("$") || text.contains("|") || text.contains("\\")
                || text.contains("(") || text.contains(")") || text.contains("{")
                || text.contains("}") || text.contains("[") || text.contains("]")) {
            return false;
        } else {
            return true;
        }
    }
    
}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/xposed/XposedMacroExpand.java<|end_filename|>

package net.mitchtech.xposed;

import android.content.Context;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.widget.EditText;
import android.widget.MultiAutoCompleteTextView;
import android.widget.TextView;

import net.mitchtech.utils.DateTimeUtils;
import net.mitchtech.utils.MacroUtils;
import net.mitchtech.utils.NetworkUtils;
import net.mitchtech.utils.PowerUtils;
import net.mitchtech.utils.StorageUtils;

import java.util.ArrayList;
import java.util.regex.Pattern;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

import static de.robv.android.xposed.XposedHelpers.findAndHookConstructor;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

public class XposedMacroExpand implements IXposedHookLoadPackage, IXposedHookZygoteInit {

    private static final String TAG = XposedMacroExpand.class.getSimpleName();
    private static final String PKG_NAME = "net.mitchtech.xposed.macroexpand";

    private XSharedPreferences prefs;

    private ArrayList<MacroEntry> mMacroList;
    private ArrayList<MacroEntry> mDynamicMacroList;
    private Context mContext;

    @Override
    public void initZygote(StartupParam startupParam) throws Throwable {
        loadPrefs();
    }

    @Override
    public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
        
        // don't replace text in this package, otherwise can't edit macros
        if (lpparam.packageName.equals(PKG_NAME)) {
            return;
        }

        // public void setText(CharSequence text, BufferType type)
        findAndHookMethod(EditText.class, "setText", CharSequence.class, TextView.BufferType.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam methodHookParam)
                            throws Throwable {
                        CharSequence actualText = (CharSequence) methodHookParam.args[0];
                        if (actualText != null) {
                            // XposedBridge.log(TAG + ": setText(): " + actualText);
                            String replacementText = replaceText(actualText.toString());
                            methodHookParam.args[0] = replacementText;
                        } 
                    }
            });
        
        // common hook method for all edittext constructors
        XC_MethodHook editTextMethodHook = new XC_MethodHook() {

            @Override
            protected void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                final EditText editText = (EditText) methodHookParam.thisObject;
                mContext = (Context) methodHookParam.args[0];
//                if (editText instanceof MultiAutoCompleteTextView) {
//                    // XposedBridge.log(TAG + ": MultiAutoCompleteTextView");
//                    return;
//                }
                editText.setOnFocusChangeListener(new OnFocusChangeListener() {

                    @Override
                    public void onFocusChange(View view, boolean hasFocus) {
                        if (isEnabled("prefIgnorePassword") && editText.getInputType() 
                                == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)) {
                         // XposedBridge.log(TAG + ": password box & ignore password enabled");
                        } else {
                            if (!hasFocus) {
                                String actualText = editText.getText().toString();
                                // XposedBridge.log(TAG + ": onFocusChange(): " + actualText);
                                String replacementText = replaceText(actualText);
                                // prevent stack overflow, only set text if modified
                                if (!actualText.equals(replacementText)) {
                                    editText.setText(replacementText);
                                    editText.setSelection(editText.getText().length());
                                }
                            }
                        }
                    }
                });

                editText.addTextChangedListener(new TextWatcher() {

                    @Override
                    public void onTextChanged(CharSequence text, int start, int before, int count) {
                    }

                    @Override
                    public void beforeTextChanged(CharSequence text, int start, int count, int after) {
                    }

                    @Override
                    public void afterTextChanged(Editable editable) {
                        if (isEnabled("prefIgnorePassword") && editText.getInputType()
                                == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)) {
                            // XposedBridge.log(TAG + ": password box & ignore password enabled");
                        } else {
                            String actualText = editable.toString();
                            // XposedBridge.log(TAG + ": afterTextChanged(): " + actualText);
                            String replacementText = replaceText(actualText);
                            // prevent stack overflow, only set text if modified
                            if (!actualText.equals(replacementText)) {
                                editText.setText(replacementText);
                                // dont move cursor if field is MultiAutoCompleteTextView
                                if (editText instanceof MultiAutoCompleteTextView) {
                                    // XposedBridge.log(TAG + ": MultiAutoCompleteTextView");
                                } else {
                                    if (isEnabled("prefMoveCursor")) {
                                        editText.setSelection(editText.getText().length());
                                    }
                                }
                            }
                        }
                    }
                });
            }
        };
        
        // public EditText(Context context) 
        findAndHookConstructor(EditText.class, Context.class, editTextMethodHook);
        
        // public EditText(Context context, AttributeSet attrs)
        findAndHookConstructor(EditText.class, Context.class, AttributeSet.class, editTextMethodHook);
        
        // public EditText(Context context, AttributeSet attrs, int defStyle)
        findAndHookConstructor(EditText.class, Context.class, AttributeSet.class, int.class, editTextMethodHook);        
    }

    private String replaceText(String actualText) {
        String replacementText = actualText;

        // process replacements in user assigned macro list
        if (mMacroList != null && !mMacroList.isEmpty()) {           
            for (MacroEntry macroEntry : mMacroList) {
                if (isEnabled("prefIgnoreCase")) {
                    // "(?i)" used for case insensitive replace
                    replacementText = replacementText.replaceAll(("(?i)" + Pattern.quote(macroEntry.actual)),
                            macroEntry.replacement);
                } else {
                    // case sensitive replacement
                    replacementText = replacementText.replaceAll(Pattern.quote(macroEntry.actual),
                            macroEntry.replacement);
                }
            }
        }

        // process replacements in dynamically generated macro list
        if (isEnabled("prefEnableDynamicMacros")) {
            if (mDynamicMacroList != null && !mDynamicMacroList.isEmpty()) {

                for (MacroEntry macroEntry : mDynamicMacroList) {

                    if (replacementText.contains(macroEntry.actual)) {

                        String dynamicText = "";
                        switch (Integer.parseInt(macroEntry.replacement)) {
                            case MacroUtils.MACRO_DATE:
                                dynamicText = DateTimeUtils.getDate();
                                break;

                            case MacroUtils.MACRO_TIME:
                                dynamicText = DateTimeUtils.getCurrentTime();
                                break;

                            case MacroUtils.MACRO_WEEKDAY:
                                dynamicText = DateTimeUtils.getDayOfWeek();
                                break;

                            case MacroUtils.MACRO_MAC_ADDRESS:
                                dynamicText = NetworkUtils.getMacAddress(mContext);
                                break;

                            case MacroUtils.MACRO_LAN_IP_ADDRESS:
                                dynamicText = NetworkUtils.getIpAddress(mContext);
                                break;

                            case MacroUtils.MACRO_WAN_IP_ADDRESS:
                                dynamicText = NetworkUtils.getPublicIpAddress();
                                break;

                            case MacroUtils.MACRO_SSID:
                                dynamicText = NetworkUtils.getWifiSsid(mContext);
                                break;

                            case MacroUtils.MACRO_BATTERY_LEVEL:
                                dynamicText = "" + PowerUtils.getBatteryLevel(mContext);
                                break;

                            case MacroUtils.MACRO_BATTERY_CHARGING:
                                dynamicText = "" + PowerUtils.getBatteryState(mContext);
                                break;

                            case MacroUtils.MACRO_INTERNAL_MB_FREE:
                                dynamicText = "" + StorageUtils.getInternalAvailableSpace();
                                break;

                            case MacroUtils.MACRO_EXTERNAL_MB_FREE:
                                dynamicText = "" + StorageUtils.getExternalAvailableSpace();
                                break;

                            default:
                                break;
                        }
                        replacementText = replacementText.replaceAll(Pattern.quote(macroEntry.actual),
                                dynamicText);
                    }
                }
            }
        }

        return replacementText;
    }

    private boolean isEnabled(String pkgName) {
        prefs.reload();
        return prefs.getBoolean(pkgName, true);
    }

    private void loadPrefs() {
        prefs = new XSharedPreferences(PKG_NAME);
        prefs.makeWorldReadable();
        mMacroList = MacroUtils.loadMacroList(prefs);
        mDynamicMacroList = MacroUtils.loadDynamicMacroList(prefs);
        XposedBridge.log(TAG + ": prefs loaded.");
//        String json = prefs.getString("json", "");
//        Type type = new TypeToken<List<MacroEntry>>() { }.getType();
//        mMacroList = new Gson().fromJson(json, type);
    }

}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/xposed/MacroPreferenceActivity.java<|end_filename|>

package net.mitchtech.xposed;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;

import com.afollestad.materialdialogs.MaterialDialog;
import com.ipaulpro.afilechooser.utils.FileUtils;

import net.mitchtech.utils.AppUtils;
import net.mitchtech.utils.MacroUtils;
import net.mitchtech.xposed.macroexpand.R;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

public class MacroPreferenceActivity extends BaseActivity {

    private static final String TAG = MacroPreferenceActivity.class.getSimpleName();
    private static final int FORMAT_AHK = 0;
    private static final int FORMAT_JSON = 1;

    private Preference mPrefImportMacros;
    private Preference mPrefExportMacros;
    private Preference mPrefAboutModule;
    private Preference mPrefAboutXposed;
    private Preference mPrefDonatePaypal;
    private Preference mPrefGithub;
    private Preference mPrefHelp;
    private Preference mPrefChangeLog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base);
//        getPreferenceManager().setSharedPreferencesMode(Context.MODE_WORLD_READABLE);
//        addPreferencesFromResource(R.xml.settings);
        initToolbar();
        initDrawer();

        getFragmentManager().beginTransaction().replace(R.id.content_frame, new SettingsFragment()).commit();
    }

    public class SettingsFragment extends PreferenceFragment implements
            SharedPreferences.OnSharedPreferenceChangeListener {

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // this is important since settings executed in the context of the hooked package
            getPreferenceManager().setSharedPreferencesMode(Context.MODE_WORLD_READABLE);
            addPreferencesFromResource(R.xml.settings);
//            getActionBar().setDisplayHomeAsUpEnabled(true);
//            mPrefs = getPreferenceScreen().getSharedPreferences();
            mPrefImportMacros = findPreference("prefImportMacros");
            mPrefExportMacros = findPreference("prefExportMacros");
            mPrefAboutModule = findPreference("prefAboutModule");
            mPrefAboutXposed = findPreference("prefAboutXposed");
            mPrefDonatePaypal = findPreference("prefDonatePaypal");
            mPrefGithub = findPreference("prefGithub");
            mPrefHelp = findPreference("prefHelp");
            mPrefChangeLog = findPreference("prefChangeLog");

            String version = AppUtils.getVersion(MacroPreferenceActivity.this);
            mPrefAboutModule.setTitle(MacroPreferenceActivity.this.getTitle() + version);
        }

        @Override
        public void onResume() {
            super.onResume();
            getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
        }

        @Override
        public void onPause() {
            super.onPause();
            getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
        }

        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

        }

        @Override
        public boolean onPreferenceTreeClick(PreferenceScreen prefScreen, Preference pref) {
            Intent intent = null;

            if (pref == mPrefImportMacros) {
                importFormatDialog();
            } else if (pref == mPrefExportMacros) {
                exportFormatDialog();
            } else if (pref == mPrefAboutModule) {
                intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_xda)));
            } else if (pref == mPrefAboutXposed) {
                intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_xposed)));
            } else if (pref == mPrefDonatePaypal) {
                intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_paypal)));
            } else if (pref == mPrefGithub) {
            } else if (pref == mPrefHelp) {
            } else if (pref == mPrefChangeLog) {
//                showChangeLogDialog();
            }

            if (intent != null) {
                try {
                    startActivity(intent);
                } catch (ActivityNotFoundException e) {
                    e.printStackTrace();
                }
                return true;
            }
            return super.onPreferenceTreeClick(prefScreen, pref);
        }
    }

    private void importFileChooser(int format) {
        Intent target = FileUtils.createGetContentIntent();
        // target.setType(FileUtils.MIME_TYPE_TEXT);
        Intent intent = Intent.createChooser(target, getString(R.string.chooser_title));
        try {
            startActivityForResult(intent, format);
        } catch (ActivityNotFoundException e) {
            // The reason for the existence of aFileChooser
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case FORMAT_AHK:
            case FORMAT_JSON:
                if (resultCode == RESULT_OK) {
                    if (data != null) {
                        final Uri uri = data.getData();
                        // Log.i(TAG, "Uri = " + uri.toString());
                        try {
                            final String path = FileUtils.getPath(this, uri);
                            // Log.i(TAG, "path = " + path);
                            importConfirmDialog(path, requestCode);
                        } catch (Exception e) {
                            Log.e(TAG, "File select error:", e);
                        }
                    }
                }
                break;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    private void importConfirmDialog(final String path, final int format) {
        new MaterialDialog.Builder(MacroPreferenceActivity.this)
                .title("Overwrite Macro List?")
//                .content("Do you want to overwrite your macro list or append imported entries? \n\nThis operation cannot be undone!")
                .content("Are you sure you want to overwrite your macro list with imported entries? \n\nThis operation cannot be undone!")
                .positiveText("Overwrite")
                .negativeText("Cancel")
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        // setProgressBarIndeterminateVisibility(true);
                        new ImportMacroListTask(format).execute(path);
                    }
                }).show();
    }

    private void importFormatDialog() {
        final CharSequence[] items = {"AutoHotKey", "JSON"};
        new MaterialDialog.Builder(MacroPreferenceActivity.this)
                .title("Select Import Format")
                .items(items)
                .itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallbackSingleChoice() {
                    @Override
                    public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
                        switch (which) {
                            case FORMAT_AHK:
                                importFileChooser(FORMAT_AHK);
                                break;
                            case FORMAT_JSON:
                                importFileChooser(FORMAT_JSON);
                                break;
                        }
                        return true;
                    }
                }).show();
    }

    private void importResultDialog(final String log) {
        new MaterialDialog.Builder(MacroPreferenceActivity.this)
                .title("Import Result")
                .content(log)
                .cancelable(false)
                .positiveText("OK")
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        AppUtils.reloadLauncherActivity(MacroPreferenceActivity.this);
                    }
                }).show();
    }

    private void exportFormatDialog() {
        final CharSequence[] items = {"AutoHotKey", "JSON"};
        new MaterialDialog.Builder(MacroPreferenceActivity.this)
                .title("Select Export Format")
                .items(items)
                .itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallbackSingleChoice() {
                    @Override
                    public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
                        setProgressBarIndeterminateVisibility(true);
                        switch (which) {
                            case FORMAT_AHK:
                                // exportMacros(FORMAT_AHK);
                                new ExportMacroListTask(FORMAT_AHK).execute();
                                break;
                            case FORMAT_JSON:
                                // exportMacros(FORMAT_JSON);
                                new ExportMacroListTask(FORMAT_JSON).execute();
                                break;
                        }
                        return true;
                    }
                }).show();
    }

    private void exportResultDialog(final String log) {
        new MaterialDialog.Builder(MacroPreferenceActivity.this)
                .title("Export Result")
                .content(log)
                .cancelable(false)
                .positiveText("OK")
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        super.onPositive(dialog);
                    }
                }).show();
    }

    class ImportMacroListTask extends AsyncTask<String, Void, String> {

        int mFormat;

        public ImportMacroListTask(int format) {
            mFormat = format;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            setProgressBarIndeterminateVisibility(true);
        }

        @Override
        protected String doInBackground(String... params) {
            StringBuilder json = new StringBuilder();
            StringBuilder log = new StringBuilder();
            String path = params[0];
            ArrayList<MacroEntry> macroList = new ArrayList<MacroEntry>();
            String line;

            try {
                BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
                while ((line = bufferedReader.readLine()) != null) {
                    if (mFormat == FORMAT_JSON) {
                        json.append(line); // json.append(line + "\n");
                        log.append("Import json: " + line + "\n");
                    } else if (mFormat == FORMAT_AHK) {
                        if (!MacroUtils.isPureAscii(line)) {
                            log.append("Skipping non-ascii line:: [" + line + "]\n");
                        } else if (line.startsWith("::")) {
                            String[] split = line.split("::");
                            if (split.length != 3) {
                                log.append("Invalid format. Skipping line: [" + line + "]\n");
                            } else {
                                String macro = split[1];
                                String replacement = split[2];
                                if (replacement.contains(";")) {
                                    String[] removeComment = replacement.split(";");
                                    replacement = removeComment[0].trim();
                                }
                                // MacroEntry macroEntry = new MacroEntry(split[1], split[2]);
                                MacroEntry macroEntry = new MacroEntry(macro, replacement);
                                log.append("Import Macro: [" + macroEntry.toString() + "]\n");
                                macroList.add(macroEntry);
                            }
                        } else {
                            log.append("Invalid format. Skipping line: [" + line + "]\n");
                        }
                    }
                }
                bufferedReader.close();

                if (mFormat == FORMAT_JSON) {
                    macroList = MacroUtils.jsonToMacroArrayList(json.toString());
                }

                String result;
                if (macroList.size() > 0) {
                    result = "Complete. Imported: " + macroList.size() + " macros from " + path;
                    // + "\n\nSoft reboot to activate";
                    MacroUtils.saveMacroList(macroList, mPrefs);
                } else {
                    result = "Complete. No macros found in file " + path;
                }

                if (mPrefs.getBoolean("prefImportDebug", false)) {
                    FileOutputStream fileOutputStream = new FileOutputStream(path + ".log");
                    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
                    outputStreamWriter.append(log);
                    outputStreamWriter.close();
                    fileOutputStream.close();
                    result = result + "\n\nDebug log output to " + path + ".log";
                }

                return result;

            } catch (Exception e) {
                Log.e(TAG, "File import error:", e);
                return "File import error:" + e;
            }
        }

        protected void onPostExecute(String result) {
            setProgressBarIndeterminateVisibility(false);
            final String output = result.toString();
            importResultDialog(output);
        }
    }

    class ExportMacroListTask extends AsyncTask<String, Void, String> {

        int mFormat;

        public ExportMacroListTask(int format) {
            mFormat = format;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            setProgressBarIndeterminateVisibility(true);
        }

        @Override
        protected String doInBackground(String... params) {
            ArrayList<MacroEntry> macroList = MacroUtils.loadMacroList(mPrefs);

            if (macroList == null || macroList.isEmpty()) {
                return "Macro list empty. No file was exported.";
            }

            try {
                String path = "";
                FileOutputStream fileOutputStream = null;
                OutputStreamWriter outputStreamWriter = null;

                switch (mFormat) {
                    case FORMAT_AHK:
                        path = Environment.getExternalStorageDirectory() + "/macros.ahk";
                        fileOutputStream = new FileOutputStream(path);
                        outputStreamWriter = new OutputStreamWriter(fileOutputStream);
                        for (MacroEntry macro : macroList) {
                            outputStreamWriter.append("::" + macro.actual + "::" + macro.replacement + "\n");
                        }
                        break;

                    case FORMAT_JSON:
                        path = Environment.getExternalStorageDirectory() + "/macros.json";
                        fileOutputStream = new FileOutputStream(path);
                        outputStreamWriter = new OutputStreamWriter(fileOutputStream);
                        outputStreamWriter.append(MacroUtils.macroArrayListToJson(macroList));
                        break;

                    default:
                        break;
                }

                outputStreamWriter.close();
                fileOutputStream.close();
                return "Complete. Exported " + macroList.size() + " macros to " + path;
            } catch (Exception e) {
                Log.e(TAG, "File export error: ", e);
                return "File export error: " + e;
            }
        }

        protected void onPostExecute(String result) {
            setProgressBarIndeterminateVisibility(false);
            final String output = result.toString();
            exportResultDialog(output);
        }
    }
}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/utils/AppUtils.java<|end_filename|>
package net.mitchtech.utils;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;

import java.util.List;

public class AppUtils {

    private static final String TAG = AppUtils.class.getSimpleName();

    public static String getVersion(Context context) {
        String version = "";
        try {
            PackageInfo pi = context.getPackageManager()
                    .getPackageInfo(context.getPackageName(), 0);
            version = " v" + pi.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Package name not found", e);
        }
        return version;
    }

    public static Class<?> getLauncherClass(Context context) {
        String className = context.getPackageManager()
                .getLaunchIntentForPackage(context.getPackageName())
                .getComponent().getClassName();
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
        return clazz;
    }

    public static void reloadLauncherActivity(Context context) {
        Class<?> clazz = getLauncherClass(context);
        Intent intent = new Intent(context, clazz);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    public static String getCurrentActiveApp(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
        String packageName = am.getRunningTasks(1).get(0).topActivity.getPackageName();
        return packageName;
    }

    public static List<ActivityManager.RunningTaskInfo> getCurrentRunningApps(Context context) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
        for (int i = 0; i < tasks.size(); i++) {
            Log.i("Running task", "Running task: " + tasks.get(i).baseActivity.toShortString() + "\t\t ID: " + tasks.get(i).id);
        }
        return tasks;
    }

    public static String getCurrentApps(Context context) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
        String apps = "";
        for (int i = 0; i < tasks.size(); i++) {
//            Log.i("Running task", "Running task: " + tasks.get(i).baseActivity.toShortString() + "\t\t ID: " + tasks.get(i).id);
//            apps += tasks.get(i).baseActivity.toShortString() + "\t\t ID: " + tasks.get(i).id;
//            apps += "[" + tasks.get(i).id + "]" + tasks.get(i).baseActivity.toShortString() + ",";
            apps += tasks.get(i).baseActivity.toShortString() + ",";

        }
        return apps;
    }

    public static List<PackageInfo> getInstalledApps(Context context) {
        PackageManager packageManager = context.getPackageManager();
        List<PackageInfo> packages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
        for (PackageInfo app : packages) {
            Log.i(TAG, "" + app.packageName + ":" + app.applicationInfo.name);
//           for (PermissionInfo permission : app.permissions) {
//               Log.i(TAG, "permission:" + permission.name);
//           }
        }

        return packages;
    }

}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/xposed/DynamicMacroPreferenceActivity.java<|end_filename|>

package net.mitchtech.xposed;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import com.afollestad.materialdialogs.MaterialDialog;

import net.mitchtech.xposed.macroexpand.R;

public class DynamicMacroPreferenceActivity extends BaseActivity {

    private static final String TAG = DynamicMacroPreferenceActivity.class.getSimpleName();

    private Preference mPrefDynamicDateKeyword;
    private Preference mPrefDynamicTimeKeyword;
    private Preference mPrefDynamicWeekdayKeyword;

    private Preference mPrefDynamicBatteryLevelKeyword;
    private Preference mPrefDynamicBatteryStateKeyword;

    private Preference mPrefDynamicIntStorageKeyword;
    private Preference mPrefDynamicExtStorageKeyword;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base);
//        getPreferenceManager().setSharedPreferencesMode(Context.MODE_WORLD_READABLE);
//        addPreferencesFromResource(R.xml.settings);
        initToolbar();
        initDrawer();

        getFragmentManager().beginTransaction().replace(R.id.content_frame, new SettingsFragment()).commit();
    }

    public class SettingsFragment extends PreferenceFragment implements
            SharedPreferences.OnSharedPreferenceChangeListener {

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // this is important since settings executed in the context of the hooked package
            getPreferenceManager().setSharedPreferencesMode(Context.MODE_WORLD_READABLE);
            addPreferencesFromResource(R.xml.dynamic_settings);
//            getActionBar().setDisplayHomeAsUpEnabled(true);
//            mPrefs = getPreferenceScreen().getSharedPreferences();
            mPrefDynamicDateKeyword = findPreference("prefDynamicDateKeyword");
            mPrefDynamicTimeKeyword = findPreference("prefDynamicTimeKeyword");
            mPrefDynamicWeekdayKeyword = findPreference("prefDynamicWeekdayKeyword");

            mPrefDynamicBatteryLevelKeyword = findPreference("prefDynamicBatteryLevel");
            mPrefDynamicBatteryStateKeyword = findPreference("prefDynamicBatteryState");

            mPrefDynamicIntStorageKeyword = findPreference("prefDynamicIntStorage");
            mPrefDynamicExtStorageKeyword = findPreference("prefDynamicExtStorage");
        }

        @Override
        public void onResume() {
            super.onResume();
            getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
        }

        @Override
        public void onPause() {
            super.onPause();
            getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
        }

        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

        }

        @Override
        public boolean onPreferenceTreeClick(PreferenceScreen prefScreen, Preference pref) {
            if ((pref == mPrefDynamicDateKeyword) ||
                    (pref == mPrefDynamicTimeKeyword) ||
                    (pref == mPrefDynamicWeekdayKeyword)) {
                setDynamicMacroKeyword(pref);
            }
            return super.onPreferenceTreeClick(prefScreen, pref);
        }
    }

    private void setDynamicMacroKeyword(final Preference pref) {
        LayoutInflater factory = LayoutInflater.from(DynamicMacroPreferenceActivity.this);
        View view = factory.inflate(R.layout.dialog_edit_dynamic_macro, null);
        TextView textView = (TextView) view.findViewById(R.id.description);
        final EditText editText = (EditText) view.findViewById(R.id.input);
        textView.setText("Enter text keyword or phrase to trigger dynamic macro expansion." +
                "\n\nChange requires soft reboot to activate.");
        editText.setText(mPrefs.getString(pref.getKey(), "default"));

        new MaterialDialog.Builder(DynamicMacroPreferenceActivity.this)
                .title("Set Macro Keyword")
                .customView(view, true)
                .positiveText("OK")
                .negativeText("Cancel")
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        String keyword = editText.getText().toString();
                        SharedPreferences.Editor editor = mPrefs.edit();
                        editor.putString(pref.getKey(), keyword);
                        editor.commit();
                    }
                }).show();
    }


}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/xposed/EditMacrosActivity.java<|end_filename|>

package net.mitchtech.xposed;

import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import com.afollestad.materialdialogs.MaterialDialog;
import com.getbase.floatingactionbutton.FloatingActionButton;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.typeface.FontAwesome;

import net.mitchtech.utils.MacroUtils;
import net.mitchtech.xposed.macroexpand.R;

import java.util.ArrayList;

import it.gmariotti.cardslib.library.internal.Card;
import it.gmariotti.cardslib.library.internal.CardArrayAdapter;
import it.gmariotti.cardslib.library.internal.CardHeader;
import it.gmariotti.cardslib.library.internal.base.BaseCard;
import it.gmariotti.cardslib.library.view.CardListView;

public class EditMacrosActivity extends BaseActivity {

    private static final String TAG = EditMacrosActivity.class.getSimpleName();

    private static final int MACRO_SIZE_WARN = 100;

    private CardListView mListView;
    private TextView mListEmptyTextView;
    private ArrayList<MacroEntry> mMacroList;
    private CardArrayAdapter mCardArrayAdapter;
    private ArrayList<Card> mCards;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_edit_macros);

        initToolbar();
        initDrawer();

        mListView = (CardListView) findViewById(R.id.listview);
        mListEmptyTextView = (TextView) findViewById(R.id.listEmptyText);
        mMacroList = MacroUtils.loadMacroList(mPrefs);

//        if (mMacroList.size() >= MACRO_SIZE_WARN) {
//            macroListLengthWarningDialog(mMacroList.size());
//        }

        mCards = new ArrayList<Card>();
        for (MacroEntry macroEntry : mMacroList) {
            mCards.add(macroEntryToCard(macroEntry));
        }

        mCardArrayAdapter = new CardArrayAdapter(this, mCards);
        if (mListView != null) {
            mListView.setAdapter(mCardArrayAdapter);
        }

        Drawable plus = new IconicsDrawable(this, FontAwesome.Icon.faw_plus).color(Color.DKGRAY).sizeDp(20);
        FloatingActionButton addButton = (FloatingActionButton) findViewById(R.id.add);
        addButton.setImageDrawable(plus);
        addButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addMacro();
            }
        });
    }

    @Override
    protected void onResume() {
        if (mMacroList == null || mMacroList.isEmpty()) {
            mListEmptyTextView.setVisibility(View.VISIBLE);
        } else {
            mListEmptyTextView.setVisibility(View.GONE);
        }
        super.onResume();
    }


    private void addMacro() {
        editMacro(-1);
    }

    //    private void editMacro(MacroEntry entry, final int position) {
    private void editMacro(final int position) {
        MacroEntry entry;
        LayoutInflater factory = LayoutInflater.from(EditMacrosActivity.this);
        final View textEntryView = factory.inflate(R.layout.dialog_edit_macro, null);
        final EditText actual = (EditText) textEntryView.findViewById(R.id.actual);
        final EditText replacement = (EditText) textEntryView.findViewById(R.id.replacement);
        if (position > -1) {
            entry = mMacroList.get(position);
            actual.setText(entry.actual, TextView.BufferType.EDITABLE);
            replacement.setText(entry.replacement, TextView.BufferType.EDITABLE);
        }

        new MaterialDialog.Builder(EditMacrosActivity.this)
                .title("Define Macro")
                .customView(textEntryView, true)
                .positiveText("Save")
                .negativeText("Cancel")
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        String actualText = actual.getText().toString();
                        String replacementText = replacement.getText().toString();
                        // if (isTextRegexFree(actualText)
                        if (position > -1) {
                            mMacroList.remove(position);
                            mCards.remove(position);
                        }
                        MacroEntry macroEntry = new MacroEntry(actualText, replacementText);
                        mMacroList.add(macroEntry);
                        mCards.add(macroEntryToCard(macroEntry));
                        mListEmptyTextView.setVisibility(View.GONE);
                        mCardArrayAdapter.notifyDataSetChanged();
                        MacroUtils.saveMacroList(mMacroList, mPrefs);
                    }
                }).show();
    }

    private void removeMacro(final int position) {
        new MaterialDialog.Builder(EditMacrosActivity.this)
                .title("Delete Macro?")
                .content("Are you sure you want to delete this macro?")
                .positiveText("Confirm")
                .negativeText("Cancel")
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        mMacroList.remove(position);
                        mCards.remove(position);
                        if (mMacroList.isEmpty()) {
                            mListEmptyTextView.setVisibility(View.VISIBLE);
                        }
                        mCardArrayAdapter.notifyDataSetChanged();
                        MacroUtils.saveMacroList(mMacroList, mPrefs);
                    }
                }).show();
    }

    private void macroListLengthWarningDialog(int length) {
        new MaterialDialog.Builder(EditMacrosActivity.this)
                .title("Size Warning")
                .content("Waring, macro list contains " + length
                        + " entries. This is permitted, but performance my degrade as a result")
                .positiveText("OK")
                .show();
    }

    private Card macroEntryToCard(MacroEntry macroEntry) {
        Card card = new Card(this);
        CardHeader header = new CardHeader(this);

        header.setTitle(macroEntry.toString());
        card.setTitle(macroEntry.isEnabled());

        header.setPopupMenu(R.menu.macro, new CardHeader.OnClickCardHeaderPopupMenuListener() {
            @Override
            public void onMenuItemClick(BaseCard baseCard, MenuItem menuItem) {
                int index = mCards.indexOf(baseCard);
                switch (menuItem.getItemId()) {

                    case R.id.action_enable:
                        baseCard.setTitle("Disabled");
                        break;

                    case R.id.action_edit:
                        editMacro(index);
                        break;

                    case R.id.action_delete:
                        removeMacro(index);
                        break;
                }
                mCardArrayAdapter.notifyDataSetChanged();
            }
        });
//        header.setButtonOverflowVisible(true);
//        header.setButtonExpandVisible(true);
        card.addCardHeader(header);

        card.setOnClickListener(new Card.OnCardClickListener() {
            @Override
            public void onClick(Card card, View view) {
                int index = mCards.indexOf(card);
                editMacro(index);

            }
        });

        card.setOnLongClickListener(new Card.OnLongCardClickListener() {
            @Override
            public boolean onLongClick(Card card, View view) {
                int index = mCards.indexOf(card);
                removeMacro(index);
                return true;
            }
        });
        return card;
    }
}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/utils/StorageUtils.java<|end_filename|>
package net.mitchtech.utils;

import android.os.Environment;
import android.os.StatFs;

public class StorageUtils {

    private static final String TAG = StorageUtils.class.getSimpleName();

    public static long getInternalAvailableSpace() {
        long availableSpaceKb = -1L;
        long availableSpaceMb = -1L;
        try {
            StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
            stat.restat(Environment.getDataDirectory().getPath());
            availableSpaceKb = ((long) stat.getAvailableBlocks() * (long) stat.getBlockSize()) / (long) 1024;
            availableSpaceMb = availableSpaceKb / (long) 1024;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return availableSpaceMb;
    }

    public static long getExternalAvailableSpace() {
        long availableSpaceKb = -1L;
        long availableSpaceMb = -1L;
        try {
            StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
            stat.restat(Environment.getExternalStorageDirectory().getPath());
            availableSpaceKb = ((long) stat.getAvailableBlocks() * (long) stat.getBlockSize()) / (long) 1024;
            availableSpaceMb = availableSpaceKb / (long) 1024;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return availableSpaceMb;
    }
}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/utils/NetworkUtils.java<|end_filename|>
package net.mitchtech.utils;

import android.content.Context;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import java.util.List;

public class NetworkUtils {

    private static final String TAG = NetworkUtils.class.getSimpleName();

    public static String getMacAddress(Context context) {
        WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInf = wifiMan.getConnectionInfo();
        String macAddress = wifiInf.getMacAddress();
        return macAddress;
    }

    public static String getIpAddress(Context context) {
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        int ip = wifiInfo.getIpAddress();

        String ipAddress = String.format("%d.%d.%d.%d",
                (ip & 0xff),
                (ip >> 8 & 0xff),
                (ip >> 16 & 0xff),
                (ip >> 24 & 0xff));
        return ipAddress;
    }

    public static String getPublicIpAddress() {
        try {
            HttpClient httpclient = new DefaultHttpClient();
            // HttpGet httpget = new HttpGet("http://ip2country.sourceforge.net/ip2c.php?format=JSON");
            // HttpGet httpget = new HttpGet("http://whatismyip.com.au/");
            HttpGet httpget = new HttpGet("http://www.whatismyip.org/");

            HttpResponse response;
            response = httpclient.execute(httpget);
            HttpEntity entity = response.getEntity();
            entity.getContentLength();

            String ipAdd = EntityUtils.toString(entity);
            JSONObject json_data = new JSONObject(ipAdd);
            String ipAddress = json_data.getString("ip");
            return ipAddress;
        } catch (Exception ex) {
            Log.e(TAG, ex.toString());
        }
        return null;
    }

    public static String getWifiSsid(Context context) {
        WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
        String wifiSsid = wifiInfo.getSSID().replace("\"", "").trim();
        return wifiSsid;
    }

    public static String getSignalStrength(Context context) {
        WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
        String signalStrength = String.valueOf(wifiInfo.getRssi());
        return signalStrength;
    }

    public static String getLinkSpeed(Context context) {
        WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
        String linkSpeed = String.valueOf(wifiInfo.getLinkSpeed()) + " Mbps";
        return linkSpeed;
    }

    public static String getWifiSecurityType(Context context) {
        WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        List<ScanResult> networkList = wifiMgr.getScanResults();
        WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
        String ssid = wifiInfo.getSSID().replaceAll("^\"|\"$", "");
        if (networkList != null) {
            for (ScanResult network : networkList) {
                if (ssid.contentEquals(network.SSID)) {
                    String capabilities = network.capabilities;
//                    Log.i(TAG, "Security:[" + capabilities + "]");
//                    if (capabilities.contains("WPA")) {
//                        // WPA encryption
//                    } else if (capabilities.contains("WEP")) {
//                        // WEP encryption
//                    } else {
//                        // open wifi, captive portal, other
//                    }
                    return capabilities;
                }
            }
        }
        return null;
    }

}


<|start_filename|>xposedMacroExpand/src/main/java/net/mitchtech/xposed/MacroEntry.java<|end_filename|>

package net.mitchtech.xposed;

public class MacroEntry {
    public String actual;
    public String replacement;
    public boolean enabled;

    public MacroEntry(String actual, String replacement) {
        super();
        this.actual = actual;
        this.replacement = replacement;
    }

    public String toString() {
        return actual + " --> " + replacement;
    }
    
    public String toAhkRow() {
        return "::" + actual + "::" + replacement + "\n";
    }

    public String isEnabled() { return (enabled) ? "Enabled" : "Disabled" ; }
}
