[
  {
    "path": "FIR_plugin.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"PLUGIN_MODULE\" version=\"4\">\n  <component name=\"DevKit.ModuleBuildProperties\" url=\"file://$MODULE_DIR$/META-INF/plugin.xml\" />\n  <component name=\"NewModuleRootManager\" LANGUAGE_LEVEL=\"JDK_1_6\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/src\" isTestSource=\"false\" />\n    </content>\n    <orderEntry type=\"jdk\" jdkName=\"IDEA IU-129.1525\" jdkType=\"IDEA JDK\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"module-library\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/commons-codec-1.6.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"module-library\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/commons-logging-1.1.1.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"module-library\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/fluent-hc-4.2.5.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"module-library\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/httpclient-4.2.5.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"module-library\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/httpclient-cache-4.2.5.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"module-library\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/httpcore-4.2.4.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"module-library\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/httpmime-4.2.5.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"module-library\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/AXMLPrinter.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"module-library\" exported=\"\">\n      <library>\n        <CLASSES>\n          <root url=\"jar://$MODULE_DIR$/libs/json.org.jar!/\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n      </library>\n    </orderEntry>\n    <orderEntry type=\"library\" name=\"libs\" level=\"project\" />\n  </component>\n</module>\n\n"
  },
  {
    "path": "META-INF/plugin.xml",
    "content": "<idea-plugin version=\"2\">\n    <id>fir.im.plug.idea</id>\n    <name>fir.im upload</name>\n    <version>2.3</version>\n    <vendor email=\"yh@fir.im\" url=\"http://fir.im\">FIR.im</vendor>\n\n    <description><![CDATA[\n\n      <b>fir.im upload</b>\n      </br>\n      <b>ONLY 2 STEPS To Distribute Beta Applications</b>\n      </br>\n      <b>FIR.im provides beta app distribution services for free in a fast and safe way by 2 steps: upload IPA/APK, download and install by a short URL . FIR is short for Fly It Remotely.</b>\n      </br>\n      <b>fir.im http://fir.im</b>\n      ]]></description>\n\n    <change-notes><![CDATA[\n     v0.1 Add:\n     <br>- apk upload<br>\n     v0.2 change:\n     <br> - App update apk versionName versionCode bug     <br>\n     v1.0 change:\n     <br> - change api to new fir api     <br>\n     <br> - new api doc http://fir.im/docs     <br>\n     <br> - catch exception and notice by slack     <br>\n     <br> - add internationalization     <br>\n     v1.1 change:\n     <br> - parse apk name     <br>\n     <br> - new api doc http://fir.im/docs     <br>\n     v1.2 change:\n     <br> - upload icon     <br>\n     <br> - new api doc http://fir.im/docs     <br>\n     v1.3 change:\n     <br> -add click link to browser it in internet <br>\n     <br> -add tips <br>\n     v1.4 change:\n     <br> -fix setting path bug<br>\n     <br> -add feedback <br>\n     v1.5 change:\n     <br> - add Automatic inspection tips<br>\n     v1.6 change:\n     <br> - add Automatic inspection checkbox<br>\n     v1.7 change:\n     <br> - finish windows bug<br>\n     v1.8 change:\n     <br> - edit apk parser<br>\n     v1.9 change:\n     <br> - change tip font-size & fix any bug<br>\n     v2.0 change:\n     <br> - add qrCode<br>\n     v2.1 change:\n     <br> - fix zipFile bug<br>\n     v2.2 change:\n     <br> - add cancel upload bug<br>\n     v2.3 change:\n     <br> - add language select<br>\n      ]]>\n    </change-notes>\n\n    <!-- please see http://confluence.jetbrains.net/display/IDEADEV/Build+Number+Ranges for description -->\n    <idea-version since-build=\"107.105\"/>\n\n    <!-- please see http://confluence.jetbrains.net/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products\n         on how to target different products -->\n\n    <!--This allow plugin to be installed on another IntelliJ Platform products-->\n    <depends>com.intellij.modules.lang</depends>\n    <!-- This tells the jetbrains plugin repository that the plugin uses the android jar -->\n    <depends>org.jetbrains.android</depends>\n\n\n    <application-components>\n\n        <!-- Add your application components here -->\n\n    </application-components>\n\n    <project-components>\n        <!-- Add your project components here -->\n    </project-components>\n\n    <actions>\n    </actions>\n\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <!-- Lets us persist data using the KeyManager -->\n        <applicationService serviceImplementation=\"ro.catalin.prata.firuploader.controller.KeysManager\"/>\n        <!-- Add your extensions here -->\n        <toolWindow id=\"FIR.im\" secondary=\"true\" anchor=\"right\"  icon=\"images/icon.png\"\n                    factoryClass=\"ro.catalin.prata.firuploader.view.Main\">\n        </toolWindow>\n    </extensions>\n\n</idea-plugin>"
  },
  {
    "path": "README.md",
    "content": "fir.im upload\n---\n> 一键上传应用到fir.im\n\n> jetbrains插件线上地址 [fir.im upload](https://plugins.jetbrains.com/plugin/7640?pr=androidstudio)\n\n> 插件下载地址 [fir.im-upload-2.3](https://github.com/FIRHQ/FIR_Plugin_Android/blob/master/dist/Fir_Android_Plugin_2.3.zip?raw=true) (下载之后使用本地安装 --添加上传取消功能)\n\n> 插件下载地址 [fir.im-upload-2.2](https://github.com/FIRHQ/FIR_Plugin_Android/blob/master/dist/Fir_Android_Plugin_2.2.zip) (下载之后使用本地安装 --添加手动中英文切换)\n\n> 插件下载地址 [fir.im-upload-2.0](https://github.com/FIRHQ/FIR_Plugin_Android/blob/master/dist/fir_plugin_2.0.0.zip) (下载之后使用本地安装 --添加了展示二维码的功能)\n\n> 插件下载地址 [fir.im-upload-1.9](https://github.com/FIRHQ/FIR_Plugin_Android/blob/master/dist/fir_plugin_1.9.0.zip) (下载之后使用本地安装)\n\n\n\n## 使用入门\n### 从安装入手\n\n在plugins插件市场搜索fir.im upload安装就可以了:现在支持平台 Android Studio & IntelliJ IDEA\n\n安装之后 重启对应的开发环境\n\n插件会在Views -> tool windows显示 FIR.im\n\n### 具体安装步骤\nfir.im upload 安装\n- 方式一、 Android Studio-> preferences -> plugins -> 搜索fir.im upload\n- 方式二、[下载包进行本地安装](http://firweb.fir.im/fir_plugin_1.9.0.zip)，步骤 Android Studio-> preferences -> plugins -> install plugin from disk..\n\n### 使用说明\n\n打开插件先设置api_token,查看[api_token](http://fir.im/user/info)\n\n插件会自动检测当前功能的apk路径（如果没有检测到apk则可以手动设置apk路径）\n\n可以填写更新日志\n\n检测提示复选框 -> 意思是 当你选中检测提示复选框，当发现apk有改变时就会直接提示是否上传，取消则不提示\n\n自动上传复选框 -> 意思是 当你选中自动上传复选框，会自动检测apk改变时直接上传，取消则不自动上传\n\n\n## 提交反馈\n\n[使用 github issue 即可](https://github.com/FIRHQ/fir_intellig_plugin/issues)\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/Model/Binary.java",
    "content": "package ro.catalin.prata.firuploader.Model;\n\n\nimport net.dongliu.apk.parser.ApkParser;\nimport net.dongliu.apk.parser.bean.ApkMeta;\nimport ro.catalin.prata.firuploader.utils.Utils;\nimport ro.catalin.prata.firuploader.view.Main;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/10\n * Time: 下午4:28\n * To change this template use File | Settings | File Templates.\n */\npublic class Binary {\n    public String name;\n    public String icon;\n    public String bundleId;\n    public String aShort;\n    public String versionName;\n    public String versionCode;\n    public String filePath;\n    public static Binary binary;\n\n    public Binary(){\n        binary = this;\n    }\n\n    public Binary(String url){\n        binary = this;\n        this.filePath = url;\n        parseApk(url);\n    }\n\n    public static Binary getInstance(){\n        if(binary == null) return new Binary();\n        return binary;\n    }\n\n    public void initPath(String url){\n//        if(this.filePath == url){\n//            return ;\n//        }\n        this.filePath = url;\n        if(this.filePath.isEmpty())  return;\n        parseApk(this.filePath);\n    }\n\n    public void parseApk(String url){\n        ApkParser apkParser = null;\n        try {\n            apkParser = new ApkParser(new File(url));\n            ApkMeta apkMeta = apkParser.getApkMeta();\n\n            System.out.println(apkMeta.getLabel());\n            System.out.println(apkMeta.getPackageName());\n            System.out.println(apkMeta.getVersionCode());\n            System.out.println(apkMeta.getLabel());\n            System.out.println(apkMeta.getIcon().getPath());\n\n            this.versionName = apkMeta.getVersionName();\n            this.versionCode = apkMeta.getVersionCode().toString();\n            this.bundleId = apkMeta.getPackageName();\n            this.name = apkMeta.getLabel();\n            this.icon = apkMeta.getIcon().getPath();\n            apkParser.close();\n        } catch (IOException e) {\n\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n            Utils.postErrorNoticeTOSlack(e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/Model/CustomMultiPartEntity.java",
    "content": "package ro.catalin.prata.firuploader.Model;\n\nimport org.apache.http.entity.mime.HttpMultipartMode;\nimport org.apache.http.entity.mime.MultipartEntity;\n\nimport java.io.FilterOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.charset.Charset;\n\n\npublic class CustomMultiPartEntity extends MultipartEntity {\n\n    private final ProgressListener listener;\n\n    public CustomMultiPartEntity(final ProgressListener listener) {\n        super();\n        this.listener = listener;\n    }\n\n    public CustomMultiPartEntity(final HttpMultipartMode mode, final ProgressListener listener) {\n        super(mode);\n        this.listener = listener;\n    }\n\n    public CustomMultiPartEntity(HttpMultipartMode mode, final String boundary, final Charset charset, final ProgressListener listener) {\n        super(mode, boundary, charset);\n        this.listener = listener;\n    }\n\n    @Override\n    public void writeTo(final OutputStream outstream) throws IOException {\n        super.writeTo(new CountingOutputStream(outstream, this.listener));\n    }\n\n    public static interface ProgressListener {\n        void transferred(long num);\n    }\n\n    public static class CountingOutputStream extends FilterOutputStream {\n\n        private final ProgressListener listener;\n        private long transferred;\n\n        public CountingOutputStream(final OutputStream out, final ProgressListener listener) {\n            super(out);\n            this.listener = listener;\n            this.transferred = 0;\n        }\n\n        public void write(byte[] b, int off, int len) throws IOException {\n            out.write(b, off, len);\n            this.transferred += len;\n            this.listener.transferred(this.transferred);\n        }\n\n        public void write(int b) throws IOException {\n            out.write(b);\n            this.transferred++;\n            this.listener.transferred(this.transferred);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/Model/Document.java",
    "content": "package ro.catalin.prata.firuploader.Model;\n\nimport ro.catalin.prata.firuploader.utils.Utils;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/8\n * Time: 上午12:21\n * 做中英话处理的类\n */\npublic class Document {\n    public String formHeader;\n    public String formToken;\n    public String formProject;\n    public String formPath;\n    public String formLog;\n    public String formLink;\n    public String setTokenBtn;\n    public String settingBtn;\n    public String uploadBtn;\n    public String formTip;\n    public String formUpload;\n    public String cancelUpload;\n    public String languageLabel;\n    public String chineseBtn;\n    public String englishBtn;\n    public Document(){\n        if(Utils.isZh()){\n            formHeader = \"fir.im 一键上传\";\n            formToken = \"api_token\";\n            formProject = \"项目名称\";\n            formPath = \"文件路径\";\n            formLog = \"更新日志\";\n            formLink = \"应用地址\";\n            setTokenBtn = \"设置\";\n            settingBtn = \"选择文件\";\n            uploadBtn = \"上传\" ;\n            formTip = \"检测提示\";\n            formUpload = \"自动上传\";\n            cancelUpload = \"取消上传\";\n            languageLabel = \"语言\";\n            chineseBtn = \"中文\";\n            englishBtn = \"英文\";\n        } else{\n            formHeader = \"fir.im upload\";\n            formToken = \"api_token\";\n            formProject = \"Project\";\n            formPath = \"File path\";\n            formLog = \"Changelog\";\n            formLink = \"Short\";\n            setTokenBtn = \"Setting\";\n            settingBtn = \"Choose path\";\n            uploadBtn = \"Upload\" ;\n            formTip = \"Check & tip\";\n            formUpload = \"Auto upload\";\n            cancelUpload = \"cancel upload\";\n            languageLabel = \"Language\";\n            chineseBtn = \"Chinese\";\n            englishBtn = \"English\";\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/Model/UploadTicket.java",
    "content": "package ro.catalin.prata.firuploader.Model;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/7\n * Time: 下午2:24\n * To change this template use File | Settings | File Templates.\n */\npublic class UploadTicket {\n    public String id;\n    public String type;\n    public String appShort;\n    public String iconUploadUrl;\n    public String binaryUploadUrl;\n    public String iconKey;\n    public String iconToken;\n    public String binaryKey;\n    public String binaryToken;\n\n\n    public UploadTicket(JSONObject jsonObject){\n\n        try {\n            this.id = jsonObject.getString(\"id\");\n            this.type = jsonObject.getString(\"type\");\n            this.appShort = jsonObject.getString(\"short\");\n\n            JSONObject cert = jsonObject.getJSONObject(\"cert\");\n            JSONObject iconCert = cert.getJSONObject(\"icon\");\n            JSONObject binaryCert = cert.getJSONObject(\"binary\");\n\n            this.iconKey = iconCert.getString(\"key\");\n            this.iconToken = iconCert.getString(\"token\");\n            this.binaryKey = binaryCert.getString(\"key\");\n            this.binaryToken = binaryCert.getString(\"token\");\n            this.iconUploadUrl = iconCert.getString(\"upload_url\");\n            this.binaryUploadUrl = binaryCert.getString(\"upload_url\");\n\n        } catch (JSONException e) {\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        }\n    }\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/apkReader/ApkInfo.java",
    "content": "package ro.catalin.prata.firuploader.apkReader;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/5/19\n * Time: 下午6:22\n * To change this template use File | Settings | File Templates.\n */\nimport java.util.*;\nimport java.util.jar.JarEntry;\n\n\npublic class ApkInfo {\n    public static int FINE = 0;\n    public static int NULL_VERSION_CODE = 1;\n    public static int NULL_VERSION_NAME = 2;\n    public static int NULL_PERMISSION = 3;\n    public static int NULL_ICON = 4;\n    public static int NULL_CERT_FILE = 5;\n    public static int BAD_CERT = 6;\n    public static int NULL_SF_FILE = 7;\n    public static int BAD_SF = 8;\n    public static int NULL_MANIFEST = 9;\n    public static int NULL_RESOURCES = 10;\n    public static int NULL_DEX = 13;\n    public static int NULL_METAINFO = 14;\n    public static int BAD_JAR = 11;\n    public static int BAD_READ_INFO = 12;\n    public static int NULL_FILE = 15;\n    public static int HAS_REF = 16;\n\n    public String rawAndroidManifest;\n\n    public List<String> dexClassName=new ArrayList<String>();;\n    public List<String> dexUrls=new ArrayList<String>();;\n\n    public String label;\n    public String fileHash;\n    public String versionName;\n    public String versionCode;\n    public String minSdkVersion;\n    public String targetSdkVersion;\n    public String packageName;\n    public List<String> Permissions;\n    public List<String> iconFileName;\n    public List<String> iconFileNameToGet;\n    public List<String> iconHash;\n    public String rsaCertFileName;\n    public byte[] rsaCertFileBytes;\n    public String sfCertFileName;\n    public byte[] sfCertFileBytes;\n    public String mfCertFileName;\n    public byte[] mfcCertFileBytes;\n    public String manifestFileName;\n    public byte[] manifestFileBytes;\n    public boolean hasIcon;\n    public boolean supportSmallScreens;\n    public boolean supportNormalScreens;\n    public boolean supportLargeScreens;\n    public boolean supportAnyDensity;\n    public Map<String, ArrayList<String>> resStrings;\n    public Map<String, String> layoutStrings;\n    public Hashtable<String, JarEntry> entryList ;\n\n    public static boolean supportSmallScreen(byte[] dpi) {\n        if (dpi[0] == 1)\n            return true;\n        return false;\n    }\n\n    public static boolean supportNormalScreen(byte[] dpi) {\n        if (dpi[1] == 1)\n            return true;\n        return false;\n    }\n\n    public static boolean supportLargeScreen(byte[] dpi) {\n        if (dpi[2] == 1)\n            return true;\n        return false;\n    }\n\n    public byte[] getDPI() {\n        byte[] dpi = new byte[3];\n        if (this.supportAnyDensity) {\n            dpi[0] = 1;\n            dpi[1] = 1;\n            dpi[2] = 1;\n        } else {\n            if (this.supportSmallScreens)\n                dpi[0] = 1;\n            if (this.supportNormalScreens)\n                dpi[1] = 1;\n            if (this.supportLargeScreens)\n                dpi[2] = 1;\n        }\n        return dpi;\n    }\n\n    public ApkInfo() {\n        hasIcon = false;\n        supportSmallScreens = false;\n        supportNormalScreens = false;\n        supportLargeScreens = false;\n        supportAnyDensity = true;\n        versionCode = null;\n        versionName = null;\n        iconFileName = null;\n        iconFileNameToGet = null;\n        rsaCertFileName = null;\n        sfCertFileName = null;\n        mfCertFileName = null;\n\n        Permissions = new ArrayList<String>();\n        entryList=new Hashtable<String, JarEntry>();\n    }\n\n    public String toString() {\n        String ret = \"versionCode\\t\" + versionCode + \"\\r\\n\" + \"versionName\\t\"\n                + versionName + \"\\r\\n\" + \"Permissions\\t\" + Permissions + \"\\r\\n\"\n                + \"iconFileName\\t\" + iconFileName + \"\\r\\n\" + \"iconName\\t\"\n                + iconFileNameToGet + \"\\r\\n\" + \"manifestFileName\\t\"\n                + manifestFileName + \"\\r\\n\" + \"layoutStrings\\t\" +  ((layoutStrings == null) ? \"\" : String.valueOf(layoutStrings.size())) + \"\\r\\n\"\n//\t\t\t\t+ manifestFileName + \"\\r\\n\" + \"layoutStrings\\t\" +  layoutStrings.keySet() + \"\\r\\n\"\n                + \"resStrings\\t\" + ((resStrings == null) ? \"\"\t: String.valueOf(resStrings.size())) + \"\\r\\n\";\n//\t\t\t\t+ \"resStrings\\t\" + resStrings.keySet() + \"\\r\\n\";\n        return ret;\n    }\n\n    private boolean isReference(List<String> strs) {\n        try {\n            for (String str : strs) {\n                if (isReference(str))\n                    return true;\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    private boolean isReference(String str) {\n        try {\n            if (str != null && str.startsWith(\"@\")) {\n                Integer.valueOf(str, 16);\n                return true;\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    public boolean hasReference() {\n        if (isReference(versionCode) || isReference(versionName)\n                || isReference(iconFileNameToGet))\n            return true;\n        else\n            return false;\n    }\n\n    public int isValid() {\n        if (hasReference()) {\n            return HAS_REF;\n        } else if (versionCode == null) {\n            return NULL_VERSION_CODE;\n        } else if (versionName == null) {\n            return NULL_VERSION_NAME;\n        } else if (Permissions == null) {\n            return NULL_PERMISSION;\n        } else if (iconFileName == null) {\n            return NULL_ICON;\n        } else if (iconFileNameToGet == null) {\n            return NULL_ICON;\n        } else if (hasIcon == false) {\n            return NULL_ICON;\n        }\n\n        return FINE;\n    }\n}"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/controller/KeysManager.java",
    "content": "package ro.catalin.prata.firuploader.controller;\n\nimport com.intellij.openapi.compiler.CompilationStatusListener;\nimport com.intellij.openapi.compiler.CompileContext;\nimport com.intellij.openapi.compiler.CompilerManager;\nimport com.intellij.openapi.components.PersistentStateComponent;\nimport com.intellij.openapi.components.ServiceManager;\nimport com.intellij.openapi.components.State;\nimport com.intellij.openapi.components.Storage;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.project.ProjectManager;\nimport com.intellij.openapi.project.ProjectManagerListener;\nimport com.intellij.openapi.ui.MessageType;\nimport com.intellij.openapi.ui.popup.Balloon;\nimport com.intellij.openapi.ui.popup.JBPopupFactory;\nimport com.intellij.openapi.wm.StatusBar;\nimport com.intellij.openapi.wm.ToolWindowManager;\nimport com.intellij.openapi.wm.WindowManager;\nimport com.intellij.ui.awt.RelativePoint;\nimport org.jdom.Element;\nimport org.jetbrains.annotations.Nullable;\nimport ro.catalin.prata.firuploader.utils.Constants;\n\nimport javax.swing.event.HyperlinkEvent;\nimport javax.swing.event.HyperlinkListener;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Iterator;\n\n\n@State(\n        name = \"KeysManager\", storages = {\n        @Storage(\n                id = \"other\",\n                file = \"$APP_CONFIG$/\" + Constants.PERSISTENCE_FILE_NAME)\n})\npublic class KeysManager implements PersistentStateComponent<Element>,CompilationStatusListener {\n\n    // xml parsing constant used as a root tag for this class\n    public static final String XML_ROOT_NAME_Key_MANAGER = \"KeyManager\";\n    // xml parsing constants used for team\n    public static final String XML_ROOT_NAME_TEAM_MANAGER = \"TeamManager\";\n    public static final String XML_TEAM_MANAGER_TEAM = \"Team\";\n    public static final String XML_TEAM_MANAGER_NAME = \"name\";\n    public static final String XML_TEAM_MANAGER_TOKEN = \"token\";\n    public static final String XML_TEAM_MANAGER_DISTRIBUTION_LIST = \"distribution\";\n    public static final String XML_TEAM_MANAGER_COMPONENT = \"component\";\n    // xml parsing constants used for api key\n    public static final String XML_ROOT_NAME_API_KEY = \"ApiKey\";\n    // xml parsing constant used for the apk file path\n    public static final String XML_ROOT_NAME_APK_FILE_PATH = \"ApkFilePath\";\n    public static final String XML_ROOT_NAME_APK_MD5_VAL = \"MD5\";\n    public static final String XML_ROOT_NAME_APK_FLAG_VAL = \"FLAG\";\n    public static final String XML_ROOT_NAME_APK_LANGUAGE_VAL = \"LANGUAGE\";\n\n    public static final String XML_ROOT_NAME_APK_UPLOAD_FLAG_VAL = \"UPLOAD_FLAG\";\n    public static final String XML_ROOT_NAME_SELECTED_MODULE_NAME = \"SelectedModuleName\";\n    public static final String XML_ROOT_NAME_SELECTED_PROJECT_NAME = \"SelectedProjectName\";\n\n    /**\n     * Maximum number of milliseconds since the last compile time before the user can send the build to Test Flight (currently 5 minutes)\n     */\n    public static final int MAX_MILLISECONDS_SINCE_LAST_COMPILE = 1000 * 60 * 5;\n    // keeps the last compile time so we can see how much time passed since the last build compile\n    private static Calendar lastCompileTime = Calendar.getInstance();\n    /**\n     * Manager's single instance\n     */\n    private static KeysManager sInstance = null;\n    /**\n     * List of teams for this user\n     */\n//    private ArrayList<Team> teamList;\n    /**\n     * Test flight api key\n     */\n    private String apiKey;\n    /**\n     * Project apk file path\n     */\n    private String apkFilePath;\n\n    private String md5;\n\n    private String language;\n\n    private String uploadFlag;\n\n    private String flag;\n    /**\n     * This is the user selected module name\n     */\n    private String selectedModuleName;\n    /**\n     * This is the user selected project name\n     */\n    private String selectedProjectName;\n\n    public KeysManager() {\n\n//        teamList = new ArrayList<Team>();\n\n    }\n\n    public static KeysManager instance() {\n\n        if (sInstance == null) {\n            sInstance = ServiceManager.getService(KeysManager.class);\n\n            ProjectManager.getInstance().addProjectManagerListener(new ProjectManagerListener() {\n                @Override\n                public void projectOpened(Project project) {\n\n                    CompilerManager.getInstance(project)\n                            .addCompilationStatusListener(sInstance);\n\n                }\n\n                @Override\n                public boolean canCloseProject(Project project) {\n                    return true;\n                }\n\n                @Override\n                public void projectClosed(Project project) {\n\n                }\n\n                @Override\n                public void projectClosing(Project project) {\n\n                    CompilerManager.getInstance(project)\n                            .removeCompilationStatusListener(sInstance);\n\n                }\n            });\n\n            CompilerManager.getInstance(ProjectManager.getInstance().getOpenProjects()[0])\n                    .addCompilationStatusListener(sInstance);\n        }\n\n        return sInstance;\n    }\n\n    @Nullable\n    @Override\n    public Element getState() {\n\n        // create the class root tag\n        Element rootTag = new Element(XML_ROOT_NAME_Key_MANAGER);\n\n        // create the team root xml tag\n        Element teamRootTag = new Element(XML_ROOT_NAME_TEAM_MANAGER);\n\n        // add the team elements\n        rootTag.addContent(teamRootTag);\n\n        if (apiKey != null) {\n            // set the api key\n            Element apiKeyTag = new Element(XML_ROOT_NAME_API_KEY).setText(apiKey);\n            rootTag.addContent(apiKeyTag);\n        }\n\n        if (apkFilePath != null) {\n            // set the apk file path\n            Element filePathTag = new Element(XML_ROOT_NAME_APK_FILE_PATH).setText(apkFilePath);\n            rootTag.addContent(filePathTag);\n        }\n\n        if (md5 != null){\n            Element filePathTag = new Element(XML_ROOT_NAME_APK_MD5_VAL).setText(md5);\n            rootTag.addContent(filePathTag);\n        }\n\n        if (language != null){\n            Element filePathTag = new Element(XML_ROOT_NAME_APK_LANGUAGE_VAL).setText(language);\n            rootTag.addContent(filePathTag);\n        }\n\n        if(uploadFlag != null){\n            Element filePathTag = new Element(XML_ROOT_NAME_APK_UPLOAD_FLAG_VAL).setText(uploadFlag);\n            rootTag.addContent(filePathTag);\n        }\n\n        if (flag != null){\n            Element filePathTag = new Element(XML_ROOT_NAME_APK_FLAG_VAL).setText(flag);\n            rootTag.addContent(filePathTag);\n        }\n\n        if (selectedModuleName != null) {\n            // set the user selected module\n            Element moduleName = new Element(XML_ROOT_NAME_SELECTED_MODULE_NAME).setText(selectedModuleName);\n            rootTag.addContent(moduleName);\n\n        }\n\n        if (selectedProjectName != null) {\n            // set the user selected project\n            Element projectName = new Element(XML_ROOT_NAME_SELECTED_PROJECT_NAME).setText(selectedProjectName);\n            rootTag.addContent(projectName);\n\n        }\n\n        return rootTag;\n    }\n\n    @Override\n    public void loadState(Element componentTag) {\n\n        if (componentTag.getName().equals(XML_TEAM_MANAGER_COMPONENT)) {\n\n            Iterator rootIterator = componentTag.getDescendants();\n\n            // loop through all the root elements and parse them accordingly\n            while (rootIterator.hasNext()) {\n\n                Object element = rootIterator.next();\n\n                if (!(element instanceof Element)) {\n                    continue;\n                }\n\n                Element rootElement = (Element) element;\n\n                if (rootElement.getName().equals(XML_ROOT_NAME_TEAM_MANAGER)) {\n                    // parse the team list\n//                    teamList = parseTeam(rootElement);\n\n                } else if (rootElement.getName().equals(XML_ROOT_NAME_API_KEY)) {\n                    // parse the api key\n                    apiKey = parseApiKey(rootElement);\n\n                } else if (rootElement.getName().equals(XML_ROOT_NAME_APK_FILE_PATH)) {\n                    // parse the apk file path\n                    apkFilePath = parseApkFilePath(rootElement);\n\n                } else if (rootElement.getName().equals(XML_ROOT_NAME_SELECTED_MODULE_NAME)) {\n                    // parse the user selected module name\n                    selectedModuleName = parseUserSelectedModuleName(rootElement);\n\n                } else if (rootElement.getName().equals(XML_ROOT_NAME_SELECTED_PROJECT_NAME)) {\n                    // parse the user selected project name\n                    selectedProjectName = parseUserSelectedProjectName(rootElement);\n\n                }  else if (rootElement.getName().equals(XML_ROOT_NAME_APK_MD5_VAL)) {\n                    // parse the apk file path\n                    md5 = parseMd5(rootElement);\n\n                }  else if (rootElement.getName().equals(XML_ROOT_NAME_APK_LANGUAGE_VAL)) {\n                    // parse the apk file path\n                    language = parseLanguage(rootElement);\n\n                } else if (rootElement.getName().equals(XML_ROOT_NAME_APK_UPLOAD_FLAG_VAL)) {\n                    // parse the apk file path\n                    uploadFlag = parseUploadFlag(rootElement);\n\n                }else if (rootElement.getName().equals(XML_ROOT_NAME_APK_FLAG_VAL)) {\n                    // parse the apk file path\n                    flag = parseFlag(rootElement);\n\n                }\n\n            }\n\n        }\n\n    }\n\n    /**\n     * Parse the user's selected project name xml element\n     *\n     * @param element the user's selected project element\n     * @return parsed value\n     */\n    public String parseUserSelectedProjectName(Element element) {\n\n        return element.getText();\n\n    }\n\n    /**\n     * Parse the user's selected module name xml element\n     *\n     * @param element the user's selected module element\n     * @return parsed value\n     */\n    public String parseUserSelectedModuleName(Element element) {\n\n        return element.getText();\n\n    }\n\n    /**\n     * Parse the apk file path xml element\n     *\n     * @param element apk file path element\n     * @return parsed file path\n     */\n    public String parseApkFilePath(Element element) {\n\n        return element.getText();\n\n    }\n\n    public String parseMd5(Element element){\n        return element.getText();\n    }\n\n    public String parseLanguage(Element element){\n        return element.getText();\n    }\n\n\n    public String parseFlag(Element element){\n        return element.getText();\n    }\n\n    public String parseUploadFlag(Element element){\n        return element.getText();\n    }\n\n    /**\n     * Parse the api key xml element\n     *\n     * @param element the root element of the api key\n     * @return parsed api key\n     */\n    public String parseApiKey(Element element) {\n\n        return element.getText();\n\n    }\n\n    /**\n     * Returns the api key for test flight authentication\n     *\n     * @return api key\n     */\n    public String getApiKey() {\n        return apiKey;\n    }\n\n    /**\n     * Set the api key used for the test flight authentication\n     *\n     * @param apiKey api key\n     */\n    public void setApiKey(String apiKey) {\n        this.apiKey = apiKey;\n    }\n\n    /**\n     * Returns the apk file path\n     *\n     * @return apk file path\n     */\n    public String getApkFilePath() {\n        return apkFilePath;\n    }\n\n    /**\n     * Set the apk file path\n     *\n     * @param apkFilePath apk file path\n     */\n    public void setApkFilePath(String apkFilePath) {\n        this.apkFilePath = apkFilePath;\n    }\n\n\n    public String getMd5(){\n        return md5;\n    }\n\n    public void setMd5(String m){\n        this.md5 = m;\n    }\n\n    public String getLanguage(){\n        return language;\n    }\n\n    public void setLanguage(String m){\n        this.language = m;\n    }\n\n    public String getUploadFlag(){\n        return uploadFlag;\n    }\n\n    public void setUploadFlag(String flag){\n        this.uploadFlag = flag;\n    }\n\n    public String getFlag(){\n        return flag;\n    }\n\n\n    public void setFlag(String fl){\n        this.flag = fl;\n    }\n    /**\n     * Returns the user's selected module name, null if none was saved so far\n     *\n     * @return user's selected module name\n     */\n    public String getSelectedModuleName() {\n        return selectedModuleName;\n    }\n\n    /**\n     * Set the user's selected module name, used to get the apk file path for the module\n     *\n     * @param selectedModuleName selected module name\n     */\n    public void setSelectedModuleName(String selectedModuleName) {\n        this.selectedModuleName = selectedModuleName;\n    }\n\n    /**\n     * Returns the selected project name\n     *\n     * @return user's selected project name\n     */\n    public String getSelectedProjectName() {\n        return selectedProjectName;\n    }\n\n    /**\n     * Set the selected project name\n     *\n     * @param selectedProjectName user selected project name\n     */\n    public void setSelectedProjectName(String selectedProjectName) {\n        this.selectedProjectName = selectedProjectName;\n    }\n\n    public static Calendar getLastCompileTime() {\n        return lastCompileTime;\n    }\n\n    public static void setLastCompileTime(Calendar lastCompileTime) {\n        KeysManager.lastCompileTime = lastCompileTime;\n    }\n\n    @Override\n    public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) {\n\n        if (errors < 1) {\n\n            // get the current time\n            lastCompileTime = Calendar.getInstance();\n\n            StatusBar statusBar = WindowManager.getInstance()\n                    .getStatusBar(ProjectManager.getInstance().getOpenProjects()[0]);\n\n\n            JBPopupFactory.getInstance()\n                    .createHtmlTextBalloonBuilder(\"编译完毕发送到FIR.im, <a href='open'>点击</a> 打开FIR.im uploader 并上传.\",\n                            MessageType.INFO, new HyperlinkListener() {\n                        @Override\n                        public void hyperlinkUpdate(HyperlinkEvent e) {\n\n                            if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {\n                                ToolWindowManager.getInstance(ProjectManager.getInstance().getOpenProjects()[0]).getToolWindow(\"FIR.im\").show(null);\n                            }\n\n                        }\n                    })\n                    .setFadeoutTime(4000)\n                    .createBalloon()\n                    .show(RelativePoint.getNorthEastOf(statusBar.getComponent()),\n                            Balloon.Position.atRight);\n\n        }\n\n    }\n\n    @Override\n    public void fileGenerated(String outputRoot, String relativePath) {\n\n    }\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/controller/ModulesManager.java",
    "content": "package ro.catalin.prata.firuploader.controller;\n\n\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.module.Module;\nimport com.intellij.openapi.module.ModuleManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.project.ProjectManager;\nimport com.intellij.psi.PsiClass;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.xml.XmlTag;\nimport org.jetbrains.android.dom.AndroidAttributeValue;\nimport org.jetbrains.android.dom.manifest.Manifest;\nimport org.jetbrains.android.facet.AndroidFacet;\nimport org.jetbrains.android.facet.AndroidRootUtil;\n\npublic class ModulesManager {\n\n    public static final String ANDROID_VERSION_CODE = \"android:versionCode\";\n    public static final String ANDROID_VERSION_NAME = \"android:versionName\";\n    public static final String ANDROID_APP_ID = \"package\";\n    public static final String ANDROID_APP_NAME = \"android:label\";\n    /**\n     * Manager's single instance\n     */\n    private static ModulesManager sInstance = null;\n\n    private ModulesManager() {\n\n    }\n\n    public static ModulesManager instance() {\n\n        if (sInstance == null) {\n            sInstance = new ModulesManager();\n        }\n\n        return sInstance;\n    }\n\n    /**\n     * Returns the given module's apk path\n     *\n     * @param module android module used to get the facet and the apk file path\n     * @return file path of the android apk for the given module\n     */\n    public String getAndroidApkPath(Module module) {\n\n        if (module == null || AndroidFacet.getInstance(module) == null) {\n            return null;\n        }\n\n        return AndroidRootUtil.getApkPath(AndroidFacet.getInstance(module));\n\n    }\n\n    /**\n     * return the given manifest Path\n     * @param module\n     * @return\n     */\n    public String getAndroidManifestPath(Module module) {\n\n        if (module == null || AndroidFacet.getInstance(module) == null) {\n            return null;\n        }\n\n        return AndroidRootUtil.getManifestFile(AndroidFacet.getInstance(module)).getPath();\n\n    }\n    /**\n     * Returns an array of module names for the current project\n     *\n     * @return array of module names\n     */\n    public String[] getAllModuleNames() {\n\n        Module[] modules = getModules();\n\n        if (modules == null) {\n            return null;\n        }\n\n        String[] moduleNames = new String[modules.length];\n\n        int index = 0;\n        for (Module module : modules) {\n\n            moduleNames[index] = module.getName();\n            index++;\n        }\n\n        return moduleNames;\n\n    }\n\n    /**\n     * Returns all modules found in the Main project,\n     * which is the first opened project if there are more than one projects opened at a time\n     *\n     * @return array of modules for the Main project\n     */\n    public Module[] getModules() {\n        Module[] modules = ModuleManager.getInstance(ProjectManager.getInstance().getOpenProjects()[0]).getSortedModules();\n        Module[] sortedModules = new Module[modules.length];\n\n        // used to go back from the last module to the first one\n        int reverseIndex = modules.length - 1;\n\n        // loop through all the modules and add the in a reverse order in the sorted array\n        for (int index = 0; index < modules.length; index++) {\n            sortedModules[index] = modules[reverseIndex];\n            reverseIndex--;\n        }\n\n        return sortedModules;\n    }\n\n    /**\n     * Returns the index of the selected module in the project modules list, if the module is not present here, 0 is returned\n     *\n     * @param moduleName module name\n     * @return 0 if the module is not in the list of the current project\n     */\n    public int getSelectedModuleIndex(String moduleName) {\n        Module[] modules = getModules();\n\n        if (modules == null || moduleName == null) {\n            return 0;\n        }\n\n        int index = 0;\n        for (Module module : modules) {\n\n            if (module.getName().equals(moduleName)) {\n                return index;\n            }\n\n            index++;\n        }\n\n        return 0;\n    }\n\n    /**\n     * Returns the module that is not(or the less) used by the other modules, this way we avoid getting library modules\n     *\n     * @return the most important module, which can be a non library module\n     */\n    public Module getMostImportantModule() {\n\n        Module[] modules = getModules();\n\n        if (modules == null) {\n            return null;\n        } else {\n\n            // the last one is the one that is not used by the other modules\n            // or is the most absent from the other modules dependency\n            return modules[0];\n\n        }\n\n    }\n\n    /**\n     * Returns a module from the Main project that has the given name\n     *\n     * @return module with the given name or null if not found in this project\n     */\n    public Module getModuleByName(String moduleName) {\n        Module[] modules = getModulesForProject(ProjectManager.getInstance().getOpenProjects()[0]);\n\n        if (modules == null) {\n            return null;\n        }\n\n        for (Module module : modules) {\n\n            if (module.getName().equals(moduleName)) {\n                return module;\n            }\n\n        }\n\n        return null;\n\n    }\n\n    /**\n     * Returns an array of module names for the current project\n     *\n     * @return array of module names\n     */\n    @Deprecated\n    public String[] getAllModuleNamesForCurrentProject(Project project) {\n\n        Module[] modules = getModulesForProject(project);\n        String[] moduleNames = new String[modules.length];\n\n        int index = 0;\n        for (Module module : modules) {\n\n            moduleNames[index] = module.getName();\n            index++;\n        }\n\n        return moduleNames;\n\n    }\n\n    /**\n     * Returns all modules found in the Main project,\n     * which is the first opened project if there are more than one projects opened at a time\n     *\n     * @return array of modules for the Main project\n     */\n    @Deprecated\n    public Module[] getModulesForProject(Project project) {\n        Module[] modules = ModuleManager.getInstance(project).getSortedModules();\n        Module[] sortedModules = new Module[modules.length];\n\n        // used to go back from the last module to the first one\n        int reverseIndex = modules.length - 1;\n\n        // loop through all the modules and add the in a reverse order in the sorted array\n        for (int index = 0; index < modules.length; index++) {\n            sortedModules[index] = modules[reverseIndex];\n            reverseIndex--;\n        }\n\n        return sortedModules;\n    }\n\n    /**\n     * Returns the index of the selected module in the project modules list, if the module is not present here, 0 is returned\n     *\n     * @param moduleName module name\n     * @return 0 if the module is not in the list of the current project\n     */\n    @Deprecated\n    public int getSelectedModuleIndexForProject(String moduleName, Project project) {\n        Module[] modules = getModulesForProject(project);\n\n        if (modules == null) {\n            return 0;\n        }\n\n        int index = 0;\n        for (Module module : modules) {\n\n            if (module.getName().equals(moduleName)) {\n                return index;\n            }\n\n            index++;\n        }\n\n        return 0;\n    }\n\n    /**\n     * Returns a module from the Main project that has the given name\n     *\n     * @return module with the given name or null if not found in this project\n     */\n    @Deprecated\n    public Module getModuleByName(String moduleName, Project project) {\n        Module[] modules = getModulesForProject(project);\n\n        if (modules == null) {\n            return null;\n        }\n\n        for (Module module : modules) {\n\n            if (module.getName().equals(moduleName)) {\n                return module;\n            }\n\n        }\n\n        return null;\n\n    }\n\n    /**\n     * Returns the module that is not(or the less) used by the other modules, this way we avoid getting library modules\n     *\n     * @return the most important module, which can be a non library module\n     */\n    @Deprecated\n    public Module getMostImportantModuleForProject(Project project) {\n\n        Module[] modules = getModulesForProject(project);\n\n        if (modules == null) {\n            return null;\n        } else {\n\n            // the last one is the one that is not used by the other modules\n            // or is the most absent from the other modules dependency\n            return modules[modules.length - 1];\n\n        }\n\n    }\n\n    /**\n     * Returns the manifest file for the given module\n     *\n     * @param module module to search the manifest document for\n     * @return manifest doc for the given module\n     */\n    public Manifest getManifestForModule(final Module module) {\n\n        if (module == null || AndroidFacet.getInstance(module) == null) {\n            return null;\n        }\n\n        return AndroidFacet.getInstance(module).getManifest();\n\n    }\n\n    /**\n     * Returns the build version name from the given manifest file\n     *\n     * @param manifest manifest file that will be searched for the build version name\n     * @return the current manifest version name value\n     */\n    public String getBuildVersionName(Manifest manifest) {\n\n        if (manifest == null || manifest.getXmlTag() == null\n                || manifest.getXmlTag().getAttribute(ANDROID_VERSION_NAME) == null) {\n            return null;\n        }\n\n        return manifest.getXmlTag().getAttribute(ANDROID_VERSION_NAME).getValue();\n    }\n\n    public String getAppId(Manifest manifest){\n        if (manifest == null || manifest.getXmlTag() == null\n                || manifest.getXmlTag().getAttribute(ANDROID_APP_ID) == null) {\n            return null;\n        }\n\n        return manifest.getXmlTag().getAttribute(ANDROID_APP_ID).getValue();\n    }\n\n    public String getAndroidAppName(Manifest manifest){\n        if (manifest == null || manifest.getXmlTag() == null\n                || manifest.getXmlTag().getAttribute(ANDROID_APP_NAME) == null) {\n            return null;\n        }\n\n\n        String name;\n        if(manifest!=null &&  manifest.getApplication()!=null && manifest.getApplication().getName()!=null){\n            AndroidAttributeValue<PsiClass> t = manifest.getApplication().getName();\n            if(t!=null) {\n                PsiClass valur = t.getValue();\n                if(valur!=null){\n                    name = valur.getName();\n                    return name;\n                }   else return null;\n            } else return null;\n\n\n        }  else return null    ;\n\n\n    }\n    /**\n     * Set the build version name in the given manifest file,\n     * note that this action is not performed in a write action environment so it should be called inside an\n     * ApplicationManager.getApplication().runWriteAction() method\n     *\n     * @param manifest manifest file that will be altered\n     * @param newValue new version name value\n     */\n    private void setBuildVersionName(Manifest manifest, String newValue) {\n\n        if (manifest != null && newValue != null && manifest.getXmlTag() != null\n                && manifest.getXmlTag().getAttribute(ANDROID_VERSION_NAME) != null) {\n            manifest.getXmlTag().getAttribute(ANDROID_VERSION_NAME).setValue(newValue);\n        }\n\n    }\n\n    /**\n     * Set the build version name and code in the manifest file asynchronously as it has to run in write mode\n     *\n     * @param manifest            manifest file to be altered\n     * @param newVersionNameValue the new version name value\n     * @param newVersionCodeValue the new version code value\n     * @param delegate            if != null, this will send callbacks on completion\n     */\n    public void setBuildVersionNameAndCode(final Manifest manifest, final String newVersionNameValue,\n                                           final String newVersionCodeValue, final ManifestChangesDelegate delegate) {\n\n        if (manifest == null) {\n            return;\n        }\n\n        // open a write action environment so we can update the manifest file\n        ApplicationManager.getApplication().runWriteAction(new Runnable() {\n            @Override\n            public void run() {\n\n                // set the version name value\n                setBuildVersionName(manifest, newVersionNameValue);\n                // set the version code value\n                setBuildVersionCode(manifest, newVersionCodeValue);\n\n                if (delegate != null) {\n\n                    // notify that the values were changed\n                    delegate.onVersionValueFinishedUpdate();\n\n                }\n\n            }\n        });\n\n    }\n\n    /**\n     * Set the build version code in the given manifest file,\n     * note that this action is not performed in a write action environment so it should be called inside an\n     * ApplicationManager.getApplication().runWriteAction() method\n     *\n     * @param manifest the project manifest file\n     * @param newValue the value to be set as the version code\n     */\n    private void setBuildVersionCode(Manifest manifest, String newValue) {\n\n        if (manifest != null && newValue != null && manifest.getXmlTag() != null\n                && manifest.getXmlTag().getAttribute(ANDROID_VERSION_CODE) != null) {\n            manifest.getXmlTag().getAttribute(ANDROID_VERSION_CODE).setValue(newValue);\n        }\n\n    }\n\n    /**\n     * Returns the code version of the given manifest file\n     *\n     * @param manifest android manifest dom object\n     * @return android build version code\n     */\n    public String getBuildVersionCode(Manifest manifest) {\n\n        if (manifest == null || manifest.getXmlTag() == null ||\n                manifest.getXmlTag().getAttribute(ANDROID_VERSION_CODE) == null) {\n            return null;\n        }\n\n        return manifest.getXmlTag().getAttribute(ANDROID_VERSION_CODE).getValue();\n    }\n\n    /**\n     * Returns the opened projects\n     *\n     * @return the opened projects\n     */\n    public Project[] getOpenedProjects() {\n\n        return ProjectManager.getInstance().getOpenProjects();\n\n    }\n\n    /**\n     * Returns a list of Strings containing the names of the opened projects\n     *\n     * @return list of opened projects names\n     */\n    public String[] getOpenedProjectsNames() {\n\n        Project[] projects = getOpenedProjects();\n\n        String[] projectsNames = new String[projects.length];\n\n        int index = 0;\n        for (Project project : projects) {\n            projectsNames[index] = project.getName();\n            index++;\n        }\n\n        return projectsNames;\n\n    }\n\n    /**\n     * Returns the project object that has the given name\n     *\n     * @param name project name to search for in the list of opened projects\n     * @return project object or null if no project was found with the given name\n     */\n    public Project getProjectByName(String name) {\n\n        if (name == null) {\n            return null;\n        }\n\n        Project projects[] = ProjectManager.getInstance().getOpenProjects();\n\n        for (Project project : projects) {\n            if (project.getName().equals(name)) {\n                return project;\n            }\n        }\n\n        return null;\n\n    }\n\n    /**\n     * Returns the index of the project that has the given name, the project name is searched in the list of opened projects\n     *\n     * @param name name of the project\n     * @return the index of the project in the list of opened projects, 0 if not found\n     */\n    public int getProjectIndexWithName(String name) {\n        int index = 0;\n        Project projects[] = ProjectManager.getInstance().getOpenProjects();\n\n        for (Project project : projects) {\n            if (project.getName().equals(name)) {\n                return index;\n            }\n            index++;\n        }\n\n        return 0;\n    }\n\n    /**\n     * Used to notify manifest changes updates as each write action needs to be done on a secondary thread\n     */\n    public interface ManifestChangesDelegate {\n\n        /**\n         * Called after the version name and code values were changed in the manifest file\n         */\n        public void onVersionValueFinishedUpdate();\n\n    }\n\n\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/provider/UploadService.java",
    "content": "package ro.catalin.prata.firuploader.provider;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.mime.content.FileBody;\nimport org.apache.http.entity.mime.content.InputStreamBody;\nimport org.apache.http.entity.mime.content.StringBody;\nimport org.apache.http.impl.client.DefaultHttpClient;\nimport org.apache.http.util.EntityUtils;\nimport org.json.JSONObject;\nimport ro.catalin.prata.firuploader.Model.Binary;\nimport ro.catalin.prata.firuploader.Model.CustomMultiPartEntity;\n\nimport java.io.File;\nimport java.nio.charset.Charset;\n\nimport ro.catalin.prata.firuploader.utils.SearchFile;\nimport ro.catalin.prata.firuploader.utils.UploadToRio;\nimport ro.catalin.prata.firuploader.utils.Utils;\nimport ro.catalin.prata.firuploader.view.Main;\n\n/**\n * 上传服务\n */\npublic class UploadService implements CustomMultiPartEntity.ProgressListener {\n\n\n    /**\n     * Used to notify the status of the upload action\n     */\n    public UploadServiceDelegate uploadServiceDelegate;\n    public CustomMultiPartEntity iconMultipartEntity;\n    public CustomMultiPartEntity multipartEntity;\n    public HttpPost post;\n    /**\n     * 向FIR上传文件\n     * @param url\n     * @param filePath\n     * @param apiToken\n     * @param binary\n     * @param appChanglog\n     * @param delegate\n     */\n    public void sendBuild(final String url, final String filePath, final String apiToken, final Binary binary,final String appChanglog, UploadServiceDelegate delegate) {\n\n        uploadServiceDelegate = delegate;\n\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                Main.getInstance().setTest(\"开始上传....\");\n                UploadToRio uploadToRio = new UploadToRio(binary.bundleId,apiToken,binary.name,binary.versionName,binary.versionCode,appChanglog)   ;\n\n                String url = uploadToRio.uploadTicket.binaryUploadUrl;\n                try {\n                    HttpClient client;\n                    client = new DefaultHttpClient();\n\n                    post = new HttpPost(url);\n\n                    Main.getInstance().setShortLink(\"http://fir.im/\"+uploadToRio.uploadTicket.appShort);\n\n                    /*****************************************upload icon***********************************************/\n                    SearchFile searchFile = new SearchFile(filePath);\n                    try {\n                        if(!binary.icon.isEmpty()){\n                            InputStreamBody iconToUpload = searchFile.query(binary.icon);\n                            iconMultipartEntity = new CustomMultiPartEntity(UploadService.this);\n                            // set the api token\n                            iconMultipartEntity.addPart(\"key\", new StringBody(uploadToRio.uploadTicket.iconKey));\n                            iconMultipartEntity.addPart(\"token\", new StringBody(uploadToRio.uploadTicket.iconToken));\n                            iconMultipartEntity.addPart(\"file\", iconToUpload);\n\n                            if (uploadServiceDelegate != null){\n                                // send the full package size\n                                uploadServiceDelegate.onPackageSizeComputed(iconMultipartEntity.getContentLength());\n                            } else{\n                                return ;\n                            }\n\n                            post.setEntity(iconMultipartEntity);\n\n                            // POST the build\n                            HttpResponse iconResponse = client.execute(post);\n                            HttpEntity iconEntity = iconResponse.getEntity();\n                            String iconResponseString = EntityUtils.toString(iconEntity, \"UTF-8\");\n                            System.out.println(iconResponseString);\n\n                            JSONObject iconJsonObject = new JSONObject(iconResponseString);\n\n                            if (iconResponse.getStatusLine().getStatusCode() == 200) {\n                                if (uploadServiceDelegate != null) {\n                                    // send success upload status\n                                    Main.getInstance().setTips(\"Icon upload success\");\n                                } else{\n                                    return;\n                                }\n\n                            }\n                            Main.getInstance().setTest(\"上传icon完成....\");\n                            searchFile.zipFile.close();\n                        }\n                    }catch (Exception e) {\n                        Utils.postErrorNoticeTOSlack(e);\n                    }\n\n                    /*****************************************upload file***********************************************/\n                    // get the apk file\n                    File fileToUpload = new File(filePath);\n\n                    multipartEntity = new CustomMultiPartEntity(UploadService.this);\n                    // set the api token\n                    multipartEntity.addPart(\"key\", new StringBody(uploadToRio.uploadTicket.binaryKey));\n                    multipartEntity.addPart(\"token\", new StringBody(uploadToRio.uploadTicket.binaryToken));\n                    multipartEntity.addPart(\"file\", new FileBody(fileToUpload));\n                    multipartEntity.addPart(\"x:name\",new StringBody(uploadToRio.appName, Charset.forName(\"UTF-8\")));\n                    multipartEntity.addPart(\"x:version\",new StringBody(uploadToRio.versionName, Charset.forName(\"UTF-8\")));\n                    multipartEntity.addPart(\"x:build\",new StringBody(uploadToRio.versionCode));\n                    multipartEntity.addPart(\"x:changelog\",new StringBody(uploadToRio.changeLog, Charset.forName(\"UTF-8\")));\n\n                    if (uploadServiceDelegate != null){\n                        // send the full package size\n                        uploadServiceDelegate.onPackageSizeComputed(multipartEntity.getContentLength());\n                    }\n\n                    post.setEntity(multipartEntity);\n\n                    // POST the build\n                    HttpResponse response = client.execute(post);\n                    HttpEntity entity = response.getEntity();\n                    String responseString = EntityUtils.toString(entity, \"UTF-8\");\n                    JSONObject jsonObject = new JSONObject(responseString);\n\n                    if (response.getStatusLine().getStatusCode() == 200) {\n                        if (uploadServiceDelegate != null) {\n                            // send success upload status\n                            uploadServiceDelegate.onUploadFinished(true);\n                        }\n\n                    } else {\n\n                        if (uploadServiceDelegate != null) {\n                            // send failed upload status\n                            uploadServiceDelegate.onUploadFinished(false);\n                        }\n\n                    }\n                    Main.getInstance().setTest(\"上传file完成....\");\n\n\n\n                } catch (Exception e) {\n                    // Ups! error occurred\n                    e.printStackTrace();\n                    Main.getInstance().setTest(\"e\"+e.getMessage());\n                    Utils.postErrorNoticeTOSlack(e);\n                    if (uploadServiceDelegate != null) {\n                        // send failed upload status\n                        uploadServiceDelegate.onUploadFinished(false);\n                    }\n                }\n\n            }\n        }).start();\n\n    }\n\n    public void iconUpload(){\n\n    }\n\n    @Override\n    public void transferred(long num) {\n\n        if (uploadServiceDelegate != null){\n            uploadServiceDelegate.onProgressChanged(num);\n        }\n\n    }\n\n    /**\n     * Upload service callback interface used to notify uploading actions like status or progress\n     */\n    public interface UploadServiceDelegate {\n\n        /**\n         * Called when the upload is done, even if an error occurred\n         *\n         * @param finishedSuccessful this flag is true if the upload was made successfully, false otherwise\n         */\n        public void onUploadFinished(boolean finishedSuccessful);\n\n        public void onPackageSizeComputed(long totalSize);\n\n        public void onProgressChanged(long progress);\n\n    }\n\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/AnalysisApk.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 14/12/17\n * Time: 下午8:48\n * To change this template use File | Settings | File Templates.\n */\n\nimport android.content.res.AXmlResourceParser;\nimport android.util.TypedValue;\nimport net.dongliu.apk.parser.ApkParser;\nimport net.dongliu.apk.parser.bean.ApkMeta;\nimport org.xmlpull.v1.XmlPullParser;\n\nimport java.io.*;\nimport java.util.Enumeration;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.Enumeration;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport java.util.jar.JarEntry;\n\nimport org.xmlpull.v1.XmlPullParser;\n\nimport android.content.res.AXmlResourceParser;\nimport android.util.TypedValue;\nimport ro.catalin.prata.firuploader.view.Main;\n\n/**\n * 分析APK文件，取得APK文件中的 包名、版本号及图标\n */\npublic class AnalysisApk {\n    /**\n     * 解压 zip 文件(apk可以当成一个zip文件)，注意不能解压 rar 文件哦，只能解压 zip 文件 解压 rar 文件 会出现\n     * java.io.IOException: Negative seek offset 异常 create date:2009- 6- 9\n     * author:Administrator\n     *\n     * @param apkUrl\n     *            zip 文件，注意要是正宗的 zip 文件哦，不能是把 rar 的直接改为 zip 这样会出现\n     *            java.io.IOException: Negative seek offset 异常\n     * @param logoUrl\n     *            图标生成的地址\n     * @throws java.io.IOException\n     */\n    public static String[] unZip(String apkUrl, String logoUrl){\n\n\n        String[] st = new String[3];\n        byte b[] = new byte[1024];\n        int length;\n        ZipFile zipFile;\n        try {\n            zipFile = new ZipFile(new File(apkUrl));\n            Enumeration<?> enumeration = zipFile.entries();\n            ZipEntry zipEntry = null;\n            while (enumeration.hasMoreElements()) {\n                zipEntry = (ZipEntry) enumeration.nextElement();\n                if (zipEntry.isDirectory()) {\n\n                } else {\n                    if (\"AndroidManifest.xml\".equals(zipEntry.getName())) {\n                        try {\n                            AXmlResourceParser parser = new AXmlResourceParser();\n                            parser.open(zipFile.getInputStream(zipEntry));\n                            while (true) {\n                                int type = parser.next();\n                                if (type == XmlPullParser.END_DOCUMENT) {\n                                    break;\n                                }\n                                switch (type) {\n                                    case XmlPullParser.START_TAG: {\n                                        for (int i = 0; i != parser.getAttributeCount(); ++i) {\n                                            if (\"versionName\".equals(parser.getAttributeName(i))) {\n                                                st[0] = getAttributeValue(parser, i);\n                                            } else if (\"package\".equals(parser.getAttributeName(i))) {\n                                                st[1] = getAttributeValue(parser, i);\n                                            } else if (\"versionCode\".equals(parser.getAttributeName(i))) {\n                                                st[2] = getAttributeValue(parser, i);\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                        } catch (Exception e) {\n                            Utils.postErrorNoticeTOSlack(e);\n                            e.printStackTrace();\n                        }\n                    }\n\n                    if (\"res/drawable-ldpi/icon.png\".equals(zipEntry.getName())) {\n                        OutputStream outputStream = new FileOutputStream(logoUrl);\n                        InputStream inputStream = zipFile.getInputStream(zipEntry);\n                        while ((length = inputStream.read(b)) > 0)\n                            outputStream.write(b, 0, length);\n                    }\n                }\n            }\n            zipFile.close();\n        } catch (IOException e) {\n            Utils.postErrorNoticeTOSlack(e);\n        }\n        return st;\n    }\n\n    private static String getAttributeValue(AXmlResourceParser parser, int index) {\n        int type = parser.getAttributeValueType(index);\n        int data = parser.getAttributeValueData(index);\n        if (type == TypedValue.TYPE_STRING) {\n            return parser.getAttributeValue(index);\n        }\n        if (type == TypedValue.TYPE_ATTRIBUTE) {\n            return String.format(\"?%s%08X\", getPackage(data), data);\n        }\n        if (type == TypedValue.TYPE_REFERENCE) {\n            return String.format(\"@%s%08X\", getPackage(data), data);\n        }\n        if (type == TypedValue.TYPE_FLOAT) {\n            return String.valueOf(Float.intBitsToFloat(data));\n        }\n        if (type == TypedValue.TYPE_INT_HEX) {\n            return String.format(\"0x%08X\", data);\n        }\n        if (type == TypedValue.TYPE_INT_BOOLEAN) {\n            return data != 0 ? \"true\" : \"false\";\n        }\n        if (type == TypedValue.TYPE_DIMENSION) {\n            return Float.toString(complexToFloat(data)) + DIMENSION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];\n        }\n        if (type == TypedValue.TYPE_FRACTION) {\n            return Float.toString(complexToFloat(data)) + FRACTION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];\n        }\n        if (type >= TypedValue.TYPE_FIRST_COLOR_INT && type <= TypedValue.TYPE_LAST_COLOR_INT) {\n            return String.format(\"#%08X\", data);\n        }\n        if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) {\n            return String.valueOf(data);\n        }\n        return String.format(\"<0x%X, type 0x%02X>\", data, type);\n    }\n\n    private static String getPackage(int id) {\n        if (id >>> 24 == 1) {\n            return \"android:\";\n        }\n        return \"\";\n    }\n\n    // ///////////////////////////////// ILLEGAL STUFF, DONT LOOK :)\n    public static float complexToFloat(int complex) {\n        return (float) (complex & 0xFFFFFF00) * RADIX_MULTS[(complex >> 4) & 3];\n    }\n\n    private static final float RADIX_MULTS[] = { 0.00390625F, 3.051758E-005F, 1.192093E-007F, 4.656613E-010F };\n    private static final String DIMENSION_UNITS[] = { \"px\", \"dip\", \"sp\", \"pt\", \"in\", \"mm\", \"\", \"\" };\n    private static final String FRACTION_UNITS[] = { \"%\", \"%p\", \"\", \"\", \"\", \"\", \"\", \"\" };\n}\n\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/Constants.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\n/**\n * 常量\n */\npublic class Constants {\n\n    /**\n     * The persistence file name\n     */\n    public static final String PERSISTENCE_FILE_NAME = \"fir_im_uploader_persist.xml\";\n\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/ParseXML.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.SAXException;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 14/12/17\n * Time: 下午9:14\n * To change this template use File | Settings | File Templates.\n */\npublic class ParseXML {\n    public static String parseAppName(String url){\n        if(url == null || url.equals(\"\")){\n            return null;\n        }\n        String appName = null;\n        try {\n            //得到DOM解析器的工厂实例\n            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();\n            //从DOM工厂中获得DOM解析器\n            DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();\n            //声明为File为了识别中文名\n            Document doc = null;\n            doc = dbBuilder.parse(url);\n\n            //得到文档名称为Student的元素的节点列表\n            NodeList list = doc.getElementsByTagName(\"application\");\n\n            //遍历该集合，显示结合中的元素及其子元素的名字\n            for(int i = 0; i< list.getLength() ; i ++){\n                Element element = (Element)list.item(i);\n                String name=element.getAttribute(\"android:label\");\n                if(name.indexOf(\"@\")>=0){\n                   appName =  getVal(url,name.substring(name.indexOf(\"/\")+1,name.length()));\n                } else{\n                   appName = name;\n                }\n\n            }\n        } catch (SAXException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        } catch (IOException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        } catch (ParserConfigurationException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        }\n\n\n        return appName;\n    }\n\n    public static String getVal(String url,String _name){\n        File file = new File(url);\n        String folderPath = file.getParent();\n        File cuFile = new File(folderPath);\n        File[] files = cuFile.listFiles();\n        String value = \"\" ;\n        double le = files.length;\n        for (int i = 0; i < files.length; i++) {\n            if(!files[i].isDirectory()){\n                continue;\n            }\n            System.out.println(\"name:::\"+files[i].getName());\n            if(files[i].getName().equals(\"res\")){\n               File[] resFile =  files[i].listFiles();\n                for (int j = 0; j < resFile.length; j++) {\n                    if(!resFile[i].isDirectory()){\n                        continue;\n                    }\n                    if(resFile[j].getName().equals(\"values\")){\n                        File[] valueFiles =  resFile[j].listFiles();\n                        for (int k = 0; k < valueFiles.length; k++) {\n                            if(valueFiles[k].getName().equals(\"strings.xml\")){\n                                File xmlFile = valueFiles[k];\n                               value =  getAppName(xmlFile.getAbsolutePath(),_name) ;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return value;\n    }\n\n    public static String getAppName(String url,String _name){\n        String appName = null;\n        try {\n            //得到DOM解析器的工厂实例\n            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();\n            //从DOM工厂中获得DOM解析器\n            DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();\n            //声明为File为了识别中文名\n            Document doc = null;\n            doc = dbBuilder.parse(url);\n\n            //得到文档名称为Student的元素的节点列表\n            NodeList list = doc.getElementsByTagName(\"string\");\n\n            //遍历该集合，显示结合中的元素及其子元素的名字\n            for(int i = 0; i< list.getLength() ; i ++){\n                Element element = (Element)list.item(i);\n                String name=element.getAttribute(\"name\");\n                if(name.equals(_name)){\n                    appName = element.getFirstChild().getNodeValue();\n                    break;\n                }\n            }\n        } catch (SAXException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        } catch (IOException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        } catch (ParserConfigurationException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        }\n\n\n        return appName;\n    }\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/SearchFile.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\nimport android.content.res.AXmlResourceParser;\nimport org.apache.http.entity.mime.content.InputStreamBody;\nimport org.xmlpull.v1.XmlPullParser;\nimport ro.catalin.prata.firuploader.view.Main;\n\nimport java.io.*;\nimport java.util.Enumeration;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/10\n * Time: 下午6:43\n * To change this template use File | Settings | File Templates.\n */\npublic class SearchFile {\n    public String url;\n    public File iconFile;\n    public ZipFile zipFile;\n\n    public SearchFile(String url){\n        this.url = url;\n    }\n    public  InputStreamBody query(String name){\n\n        int length = 0;\n        byte b[] = new byte[1024];\n        InputStreamBody fileContent = null;\n        try {\n//            Utils.postSuccessNoticeToSlack(url);\n            zipFile = new ZipFile(new File(url));\n            Enumeration<?> enumeration = zipFile.entries();\n            ZipEntry zipEntry = null;\n\n            while (enumeration.hasMoreElements()) {\n                zipEntry = (ZipEntry) enumeration.nextElement();\n                if (zipEntry.isDirectory()) {\n\n                } else {\n\n                    if (name.equals(zipEntry.getName())) {\n//                        Utils.postSuccessNoticeToSlack(name);\n//                        iconFile = new File(\"aa.png\");\n//                        if(!iconFile.exists()){\n//                           iconFile.createNewFile();\n//                        }\n//                        FileOutputStream fop = new FileOutputStream(iconFile);\n\n\n//                        OutputStream outputStream = new FileOutputStream(name);\n                        InputStream inputStream = zipFile.getInputStream(zipEntry);\n                        fileContent=new InputStreamBody(inputStream,name);\n//                        while ((length = inputStream.read(b)) > 0)\n//                            fop.write(b, 0, length);\n//\n//                        fop.flush();\n//                        fop.close();\n\n\n                    }\n\n                }\n            }\n        } catch (IOException e) {\n            Utils.postErrorNoticeTOSlack(e);\n        }\n        return fileContent;\n    }\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/TimerScan.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\nimport ro.catalin.prata.firuploader.view.Main;\n\nimport java.util.Date;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/12\n * Time: 下午11:38\n * To change this template use File | Settings | File Templates.\n */\npublic class TimerScan {\n    Timer timer;\n    long Interval = 30000;\n    long delay = 30000;\n\n    public TimerScan(){\n        timer = new Timer();\n        timer.schedule(new TimerScanTask(), delay, Interval);\n\n    }\n\n}\n\n\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/TimerScanTask.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\nimport ro.catalin.prata.firuploader.controller.KeysManager;\nimport ro.catalin.prata.firuploader.view.Main;\n\nimport javax.swing.*;\nimport java.util.Date;\nimport java.util.TimerTask;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/12\n * Time: 下午11:40\n * To change this template use File | Settings | File Templates.\n */\npublic class TimerScanTask  extends TimerTask {\n\n\n    @Override\n    public void run() {\n        Date date = new Date(this.scheduledExecutionTime());\n        System.out.println(\"本次执行该线程的时间为：\" + date);\n\n\n        String path = null;\n\n        if(Main.getInstance().binary.filePath.isEmpty()){\n            System.out.println(\"本次执行该线程的时间为：1\" + date);\n            return;\n        }\n        path = Main.getInstance().binary.filePath;\n        System.out.println(\"本次执行该线程的时间为：2\" + date);\n        String md5 = Utils.getMd5(path);\n        if(!md5.equals(KeysManager.instance().getMd5())) {\n            if(\"yes\".equals(KeysManager.instance().getUploadFlag())){\n               System.out.println(\"自动检测上传\");\n               Main.getInstance().uploadBuild();\n               return;\n            }\n            if(\"cancel\".equals(KeysManager.instance().getFlag())){\n                System.out.println(\"取消了自动检测提示\");\n                return;\n            }else{\n                System.out.println(\"本次执行该线程的时间为：3\" + date);\n                SwingUtilities.invokeLater(new Runnable() {\n                    public void run() {\n                        Tips.showMD5ChangedUploadTips();\n                    }\n//            Main.getInstance().uploadFinishNotice();\n                });\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/Tips.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\nimport com.intellij.openapi.project.ProjectManager;\nimport com.intellij.openapi.ui.MessageType;\nimport com.intellij.openapi.ui.popup.Balloon;\nimport com.intellij.openapi.ui.popup.JBPopupFactory;\nimport com.intellij.openapi.wm.StatusBar;\nimport com.intellij.openapi.wm.ToolWindowManager;\nimport com.intellij.openapi.wm.WindowManager;\nimport com.intellij.ui.awt.RelativePoint;\nimport ro.catalin.prata.firuploader.controller.KeysManager;\n\nimport javax.swing.event.HyperlinkEvent;\nimport javax.swing.event.HyperlinkListener;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/13\n * Time: 下午6:23\n * To change this template use File | Settings | File Templates.\n */\npublic class Tips {\n\n    public static void showCustomTips(){\n\n    }\n\n    public static void showBuildFinishUploadTips(){\n        StatusBar statusBar = WindowManager.getInstance()\n                .getStatusBar(ProjectManager.getInstance().getOpenProjects()[0]);\n\n\n        JBPopupFactory.getInstance()\n                .createHtmlTextBalloonBuilder(\"编译完毕发送到FIR.im, <a href='open'>点击</a> 打开FIR.im uploader 并上传.\",\n                        MessageType.INFO, new HyperlinkListener() {\n                    @Override\n                    public void hyperlinkUpdate(HyperlinkEvent e) {\n\n                        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {\n                            ToolWindowManager.getInstance(ProjectManager.getInstance().getOpenProjects()[0]).getToolWindow(\"FIR.im\").show(null);\n                        }\n\n                    }\n                })\n                .setFadeoutTime(4000)\n                .createBalloon()\n                .show(RelativePoint.getNorthEastOf(statusBar.getComponent()),\n                        Balloon.Position.atRight);\n    }\n\n    public static void  showMD5ChangedUploadTips(){\n        StatusBar statusBar = WindowManager.getInstance()\n                .getStatusBar(ProjectManager.getInstance().getOpenProjects()[0]);\n\n\n        JBPopupFactory.getInstance()\n                .createHtmlTextBalloonBuilder(\"<p style='font-size:12px'>检测到apk文件改变了还没有上传, <a style='font-size:12px' href='http://fir.im/androidStudio/open'>点击</a>上传吧.</p></br><p><a style='font-size:12px' href='http://fir.im/androidStudio/cancel'>取消提示</a></p>\",\n                        MessageType.INFO, new HyperlinkListener() {\n                    @Override\n                    public void hyperlinkUpdate(HyperlinkEvent e) {\n\n                        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {\n                            if(e.getURL().toString().equals(\"http://fir.im/androidStudio/open\")) {\n                                ToolWindowManager.getInstance(ProjectManager.getInstance().getOpenProjects()[0]).getToolWindow(\"FIR.im\").show(null);\n                            }\n                            if(e.getURL().toString().equals(\"http://fir.im/androidStudio/cancel\")) {\n                                System.out.println(e.getURL().toString()) ;\n                                KeysManager.instance().setFlag(\"cancel\");\n                            }\n                        }\n\n                    }\n                })\n\n                .setFadeoutTime(6000)\n                .setCloseButtonEnabled(true)\n                .createBalloon()\n                .show(RelativePoint.getNorthEastOf(statusBar.getComponent()),\n                        Balloon.Position.atRight);\n    }\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/UploadToRio.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\nimport org.apache.commons.httpclient.NameValuePair;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.entity.UrlEncodedFormEntity;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.impl.client.DefaultHttpClient;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.apache.http.util.EntityUtils;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport ro.catalin.prata.firuploader.Model.UploadTicket;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/7\n * Time: 下午1:48\n * To change this template use File | Settings | File Templates.\n */\npublic class UploadToRio {\n    public static final String FIR_UPLOAD_TOKEN_URL = \"http://fir.im/api/v2/info/\" ;\n    public static final String FIR_UPDATE_APP_INFO = \"http://fir.im/api/v2/app/\"  ;\n    public static final String FIR_BASE_URL = \"http://api.fir.im\" ;\n\n    public void setVersionId(String versionId) {\n        this.versionId = versionId;\n    }\n\n    public  String versionId;\n    public String token;\n    public String appName;\n    public String appId;\n    public String versionName;\n    public String versionCode;\n    public String changeLog;\n    public String appShort;\n    public String appOid;\n    public UploadTicket uploadTicket;\n\n    public UploadToRio(String appId,String token,String appName,String versionName,String versionCode,String changeLog){\n        this.appId = appId;\n        this.appName = appName;\n        this.versionCode = versionCode;\n        this.changeLog = changeLog;\n        this.versionName = versionName;\n        this.token = token;\n\n        uploadTicket = new UploadTicket(getUploadToken()) ;\n\n    }\n\n    /**\n     * 获取上传token\n     * @return   JSONObject\n     */\n    public JSONObject getUploadToken(){\n        try {\n            HttpClient httpClient = new DefaultHttpClient() ;\n            String url = FIR_BASE_URL + \"/apps\" ;\n            String type = \"android\";\n            HttpPost post = new HttpPost(url);\n            List<NameValuePair> parameters ;\n\n            ArrayList<BasicNameValuePair> postParameters = new ArrayList<BasicNameValuePair>();\n            postParameters.add(new BasicNameValuePair(\"type\", type));\n            postParameters.add(new BasicNameValuePair(\"bundle_id\", this.appId));\n            postParameters.add(new BasicNameValuePair(\"api_token\",this.token));\n            post.setEntity(new UrlEncodedFormEntity(postParameters, \"UTF-8\"));\n            HttpResponse response = null;\n            response = httpClient.execute(post) ;\n            HttpEntity entity = response.getEntity();\n            String responseString = EntityUtils.toString(entity, \"UTF-8\");\n\n            System.out.println(responseString);\n            JSONObject jo;\n            jo = new JSONObject(responseString);\n            return jo;\n\n        } catch (UnsupportedEncodingException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        }  catch (IOException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        }  catch (JSONException e) {\n            Utils.postErrorNoticeTOSlack(e);\n            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n        }\n        return new JSONObject();\n    }\n\n    /**\n     * 上传Binary\n     * @return\n     */\n    public JSONObject uploadBinary(){\n\n        return new JSONObject();\n    }\n\n    /**\n     * 上传icon\n     * @return\n     */\n    public JSONObject uploadIcon(){\n        return new JSONObject() ;\n    }\n\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/utils/Utils.java",
    "content": "package ro.catalin.prata.firuploader.utils;\n\nimport com.intellij.openapi.fileChooser.FileChooserDescriptor;\nimport com.intellij.openapi.fileTypes.FileType;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.entity.UrlEncodedFormEntity;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.client.methods.HttpPut;\nimport org.apache.http.impl.client.DefaultHttpClient;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.apache.http.util.EntityUtils;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport ro.catalin.prata.firuploader.controller.KeysManager;\nimport ro.catalin.prata.firuploader.view.Main;\n\nimport java.io.*;\nimport java.math.BigInteger;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.security.MessageDigest;\nimport java.util.ArrayList;\nimport java.util.Locale;\n\n\npublic class Utils {\n\n    /**\n     * Checks if the given string is null and if it is, it returns an empty string\n     *\n     * @param string can be null\n     * @return empty string if the string is null, the passed string otherwise\n     */\n    public static String validateString(String string) {\n        if (string == null) {\n            return \"\";\n        } else {\n            return string;\n        }\n    }\n\n    /**\n     * Used to build a FileChooserDescriptor object with the given FileType and has the following options enabled:\n     * - controls whether files can be chosen\n     * - controls whether folders can be chosen\n     * - controls whether .jar files can be chosen\n     * - controls whether .jar files will be returned as files or as folders\n     *\n     * @param fileType the FileType used for the filtering\n     * @return new FileChooserDescriptor\n     */\n    public static FileChooserDescriptor createSingleFileDescriptor(final FileType fileType) {\n        return new FileChooserDescriptor(true, true, true, true, false, false) {\n            @Override\n            public boolean isFileVisible(final VirtualFile file, final boolean showHiddenFiles) {\n                return file.isDirectory() || file.getFileType() == fileType;\n            }\n\n            @Override\n            public boolean isFileSelectable(final VirtualFile file) {\n                return super.isFileSelectable(file) && file.getFileType() == fileType;\n            }\n        };\n    }\n\n    /**\n     * 发送slack通知\n     * @param msg\n     */\n    public static void postNoticeTOSlack(final String msg){\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                HttpClient httpClient = new DefaultHttpClient() ;\n                String postUrl = \"https://hooks.slack.com/services/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\"  ;\n                HttpPost httppost = new HttpPost(postUrl);\n                HttpResponse response = null;\n                try {\n                    ArrayList<BasicNameValuePair> postParameters = new ArrayList<BasicNameValuePair>();\n                    JSONObject obj = new JSONObject();\n                    obj.append(\"text\",msg) ;\n                    postParameters.add(new BasicNameValuePair(\"payload\", obj.toString().replace(\"[\",\"\").replace(\"]\",\"\")));\n                    httppost.setEntity(new UrlEncodedFormEntity(postParameters,\"UTF-8\"));\n                } catch (UnsupportedEncodingException e) {\n                    Utils.postErrorNoticeTOSlack(e);\n                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n                } catch (JSONException e) {\n                    Utils.postErrorNoticeTOSlack(e);\n                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n                }\n                try {\n                    response = httpClient.execute(httppost);\n                    HttpEntity entity = response.getEntity();\n                    String responseString = EntityUtils.toString(entity, \"UTF-8\");\n\n                } catch (IOException e) {\n                    Utils.postErrorNoticeTOSlack(e);\n                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n                }\n            }\n        }).start();\n    }\n\n    public static void postSuccessNoticeToSlack(final String msg){\n        postNoticeTOSlack(\"#AndroidStudio#success#\"+msg);\n    }\n\n    public static void postErrorNoticeTOSlack(final Exception e){\n        StringWriter writer = new StringWriter();\n        e.printStackTrace(new PrintWriter(writer,true));\n\n        postNoticeTOSlack(\"#AndroidStudio#Error#\"+writer.toString());\n\n    }\n\n    public static void local(){\n        Locale locale = Locale.getDefault();\n        System.out.println(locale.getLanguage());\n        System.out.println(locale.getCountry());\n\n        String language = locale.getLanguage();\n        String country = locale.getCountry();\n    }\n\n    /**\n     * 国际化判断\n     * @return\n     */\n    public static boolean isZh(){\n       Locale locale = Locale.getDefault();\n       String language = locale.getLanguage();\n       if(KeysManager.instance().getLanguage() == null) {\n           if(language.equals(\"zh\")){\n               return true;\n           }else{\n               return false ;\n           }\n       }\n       if(!KeysManager.instance().getLanguage().isEmpty()) {\n           if(KeysManager.instance().getLanguage().equals(\"chinese\")){\n               return true;\n           }else{\n               return false;\n           }\n       }\n\n        return true;\n\n    }\n\n    /**\n     * 获取文件的MD5\n     * @param path\n     * @return\n     */\n    public static String getMd5(String path){\n        String value = null;\n        try {\n            MessageDigest md = MessageDigest.getInstance(\"MD5\");\n            FileInputStream fis = new FileInputStream(path);\n\n            byte[] dataBytes = new byte[1024];\n\n            int nRead = 0;\n            while ((nRead = fis.read(dataBytes)) != -1) {\n                md.update(dataBytes, 0, nRead);\n            };\n            byte[] mBytes = md.digest();\n            StringBuffer sb = new StringBuffer();\n            for (int i = 0; i < mBytes.length; i++) {\n                sb.append(Integer.toString((mBytes[i] & 0xff) + 0x100, 16).substring(1));\n            }\n            System.out.println(\"Digest(in hex format):: \" + sb.toString());\n            value = sb.toString();\n            fis.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n\n        }\n        return value;\n    }\n\n}\n\n\n\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/view/Main.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"ro.catalin.prata.firuploader.view.Main\">\n  <grid id=\"27dc6\" binding=\"panel1\" default-binding=\"true\" layout-manager=\"BorderLayout\" hgap=\"0\" vgap=\"0\">\n    <constraints>\n      <xy x=\"20\" y=\"20\" width=\"585\" height=\"817\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <grid id=\"18eab\" layout-manager=\"FlowLayout\" hgap=\"5\" vgap=\"5\" flow-align=\"1\">\n        <constraints border-constraint=\"North\"/>\n        <properties>\n          <background color=\"-14308151\"/>\n          <foreground color=\"-6237205\"/>\n        </properties>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"4c71f\" class=\"javax.swing.JLabel\" binding=\"formHeader\">\n            <constraints/>\n            <properties>\n              <font size=\"20\"/>\n              <foreground color=\"-1\"/>\n              <text value=\"FIR.im 一键上传\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n      <grid id=\"956e8\" layout-manager=\"GridLayoutManager\" row-count=\"16\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints border-constraint=\"Center\"/>\n        <properties>\n          <background color=\"-1\"/>\n          <toolTipText value=\"\"/>\n        </properties>\n        <border type=\"empty\">\n          <font/>\n          <title-color color=\"-16777216\"/>\n        </border>\n        <children>\n          <component id=\"8c34\" class=\"javax.swing.JLabel\" binding=\"formProject\">\n            <constraints>\n              <grid row=\"5\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"16\"/>\n              <foreground color=\"-6710887\"/>\n              <text value=\"PROJECT\"/>\n            </properties>\n          </component>\n          <component id=\"2b66e\" class=\"javax.swing.JLabel\" binding=\"formPath\">\n            <constraints>\n              <grid row=\"7\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"16\"/>\n              <foreground color=\"-6710887\"/>\n              <text value=\"APK PATH\"/>\n            </properties>\n          </component>\n          <component id=\"88db6\" class=\"javax.swing.JLabel\" binding=\"formToken\">\n            <constraints>\n              <grid row=\"3\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"16\"/>\n              <foreground color=\"-6710887\"/>\n              <text value=\"TOKEN\"/>\n            </properties>\n            <clientProperties>\n              <html.disable class=\"java.lang.Boolean\" value=\"false\"/>\n            </clientProperties>\n          </component>\n          <component id=\"a45d9\" class=\"javax.swing.JLabel\" binding=\"formLink\">\n            <constraints>\n              <grid row=\"10\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"16\"/>\n              <foreground color=\"-6710887\"/>\n              <text value=\"SHORT LINK\"/>\n            </properties>\n          </component>\n          <component id=\"734c\" class=\"javax.swing.JLabel\" binding=\"formLog\">\n            <constraints>\n              <grid row=\"12\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"16\"/>\n              <foreground color=\"-6710887\"/>\n              <text value=\"CHANGE LOG\"/>\n            </properties>\n          </component>\n          <grid id=\"df222\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"12\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"d6a2\" class=\"javax.swing.JTextArea\" binding=\"changeLogTa\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\">\n                    <preferred-size width=\"230\" height=\"100\"/>\n                  </grid>\n                </constraints>\n                <properties>\n                  <background color=\"-724752\"/>\n                  <lineWrap value=\"true\"/>\n                  <toolTipText value=\"更新日志..\"/>\n                  <wrapStyleWord value=\"true\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <hspacer id=\"e94dd\">\n            <constraints>\n              <grid row=\"4\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <hspacer id=\"5172b\">\n            <constraints>\n              <grid row=\"6\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <hspacer id=\"1d19\">\n            <constraints>\n              <grid row=\"9\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <hspacer id=\"b2c93\">\n            <constraints>\n              <grid row=\"11\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <grid id=\"ad92\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"3\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"c2649\" class=\"javax.swing.JButton\" binding=\"setTokenBtn\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                    <preferred-size width=\"130\" height=\"30\"/>\n                  </grid>\n                </constraints>\n                <properties>\n                  <background color=\"-14308151\"/>\n                  <text value=\"Setting\"/>\n                </properties>\n              </component>\n              <component id=\"28bb4\" class=\"javax.swing.JLabel\" binding=\"formHelp\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <font size=\"22\" style=\"1\"/>\n                  <foreground color=\"-13369396\"/>\n                  <text value=\"?         \"/>\n                  <toolTipText value=\"需要帮助？\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <grid id=\"c357d\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"5\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"40b5b\" class=\"javax.swing.JComboBox\" binding=\"projectCombo\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"2\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                    <preferred-size width=\"130\" height=\"30\"/>\n                  </grid>\n                </constraints>\n                <properties/>\n              </component>\n            </children>\n          </grid>\n          <grid id=\"9c385\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"7\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"ce44c\" class=\"javax.swing.JButton\" binding=\"settingBtn\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                    <preferred-size width=\"130\" height=\"30\"/>\n                  </grid>\n                </constraints>\n                <properties>\n                  <text value=\"Choose Path\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <grid id=\"ded29\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"10\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n              <font size=\"16\"/>\n              <toolTipText value=\"\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"b72ca\" class=\"javax.swing.JLabel\" binding=\"shortLink\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <font size=\"16\"/>\n                  <foreground color=\"-16737895\"/>\n                  <text value=\"\"/>\n                  <toolTipText value=\"点击查看\"/>\n                </properties>\n              </component>\n              <component id=\"e2fef\" class=\"javax.swing.JLabel\" binding=\"qrCodeLabel\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <horizontalAlignment value=\"0\"/>\n                  <horizontalTextPosition value=\"4\"/>\n                  <text value=\"查看二维码\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <component id=\"5a770\" class=\"javax.swing.JLabel\" binding=\"apkPath\">\n            <constraints>\n              <grid row=\"8\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"9\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"11\"/>\n              <text value=\"\"/>\n            </properties>\n          </component>\n          <grid id=\"19d19\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children/>\n          </grid>\n          <grid id=\"2dfe2\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"1e68\" class=\"javax.swing.JButton\" binding=\"chinese\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"Chinese\"/>\n                </properties>\n              </component>\n              <component id=\"ff763\" class=\"javax.swing.JButton\" binding=\"english\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"English\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <grid id=\"4dd9d\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children/>\n          </grid>\n          <grid id=\"bd72f\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"13\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"d75c4\" class=\"javax.swing.JCheckBox\" binding=\"formTipCB\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"\"/>\n                  <toolTipText value=\"检测文件改变是否提示\"/>\n                </properties>\n              </component>\n              <hspacer id=\"55f1f\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n              </hspacer>\n            </children>\n          </grid>\n          <grid id=\"77f4\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"14\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"aeca6\" class=\"javax.swing.JCheckBox\" binding=\"formUploadCB\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"\"/>\n                  <toolTipText value=\"自动上传功能，如果选中发现文件改变自己会上传\"/>\n                </properties>\n              </component>\n              <component id=\"8df8c\" class=\"javax.swing.JButton\" binding=\"cancelUploadButton\" default-binding=\"true\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"cancelUpload\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <grid id=\"f9dae\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"15\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children/>\n          </grid>\n          <component id=\"56fe7\" class=\"javax.swing.JLabel\" binding=\"formTip\">\n            <constraints>\n              <grid row=\"13\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"16\"/>\n              <foreground color=\"-6710887\"/>\n              <text value=\"CHECK &amp; TIP\"/>\n              <toolTipText value=\"检测文件改变是否提示\"/>\n            </properties>\n          </component>\n          <component id=\"ef691\" class=\"javax.swing.JLabel\" binding=\"formUpload\">\n            <constraints>\n              <grid row=\"14\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"16\"/>\n              <foreground color=\"-6710887\"/>\n              <text value=\"AUTO UPLOAD\"/>\n              <toolTipText value=\"自动上传功能，如果选中发现文件改变自己会上传\"/>\n            </properties>\n          </component>\n          <component id=\"58176\" class=\"javax.swing.JLabel\" binding=\"languageLabel\">\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"16\"/>\n              <foreground color=\"-6710887\"/>\n              <text value=\"LANGUAGE\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n      <grid id=\"f7964\" layout-manager=\"GridLayoutManager\" row-count=\"6\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"20\" left=\"0\" bottom=\"20\" right=\"0\"/>\n        <constraints border-constraint=\"South\"/>\n        <properties>\n          <background color=\"-1\"/>\n        </properties>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"2f49a\" class=\"javax.swing.JButton\" binding=\"uploadBtn\">\n            <constraints>\n              <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"240\" height=\"45\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <background color=\"-14308151\"/>\n              <text value=\"Upload\"/>\n            </properties>\n          </component>\n          <component id=\"5b34e\" class=\"javax.swing.JProgressBar\" binding=\"progressBar\">\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n          </component>\n          <component id=\"4ae56\" class=\"javax.swing.JLabel\" binding=\"tips\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"18\"/>\n              <foreground color=\"-52429\"/>\n              <text value=\"Success\"/>\n            </properties>\n          </component>\n          <grid id=\"1fb60\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"4\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children/>\n          </grid>\n          <grid id=\"c96d0\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"5\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children/>\n          </grid>\n          <grid id=\"bd011\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"3\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <background color=\"-1\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children/>\n          </grid>\n        </children>\n      </grid>\n    </children>\n  </grid>\n</form>\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/view/Main.java",
    "content": "package ro.catalin.prata.firuploader.view;\n\nimport com.intellij.openapi.fileChooser.FileChooser;\nimport com.intellij.openapi.fileChooser.FileChooserDescriptor;\nimport com.intellij.openapi.fileTypes.FileType;\nimport com.intellij.openapi.fileTypes.FileTypeManager;\nimport com.intellij.openapi.module.Module;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.project.ProjectManager;\nimport com.intellij.openapi.project.ProjectManagerListener;\nimport com.intellij.openapi.ui.MessageType;\nimport com.intellij.openapi.ui.Messages;\nimport com.intellij.openapi.ui.popup.Balloon;\nimport com.intellij.openapi.ui.popup.JBPopupFactory;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.openapi.wm.*;\nimport com.intellij.ui.awt.RelativePoint;\nimport com.intellij.ui.content.Content;\nimport com.intellij.ui.content.ContentFactory;\nimport ro.catalin.prata.firuploader.Model.Binary;\nimport ro.catalin.prata.firuploader.controller.KeysManager;\nimport ro.catalin.prata.firuploader.controller.ModulesManager;\nimport ro.catalin.prata.firuploader.provider.UploadService;\nimport ro.catalin.prata.firuploader.utils.TimerScan;\nimport ro.catalin.prata.firuploader.utils.Utils;\n\nimport javax.swing.*;\nimport javax.swing.event.HyperlinkEvent;\nimport javax.swing.event.HyperlinkListener;\nimport java.awt.*;\nimport java.awt.event.*;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.List;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 14/12/15\n * Time: 下午7:39\n * To change this template use File | Settings | File Templates.\n */\n public class Main implements ToolWindowFactory , UploadService.UploadServiceDelegate{\n    private JPanel panel1;\n    private JButton setTokenBtn;\n    private JComboBox projectCombo;\n    private JLabel apkPath;\n    private JLabel shortLink;\n    private JTextArea changeLogTa;\n    private JButton uploadBtn;\n    private JProgressBar progressBar;\n    private JButton settingBtn;\n    private JLabel tips;\n    private JLabel formHeader;\n    private JLabel formToken;\n    private JLabel formProject;\n    private JLabel formPath;\n    private JLabel formLink;\n    private JLabel formLog;\n    private JLabel formHelp;\n    private JLabel formTip;\n    private JCheckBox formTipCB;\n    private JLabel formUpload;\n    private JCheckBox formUploadCB;\n    private JLabel qrCodeLabel;\n    private JButton cancelUploadButton;\n    private JLabel languageLabel;\n    private JButton chinese;\n    private JButton english;\n    private ToolWindow toolWindow;\n    private String appVersion;\n    private String appVersionCode;\n    private String appId;\n    private String appName;\n    private String appShort;\n    public static Main m;\n    private String apkAbsolutePath;\n    private QrCodeJDialog qrCode;\n    private UploadService uploadService;\n    public Binary binary;\n    public ro.catalin.prata.firuploader.Model.Document document;\n    private Color COLOR_DARK_PURPLE = new Color(37, 172, 201);\n    private TimerScan timerScan;\n    public Main() {\n        initComponent();\n        if(!\"yes\".equals(KeysManager.instance().getUploadFlag()) || !\"cancel\".equals(KeysManager.instance().getUploadFlag())){\n            KeysManager.instance().setUploadFlag(\"cancel\");\n        }\n        m = Main.this;\n        qrCode = new QrCodeJDialog();\n        binary = new Binary();\n        Main.getInstance().setTest(\"start\");\n        Main.getInstance().setTest(\"end\");\n        progressBar.setVisible(false);\n        tips.setVisible(false);\n        uploadService = new UploadService();\n        uploadBtn.addActionListener(new ActionListener() {\n            public void actionPerformed(ActionEvent e) {\n                updateBuildVersionFields();\n                performUploadValidation();\n\n            }\n        });\n        chinese.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent actionEvent) {\n                KeysManager.instance().setLanguage(\"chinese\");\n                initComponent();\n            }\n        });\n\n        english.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent actionEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n                KeysManager.instance().setLanguage(\"english\");\n                initComponent();\n            }\n        });\n        cancelUploadButton.setVisible(false);\n        cancelUploadButton.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent actionEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n                uploadService.post.abort();\n                uploadService.uploadServiceDelegate = null;\n                uploadService = null;  //销毁对象暂停上传\n                uploadService = new UploadService();\n                progressBar.setVisible(false);\n                uploadBtn.setEnabled(true);\n                uploadBtn.setText(document.uploadBtn);\n                changeLogTa.setText(\"\");\n                Main.getInstance().tips.setVisible(false);\n                Main.getInstance().tips.repaint();\n                cancelUploadButton.setVisible(false);\n            }\n        });\n        setTokenBtn.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent actionEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n                // open an input dialog for the api key\n                String apiKey = Messages.showInputDialog(ProjectManager.getInstance().getOpenProjects()[0],\n                        \"<HTML>获取api token <a href=\\\"http://fir.im/user/info\\\">here</a>.</HTML>\",\n                        \"api token\", null, KeysManager.instance().getApiKey(), null);\n\n                // save the api key after a minor validation\n                if (apiKey != null && apiKey.length() > 3) {\n                    KeysManager.instance().setApiKey(apiKey);\n                }\n            }\n        });\n        projectCombo.addItemListener(new ItemListener() {\n            @Override\n            public void itemStateChanged(ItemEvent e) {\n\n                // if a module is selected, save the module\n                KeysManager.instance().setSelectedModuleName((String) projectCombo.getSelectedItem());\n\n                // update the apk path\n                Module module = ModulesManager.instance().getModuleByName((String) projectCombo.getSelectedItem());\n                String filePath = ModulesManager.instance().getAndroidApkPath(module);\n                if (filePath == null)\n                    filePath = \"\";\n                // the file was selected so add it to the text field\n                File file = new File(filePath);\n                if (!file.exists() || filePath.toLowerCase().indexOf(\".apk\") < 0) {\n                    filePath = \"\";\n                }\n                apkAbsolutePath = filePath;\n                binary.initPath(apkAbsolutePath);\n\n                apkPath.setText(splitPath(filePath));\n\n                // update the build version fields too\n                updateBuildVersionFields();\n\n            }\n        });\n\n        setupValuesOnUI();\n        settingBtn.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent actionEvent) {\n                // create a new file type with the apk extension to be used for file type filtering\n                FileType type = FileTypeManager.getInstance().getFileTypeByExtension(\"apk\");\n\n                // create a descriptor for the file chooser\n                FileChooserDescriptor descriptor = Utils.createSingleFileDescriptor(type);\n                descriptor.setTitle(\"APK File\");\n                descriptor.setDescription(\"choose apk to upload FIR.im\");\n\n                // by default open the first opened project root directory\n                VirtualFile fileToSelect = ProjectManager.getInstance().getOpenProjects()[0].getBaseDir();\n\n                // open the file chooser\n                FileChooser.chooseFiles(descriptor, null, fileToSelect, new FileChooser.FileChooserConsumer() {\n                    @Override\n                    public void cancelled() {\n\n                        // do nothing for now...\n\n                    }\n\n                    @Override\n                    public void consume(List<VirtualFile> virtualFiles) {\n\n                        String filePath = virtualFiles.get(0).getPath();\n\n                        // the file was selected so add it to the text field\n                        File file = new File(filePath) ;\n                        if(!file.exists() || filePath.toLowerCase().indexOf(\".apk\")<0 )    {\n                            filePath = \"\";\n                        }\n\n                        apkAbsolutePath = filePath;\n\n                        apkPath.setText(splitPath(filePath));\n                        binary.initPath(apkAbsolutePath);\n\n                        // save the file path\n                        KeysManager.instance().setApkFilePath(filePath);\n                        updateBuildVersionFields();\n\n                    }\n                });            }\n        });\n        shortLink.addMouseListener(new MouseListener() {\n            @Override\n            public void mouseClicked(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n                browserUrl(Main.getInstance().shortLink.getText());\n            }\n\n            @Override\n            public void mousePressed(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseReleased(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseEntered(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseExited(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n        });\n\n        qrCodeLabel.addMouseListener(new MouseListener() {\n            @Override\n            public void mouseClicked(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n                if (!shortLink.getText().isEmpty()){\n                    qrCode.show();\n                }\n            }\n\n            @Override\n            public void mousePressed(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseReleased(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseEntered(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseExited(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n        });\n\n        formHelp.addMouseListener(new MouseListener() {\n            @Override\n            public void mouseClicked(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n                try {\n                    Desktop desktop = Desktop.getDesktop();\n                    String message = \"mailto:yh@fir.im?subject=firuploader\";\n                    URI uri = URI.create(message);\n                    desktop.mail(uri);\n                } catch (IOException e) {\n                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n                    Utils.postErrorNoticeTOSlack(e);\n                }\n            }\n\n            @Override\n            public void mousePressed(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseReleased(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseEntered(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n\n            @Override\n            public void mouseExited(MouseEvent mouseEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n            }\n        });\n        timerScan = new TimerScan();\n\n        this.formTipCB.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent actionEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n                if(formTipCB.isSelected()){\n                    KeysManager.instance().setFlag(\"\");\n                }else{\n                    KeysManager.instance().setFlag(\"cancel\");\n                }\n            }\n        });\n\n        this.formUploadCB.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent actionEvent) {\n                //To change body of implemented methods use File | Settings | File Templates.\n                if(formUploadCB.isSelected()){\n                    KeysManager.instance().setUploadFlag(\"yes\");\n                }else{\n                    KeysManager.instance().setUploadFlag(\"cancel\");\n                }\n            }\n        });\n    }\n\n    public String splitPath(String filep){\n        String rt = \"\";\n        if(filep == null){\n            filep = \"\";\n        }\n        if(filep.length()>30){\n            filep =\"...\"+filep.substring(filep.length()-30,filep.length());\n        }\n        return filep  ;\n    }\n    public void setShortLink (String sh){\n        this.appShort = sh;\n        shortLink.setText(sh);\n        shortLink.repaint();\n        qrCode.setUri(sh);\n    }\n    public static Main getInstance(){\n\n           return m;\n    }\n    /**\n     * Updates the build version(code and name) fields\n     */\n    public void updateBuildVersionFields() {\n        Module module = ModulesManager.instance().getModuleByName((String) projectCombo.getSelectedItem());\n        // update the code and name text fields manifest build version code and name values\n\n\n      String[] apk = new String[3];\n      if(apkAbsolutePath != null){\n           binary.initPath(apkAbsolutePath);\n      }\n\n    }\n\n    public void setTips(String content){\n        Main.getInstance().tips.setText(content);\n    }\n    /**\n     * Performs validation before uploading the build to test flight, if everything is in order, the build is sent\n     */\n    public void performUploadValidation() {\n\n        String mm = apkPath.getText();\n        if (KeysManager.instance().getApiKey() == null) {\n\n            Messages.showErrorDialog(\"请设置fir.im的api token\",\n                    \"api token 不合法\");\n\n        } else if (apkAbsolutePath == null || apkAbsolutePath.length() < 3) {\n\n            Messages.showErrorDialog(\"工程没有发现apk文件请单击设置来设置apk路径\",\n                    \"apk文件不合法\");\n\n        }  else if(binary.name == null){\n             binary.name = Messages.showInputDialog(ProjectManager.getInstance().getOpenProjects()[0],\n                    \"<HTML>请设置apk应用名称</HTML>\",\n                    \"apk的名称\", null, \"\", null);\n\n        } else {\n\n\n                uploadBuild();\n\n        }\n\n\n    }\n    /**\n     * Uploads the build to test flight, it updates also the UI\n     */\n    public void uploadBuild() {\n        Main.getInstance().binary.initPath(apkAbsolutePath);\n        progressBar.setValue(0);\n        progressBar.setVisible(true);\n        uploadBtn.setEnabled(false);\n        tips.setVisible(true);\n        uploadBtn.setText(\"uploading...\");\n        tips.setText(\"uploading....\");\n\n        cancelUploadButton.setVisible(true);\n        // upload the build\n        uploadService.sendBuild(null, apkAbsolutePath, KeysManager.instance().getApiKey(),\n                binary,\n                changeLogTa.getText(),\n                Main.this);\n\n    }\n\n   public void setTest(String text){\n\n       //logContent.setText(text+\"\\n\"+logContent.getText() );\n   }\n    /**\n     * Set the default or previously saved values on the UI components\n     */\n    public void setupValuesOnUI() {\n\n        Module previouslySelectedModule;\n\n        // if the apk file path was not saved previously by the user, set the saved module apk file path or the best matching module\n        previouslySelectedModule = ModulesManager.instance().getModuleByName(KeysManager.instance().getSelectedModuleName());\n\n        if (previouslySelectedModule != null) {\n            String filePath = ModulesManager.instance().getAndroidApkPath(previouslySelectedModule);\n\n            if(filePath == null)\n                filePath = \"\";\n            // the file was selected so add it to the text field\n            File file = new File(filePath) ;\n            if(!file.exists() || filePath.toLowerCase().indexOf(\".apk\") < 0)    {\n                filePath = \"\";\n            }\n\n            apkAbsolutePath = filePath;\n\n            apkPath.setText(splitPath(filePath));\n            binary.initPath(apkAbsolutePath);\n\n        } else {\n\n            // get the best matching module for this project and set it's file path\n            previouslySelectedModule = ModulesManager.instance().getMostImportantModule();\n            String filePath = ModulesManager.instance().getAndroidApkPath(previouslySelectedModule);\n\n            // the file was selected so add it to the text field\n            if(filePath == null)\n                filePath = \"\";\n            File file = new File(filePath) ;\n            if(!file.exists())    {\n                filePath = \"\";\n            }\n\n            apkAbsolutePath = filePath;\n\n            apkPath.setText(splitPath(filePath));\n            binary.initPath(apkAbsolutePath);\n        }\n\n        // set the model of the modules\n        projectCombo.setModel(new DefaultComboBoxModel(ModulesManager.instance().getAllModuleNames()));\n\n\n\n        // set the selection\n        projectCombo.setSelectedIndex(ModulesManager.instance().getSelectedModuleIndex(previouslySelectedModule.getName()));\n\n        updateBuildVersionFields();\n    }\n    @Override\n    public void createToolWindowContent(Project project, ToolWindow toolWindow) {\n        //To change body of implemented methods use File | Settings | File Templates.\n        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();\n        Content content = contentFactory.createContent(panel1, \"\", false);\n        toolWindow.getContentManager().addContent(content);\n\n        this.toolWindow = toolWindow;\n        ProjectManager.getInstance().addProjectManagerListener(new ProjectManagerListener() {\n            @Override\n            public void projectOpened(Project project) {\n\n                // get the best matching module for this project and set it's file path\n                Module previouslySelectedModule = ModulesManager.instance().getMostImportantModule();\n                String filePath = previouslySelectedModule.getModuleFilePath();\n                if(filePath == null)\n                    filePath = \"\";\n                File file = new File(filePath) ;\n                if(!file.exists() || filePath.toLowerCase().indexOf(\".apk\")< 0)    {\n                    filePath = \"\";\n                }\n                apkAbsolutePath = filePath;\n                binary.initPath(apkAbsolutePath);\n\n                apkPath.setText(splitPath(filePath));\n\n                KeysManager.instance().setSelectedModuleName(previouslySelectedModule.getName());\n\n                String[] modules = ModulesManager.instance().getAllModuleNames();\n                if (modules != null) {\n                    // set the model of the modules\n                    projectCombo.setModel(new DefaultComboBoxModel(ModulesManager.instance().getAllModuleNames()));\n                }\n\n                // set the selection\n                projectCombo.setSelectedIndex(ModulesManager.instance().getSelectedModuleIndex(previouslySelectedModule.getName()));\n\n            }\n\n            @Override\n            public boolean canCloseProject(Project project) {\n                return true;\n            }\n\n            @Override\n            public void projectClosed(Project project) {\n\n\n            }\n\n            @Override\n            public void projectClosing(Project project) {\n\n            }\n        });\n    }\n\n    @Override\n    public void onUploadFinished(final boolean finishedSuccessful) {\n\n        // upload is now finished, run some UI updates on the UI thread\n        SwingUtilities.invokeLater(new Runnable() {\n            public void run() {\n\n                cancelUploadButton.setVisible(false);\n                if (!finishedSuccessful) {\n\n                    Messages.showErrorDialog(\"上传失败！有问题请联系dev@fir.im\", \"上传失败！有问题请联系dev@fir.im\");\n                    progressBar.setVisible(false);\n                    uploadBtn.setEnabled(true);\n                    uploadBtn.setText(document.uploadBtn);\n                    changeLogTa.setText(\"\");\n                    Main.getInstance().tips.setVisible(false);\n                    Main.getInstance().tips.repaint();\n                    return;\n\n                }\n\n                progressBar.setVisible(false);\n                uploadBtn.setEnabled(true);\n                uploadBtn.setText(document.uploadBtn);\n                Main.getInstance().tips.setText(\"File upload success\");\n                KeysManager.instance().setMd5(Utils.getMd5(Main.getInstance().binary.filePath));\n                changeLogTa.setText(\"\");\n                uploadFinishNotice();\n                Thread th = new Thread(new Runnable() {\n                    @Override\n                    public void run() {\n                        //To change body of implemented methods use File | Settings | File Templates.\n                        try {\n                            // thread to sleep for 1000 milliseconds\n                            Thread.sleep(2000);\n                            Main.getInstance().tips.setVisible(false);\n                            Main.getInstance().tips.repaint();\n                            Utils.postSuccessNoticeToSlack(\"#\" + Main.getInstance().binary.name + \"#\" + Main.getInstance().appShort);\n                        } catch (Exception e) {\n                            Utils.postErrorNoticeTOSlack(e);\n                            System.out.println(e);\n                        }\n                    }\n                });\n                th.start();\n\n            }\n        });\n\n    }\n\n    @Override\n    public void onPackageSizeComputed(final long totalSize) {\n\n        SwingUtilities.invokeLater(new Runnable() {\n            public void run() {\n\n                progressBar.setMaximum((int) totalSize);\n\n            }\n        });\n\n    }\n\n    @Override\n    public void onProgressChanged(final long progress) {\n\n        SwingUtilities.invokeLater(new Runnable() {\n            public void run() {\n\n                progressBar.setValue((int) progress);\n\n            }\n        });\n\n    }\n\n\n    public void uploadFinishNotice(){\n        StatusBar statusBar = WindowManager.getInstance()\n                .getStatusBar(ProjectManager.getInstance().getOpenProjects()[0]);\n        JComponent component = statusBar.getComponent();\n        final Rectangle rect = component.getVisibleRect();\n        final Point p = new Point(rect.x + rect.width - 30, rect.y - 30);\n        final RelativePoint point = new RelativePoint(component, p);\n\n        JBPopupFactory.getInstance()\n                .createHtmlTextBalloonBuilder(\"<p style='font-size:12px;color:black'>fir.im上传成功Y(^_^)Y</p></br><p style='font-size:12px;'> <a style='font-size:12px' href='\"+shortLink.getText()+\"'>\"+shortLink.getText()+\"</a> 打开链接去查看.</p>\",\n                        MessageType.INFO, new HyperlinkListener() {\n                    @Override\n                    public void hyperlinkUpdate(HyperlinkEvent e) {\n\n                        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {\n//                            ToolWindowManager.getInstance(ProjectManager.getInstance().getOpenProjects()[0]).getToolWindow(\"FIR.im\").show(null);\n                            browserUrl(Main.getInstance().shortLink.getText())  ;\n                        }\n\n                    }\n                })\n                .setFadeoutTime(10000)\n                .setCloseButtonEnabled(true)\n                .createBalloon()\n                .show(point,\n                        Balloon.Position.atRight);\n    }\n    public void browserUrl(String url){\n        try {\n            Desktop.getDesktop().browse(new URI( url));\n        } catch (IOException e1) {\n            e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n            Utils.postErrorNoticeTOSlack(e1);\n        } catch (URISyntaxException e1) {\n            e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.\n            Utils.postErrorNoticeTOSlack(e1);\n        }\n    }\n\n    public void initComponent(){\n        document = new ro.catalin.prata.firuploader.Model.Document();\n        this.formHeader.setText(document.formHeader);\n        this.formLink.setText(document.formLink);\n        this.formLog.setText(document.formLog);\n        this.formPath.setText(document.formPath);\n        this.formToken.setText(document.formToken);\n        this.formProject.setText(document.formProject);\n        this.settingBtn.setText(document.settingBtn);\n        this.setTokenBtn.setText(document.setTokenBtn);\n        this.uploadBtn.setText(document.uploadBtn);\n        this.formTip.setText(document.formTip);\n        this.languageLabel.setText(document.languageLabel);\n        this.cancelUploadButton.setText(document.cancelUpload);\n        this.chinese.setText(document.chineseBtn);\n        this.english.setText(document.englishBtn);\n        if(\"cancel\".equals(KeysManager.instance().getFlag())){\n            this.formTipCB.setSelected(false);\n        }else{\n            this.formTipCB.setSelected(true);\n        }\n\n        if(\"cancel\".equals(KeysManager.instance().getUploadFlag())) {\n           this.formUploadCB.setSelected(false);\n        }else if(\"yes\".equals(KeysManager.instance().getUploadFlag())){\n            this.formUploadCB.setSelected(true);\n        }else{\n            this.formUploadCB.setSelected(false);\n            KeysManager.instance().setUploadFlag(\"cancel\");\n        }\n        this.formUpload.setText(document.formUpload);\n    }\n\n}\n"
  },
  {
    "path": "src/ro/catalin/prata/firuploader/view/QrCodeJDialog.java",
    "content": "package ro.catalin.prata.firuploader.view;\n\nimport javax.imageio.ImageIO;\nimport javax.swing.*;\nimport java.awt.*;\nimport java.io.IOException;\nimport java.net.URL;\n\n/**\n * Created with IntelliJ IDEA.\n * User: will\n * Date: 15/8/24\n * Time: 下午4:39\n * To change this template use File | Settings | File Templates.\n */\npublic class QrCodeJDialog extends JDialog {\n    public String uri = \"\";\n    JPanel panel;\n    int screenWidth;\n    int screenHeight;\n    public QrCodeJDialog(){\n\n        super();\n        getScreen();\n        this.setLocation((screenWidth-200)/2,(screenHeight-200)/2);\n        initPanel();\n\n    }\n\n    public void setUri(String uri){\n        this.uri = uri;\n        initPanel();\n    }\n\n    public void initPanel(){\n\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                        System.out.print(\"............\"+QrCodeJDialog.this.uri);\n                        QrCodeJDialog.this.setSize(200,200);\n                        Image image = null;\n                        try {\n                            URL url = new URL(\"http://qr.liantu.com/api.php?w=200&text=\"+QrCodeJDialog.this.uri);\n                            image = ImageIO.read(url);\n                        } catch (IOException e) {\n                            e.printStackTrace();\n                        }\n                        ImageIcon im = new ImageIcon(image);\n                        JLabel label = new JLabel(\"\", im, JLabel.CENTER);\n                        if(panel != null)\n                            QrCodeJDialog.this.remove(panel);\n                        panel = new JPanel(new BorderLayout());\n                        panel.add( label, BorderLayout.CENTER );\n                        QrCodeJDialog.this.add(panel) ;\n\n                    }\n        }).start();\n\n    }\n\n    public void getScreen(){\n        screenWidth=((int)java.awt.Toolkit.getDefaultToolkit().getScreenSize().width);\n        screenHeight = ((int)java.awt.Toolkit.getDefaultToolkit().getScreenSize().height);\n        System.out.println(screenWidth+\"\"+screenHeight);\n    }\n}\n"
  }
]