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