├── app ├── .gitignore ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── gradle-plugins │ │ │ └── center.uploadpgy.plugin.properties │ │ └── java │ │ └── net │ │ └── center │ │ └── upload_plugin │ │ ├── PluginUtils.java │ │ ├── helper │ │ ├── HttpHelper.java │ │ ├── CmdHelper2.kt │ │ ├── CmdHelper.java │ │ └── SendMsgHelper.java │ │ ├── params │ │ ├── GitLogParams.java │ │ ├── SendWeixinGroupParams.java │ │ ├── SendFeishuParams.java │ │ ├── UploadFirImParams.java │ │ ├── SendDingParams.java │ │ └── UploadPgyParams.java │ │ ├── model │ │ ├── feishu │ │ │ ├── TextDTO.java │ │ │ ├── ElementsDTO.java │ │ │ └── FeiShuRequestBean.java │ │ ├── BasePgyResult.java │ │ ├── PgyCOSTokenResult.java │ │ ├── WXGroupRequestBean.java │ │ ├── PgyUploadResult.java │ │ └── DingDingRequestBean.java │ │ ├── PluginConstants.java │ │ ├── task │ │ ├── BuildAndUploadTask.java │ │ ├── OnlyUploadTask.java │ │ └── BaseTask.java │ │ └── UploadApkPlugin.java ├── proguard-rules.pro └── build.gradle ├── .idea ├── .gitignore ├── compiler.xml ├── vcs.xml ├── misc.xml ├── gradle.xml └── jarRepositories.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── config.gradle ├── .gitignore ├── settings.gradle ├── gradle.properties ├── gradlew.bat ├── gradlew └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centerzx/UploadApkPlugin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/resources/META-INF/gradle-plugins/center.uploadpgy.plugin.properties: -------------------------------------------------------------------------------- 1 | implementation-class=net.center.upload_plugin.UploadApkPlugin -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | dependenceVersion = [ 3 | okhttpVersion : "3.12.12", 4 | GsonVersion : "2.8.8", 5 | ] 6 | } -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 29 01:26:13 CST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | .idea/inspectionProfiles/Project_Default.xml 17 | .idea/ 18 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | //dependencyResolutionManagement { 2 | // repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | // repositories { 4 | // google() 5 | // mavenCentral() 6 | // jcenter() // Warning: this repository is going to shut down soon 7 | // } 8 | //} 9 | //rootProject.name = "UploadApkPlugin" 10 | include ':app' 11 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/PluginUtils.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin; 2 | 3 | /** 4 | * Created by Android-ZX 5 | * 2021/9/3. 6 | */ 7 | public class PluginUtils { 8 | 9 | public static boolean isEmpty(CharSequence s) { 10 | if (s == null) { 11 | return true; 12 | } else { 13 | return s.length() == 0; 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/helper/HttpHelper.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.helper; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import okhttp3.OkHttpClient; 6 | 7 | public class HttpHelper { 8 | 9 | 10 | public static OkHttpClient getOkHttpClient() { 11 | return new OkHttpClient.Builder() 12 | .readTimeout(60, TimeUnit.SECONDS) 13 | .connectTimeout(60, TimeUnit.SECONDS) 14 | .writeTimeout(60, TimeUnit.SECONDS).build(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/params/GitLogParams.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.params; 2 | 3 | import org.gradle.api.Project; 4 | 5 | /** 6 | * 设置获取Git日志的参数 7 | */ 8 | public class GitLogParams { 9 | 10 | /** 11 | * 获取以当前时间为准的,多少天之前的Git记录,默认-1为全部,会结合数量进行获取 12 | */ 13 | public int gitLogHistoryDayTime = -1; 14 | 15 | /** 16 | * Git记录的最大数量 17 | */ 18 | public int gitLogMaxCount = 10; 19 | 20 | public static GitLogParams getGitParamsConfig(Project project) { 21 | GitLogParams extension = project.getExtensions().findByType(GitLogParams.class); 22 | return extension; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/model/feishu/TextDTO.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.model.feishu; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by center 7 | * Time:2022-09-02. 8 | * Desc: 9 | */ 10 | public class TextDTO { 11 | 12 | @SerializedName("content") 13 | private String content; 14 | @SerializedName("tag") 15 | private String tag; 16 | 17 | public String getContent() { 18 | return content; 19 | } 20 | 21 | public void setContent(String content) { 22 | this.content = content; 23 | } 24 | 25 | public String getTag() { 26 | return tag; 27 | } 28 | 29 | public void setTag(String tag) { 30 | this.tag = tag; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/model/BasePgyResult.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by center 7 | * Time:2022-08-20. 8 | * Desc: 9 | */ 10 | public class BasePgyResult { 11 | 12 | @SerializedName("code") 13 | private Integer code; 14 | @SerializedName("message") 15 | private String message; 16 | 17 | 18 | public Integer getCode() { 19 | return code; 20 | } 21 | 22 | public void setCode(Integer code) { 23 | this.code = code; 24 | } 25 | 26 | public String getMessage() { 27 | return message; 28 | } 29 | 30 | public void setMessage(String message) { 31 | this.message = message; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/PluginConstants.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin; 2 | 3 | /** 4 | * Created by Android-ZX 5 | * 2021/9/3. 6 | */ 7 | public class PluginConstants { 8 | 9 | public static final String TASK_GROUP_NAME="publishToThirdPlatform"; 10 | public static final String TASK_DES="tools of upload to third platform"; 11 | 12 | public static final String UPLOAD_PARAMS_NAME = "uploadPgyParams"; 13 | public static final String DING_PARAMS_NAME = "buildDingParams"; 14 | public static final String FEISHU_PARAMS_NAME = "buildFeiShuParams"; 15 | public static final String WEIXIN_GROUP_PARAMS_NAME = "buildWeixinGroupParams"; 16 | public static final String GIT_LOG_PARAMS_NAME = "buildGitLogParams"; 17 | 18 | public static final String ANDROID_EXTENSION_NAME = "android"; 19 | public static final String TASK_EXTENSION_NAME = "ApkBuildUploadPlatform"; 20 | public static final String TASK_EXTENSION_NAME_ONLY_UPLOAD = "OnlyUploadApkToPlatform"; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | //支持java语言 2 | apply plugin: 'java-library' 3 | //groovy语言 4 | //apply plugin: 'groovy' 5 | //kotlin语言 6 | //apply plugin: 'kotlin' 7 | 8 | //使用maven 9 | //apply plugin: 'maven' 10 | 11 | //先上传到github,再https://jitpack.io/ 12 | apply plugin: 'com.github.dcendents.android-maven' 13 | 14 | 15 | apply from: "../config.gradle" 16 | 17 | dependencies { 18 | // gradle sdk 19 | implementation gradleApi() 20 | // groovy sdk 21 | implementation localGroovy() 22 | implementation "com.android.tools.build:gradle:$build_gradle_version" 23 | 24 | implementation "com.google.code.gson:gson:${dependenceVersion.GsonVersion}" 25 | implementation "com.squareup.okhttp3:okhttp:${dependenceVersion.okhttpVersion}" 26 | } 27 | 28 | //repositories { 29 | // mavenCentral() 30 | //} 31 | 32 | 33 | //uploadArchives { 34 | // repositories.mavenDeployer { 35 | // repository(url: uri('../publishPluginRepo')) 36 | // pom.groupId = "net.center.upload.plugin" 37 | // pom.artifactId = "pgy-plugin" 38 | // pom.version = "1.0.0" 39 | //// pom.packaging = "aar" 40 | // } 41 | //} 42 | 43 | java { 44 | sourceCompatibility = JavaVersion.VERSION_1_8 45 | targetCompatibility = JavaVersion.VERSION_1_8 46 | } -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/params/SendWeixinGroupParams.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.params; 2 | 3 | import org.gradle.api.Project; 4 | 5 | /** 6 | * Created by Android-ZX 7 | *

8 | * 发送到企业微信群的消息参数 9 | */ 10 | public class SendWeixinGroupParams { 11 | 12 | public String webHookUrl; 13 | public String msgtype = "markdown"; 14 | /** 15 | * 如果使用文本可添加参数是否@全体群人员,默认true:isAtAll = true。其他类型不支持 16 | */ 17 | public boolean isAtAll = true; 18 | public String contentTitle; 19 | public String contentText; 20 | /** 21 | * 是否支持发送git记录 22 | */ 23 | public boolean isSupportGitLog = true; 24 | 25 | public SendWeixinGroupParams() { 26 | 27 | } 28 | 29 | public SendWeixinGroupParams(String webHookUrl, String msgtype, boolean isAtAll, String contentTitle, String contentText) { 30 | this.webHookUrl = webHookUrl; 31 | this.msgtype = msgtype; 32 | this.isAtAll = isAtAll; 33 | this.contentText = contentText; 34 | this.contentTitle = contentTitle; 35 | } 36 | 37 | public static SendWeixinGroupParams getWeixinGroupConfig(Project project) { 38 | SendWeixinGroupParams extension = project.getExtensions().findByType(SendWeixinGroupParams.class); 39 | if (extension == null) { 40 | extension = new SendWeixinGroupParams(); 41 | } 42 | return extension; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/params/SendFeishuParams.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.params; 2 | 3 | import org.gradle.api.Project; 4 | 5 | /** 6 | * Created by Android-ZX 7 | * 2021/9/3. 8 | *

9 | * 发送到飞书的消息参数 10 | */ 11 | public class SendFeishuParams { 12 | 13 | public String webHookHostUrl; 14 | public String contentTitle; 15 | public String contentText; 16 | public String msgtype = "post"; 17 | public boolean isAtAll = false; 18 | public String clickTxt = "点我进行下载"; 19 | /** 20 | * 是否支持发送git记录 21 | */ 22 | public boolean isSupportGitLog = true; 23 | 24 | public SendFeishuParams() { 25 | 26 | } 27 | 28 | public SendFeishuParams(String webHookHostUrl, String contentTitle, String contentText, String msgtype, boolean isAtAll, String clickTxt) { 29 | this.webHookHostUrl = webHookHostUrl; 30 | this.contentText = contentText; 31 | this.contentTitle = contentTitle; 32 | this.msgtype = msgtype; 33 | this.isAtAll = isAtAll; 34 | this.clickTxt = clickTxt; 35 | } 36 | 37 | public static SendFeishuParams getFeishuParamsConfig(Project project) { 38 | SendFeishuParams extension = project.getExtensions().findByType(SendFeishuParams.class); 39 | if (extension == null) { 40 | extension = new SendFeishuParams(); 41 | } 42 | return extension; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/params/UploadFirImParams.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.params; 2 | 3 | import org.gradle.api.Project; 4 | 5 | /** 6 | * Created by Android-ZX 7 | *

8 | * fir.im上传参数设置 9 | */ 10 | public class UploadFirImParams { 11 | 12 | //App 的 bundleId(发布新应用时必填) 13 | public String bundleId; 14 | //长度为 32, 用户在 fir 的 api_token 15 | public String apiToken; 16 | //应用名称(上传 ICON 时不需要) 17 | public String appName; 18 | //版本号(上传 ICON 时不需要) 19 | public String appVersion; 20 | //Build 号(上传 ICON 时不需要) 21 | public String buildCode; 22 | //更新日志(上传 ICON 时不需要) 23 | public String changelogText; 24 | 25 | public UploadFirImParams() { 26 | 27 | } 28 | 29 | public UploadFirImParams(String bundleId, String apiToken, String appName, String appVersion, String buildCode, String changelogText) { 30 | this.bundleId = bundleId; 31 | this.apiToken = apiToken; 32 | this.appName = appName; 33 | this.appVersion = appVersion; 34 | this.buildCode = buildCode; 35 | this.changelogText = changelogText; 36 | } 37 | 38 | public static UploadFirImParams getConfig(Project project) { 39 | UploadFirImParams extension = project.getExtensions().findByType(UploadFirImParams.class); 40 | if (extension == null) { 41 | extension = new UploadFirImParams(); 42 | } 43 | return extension; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/model/feishu/ElementsDTO.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.model.feishu; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by center 9 | * Time:2022-09-03. 10 | * Desc: 11 | */ 12 | public class ElementsDTO { 13 | 14 | @SerializedName("tag") 15 | private String tag; 16 | @SerializedName("text") 17 | private TextDTO text; 18 | @SerializedName("type") 19 | private String type; 20 | @SerializedName("url") 21 | private String url; 22 | @SerializedName("actions") 23 | private List actions; 24 | 25 | public String getTag() { 26 | return tag; 27 | } 28 | 29 | public void setTag(String tag) { 30 | this.tag = tag; 31 | } 32 | 33 | public TextDTO getText() { 34 | return text; 35 | } 36 | 37 | public void setText(TextDTO text) { 38 | this.text = text; 39 | } 40 | 41 | public String getType() { 42 | return type; 43 | } 44 | 45 | public void setType(String type) { 46 | this.type = type; 47 | } 48 | 49 | public String getUrl() { 50 | return url; 51 | } 52 | 53 | public void setUrl(String url) { 54 | this.url = url; 55 | } 56 | 57 | public List getActions() { 58 | return actions; 59 | } 60 | 61 | public void setActions(List actions) { 62 | this.actions = actions; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/params/SendDingParams.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.params; 2 | 3 | import org.gradle.api.Project; 4 | 5 | /** 6 | * Created by Android-ZX 7 | * 2021/9/3. 8 | *

9 | * 发送到钉钉的消息参数 10 | */ 11 | public class SendDingParams { 12 | 13 | public String accessToken; 14 | public String contentText; 15 | public String contentTitle; 16 | public String msgtype = "link"; 17 | public boolean isAtAll = false; 18 | public String clickTxt = "点我进行下载"; 19 | /** 20 | * 是否支持发送git记录 21 | */ 22 | public boolean isSupportGitLog = true; 23 | 24 | public SendDingParams() { 25 | 26 | } 27 | 28 | public SendDingParams(String accessToken) { 29 | this(accessToken, "", "测试包版本:"); 30 | } 31 | 32 | public SendDingParams(String accessToken, String contentText, String contentTitle) { 33 | this(accessToken, contentText, contentTitle, "link", false); 34 | } 35 | 36 | public SendDingParams(String accessToken, String contentText, String contentTitle, String msgtype, boolean isAtAll) { 37 | this.accessToken = accessToken; 38 | this.contentText = contentText; 39 | this.contentTitle = contentTitle; 40 | this.msgtype = msgtype; 41 | this.isAtAll = isAtAll; 42 | } 43 | 44 | public static SendDingParams getDingParamsConfig(Project project) { 45 | SendDingParams extension = project.getExtensions().findByType(SendDingParams.class); 46 | if (extension == null) { 47 | extension = new SendDingParams(); 48 | } 49 | return extension; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/helper/CmdHelper2.kt: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.helper 2 | 3 | import java.io.BufferedReader 4 | import java.io.InputStreamReader 5 | import java.nio.charset.StandardCharsets 6 | 7 | /** 8 | * Created by 钟新 9 | * Time:2022/9/1. 10 | * Desc: 11 | */ 12 | object CmdHelper2 { 13 | private const val LOG_MAX_COUNT = 50 14 | private const val LOG_MIN_COUNT = 10 15 | private const val GIT_LOG_BASIC_CMD = "git log --oneline --pretty=format:\"%an:%s\" --no-merges" 16 | const val gitLogCmd = GIT_LOG_BASIC_CMD + " --since=\"2022-8-30\" --until=\"2022-10-1\"" 17 | 18 | /** 19 | * %ai 是时间,格式:2022-08-31 23:18:48 +0800 20 | */ 21 | const val gitLogCmd2 = "git log --oneline --pretty=format:\"%ai,%an:%s\" --no-merges --since=2days" 22 | const val gitLogCmd3 = GIT_LOG_BASIC_CMD + " --max-count=10" 23 | fun getGitLogByTimeAndCount(logDayTime: Int, logMaxCount: Int): String { 24 | var logMaxCount = logMaxCount 25 | val logBuilder = StringBuilder(GIT_LOG_BASIC_CMD) 26 | if (logDayTime >= 1) { 27 | logBuilder.append(" --since=").append(logDayTime).append("days") 28 | logMaxCount = LOG_MAX_COUNT 29 | } else { 30 | if (logMaxCount <= 0) { 31 | logMaxCount = LOG_MIN_COUNT 32 | } else if (logMaxCount > LOG_MAX_COUNT) { 33 | logMaxCount = LOG_MAX_COUNT 34 | } 35 | } 36 | logBuilder.append(" --max-count=").append(logMaxCount) 37 | return exeCmd(logBuilder.toString()) 38 | } 39 | 40 | @JvmStatic 41 | fun main(args: Array) { 42 | exeCmd(gitLogCmd) 43 | } 44 | 45 | fun exeCmd(commandStr: String?): String { 46 | val p = Runtime.getRuntime().exec(commandStr) 47 | val ret = BufferedReader(InputStreamReader(p.inputStream, StandardCharsets.UTF_8)).useLines { it.mapIndexed { i, s -> "$i. $s" }.joinToString("\n") } 48 | println("ExeCmd result:\n$ret") 49 | return ret 50 | } 51 | } -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/task/BuildAndUploadTask.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.task; 2 | 3 | 4 | import com.android.build.gradle.api.BaseVariantOutput; 5 | 6 | import net.center.upload_plugin.params.UploadPgyParams; 7 | 8 | import org.gradle.api.GradleException; 9 | import org.gradle.api.tasks.TaskAction; 10 | 11 | import java.io.File; 12 | import java.util.Objects; 13 | 14 | /** 15 | * Created by Android-ZX 16 | * 2021/9/3. 17 | */ 18 | public class BuildAndUploadTask extends BaseTask { 19 | 20 | @TaskAction 21 | public void uploadToPGY() { 22 | for (BaseVariantOutput output : mVariant.getOutputs()) { 23 | File apkDir = output.getOutputFile(); 24 | if (apkDir == null || !apkDir.exists()) { 25 | throw new GradleException("The compiled APK file to upload does not exist!"); 26 | } 27 | System.out.println("ApkDir path: " + apkDir.getAbsolutePath()); 28 | File apk = null; 29 | if (apkDir.getName().endsWith(".apk")) { 30 | apk = apkDir; 31 | } else { 32 | if (apkDir.listFiles() != null) { 33 | for (int i = Objects.requireNonNull(apkDir.listFiles()).length - 1; i >= 0; i--) { 34 | File apkFile = Objects.requireNonNull(apkDir.listFiles())[i]; 35 | if (apkFile != null && apkFile.exists() && apkFile.getName().endsWith(".apk")) { 36 | apk = apkFile; 37 | break; 38 | } 39 | } 40 | } 41 | } 42 | if (apk == null || !apk.exists()) { 43 | throw new GradleException("The compiled APK file to upload does not exist!"); 44 | } 45 | System.out.println("Final upload apk path: " + apk.getAbsolutePath()); 46 | UploadPgyParams params = UploadPgyParams.getConfig(mTargetProject); 47 | // uploadPgyAndSendMessage(params.apiKey, params.appName, params.buildInstallType 48 | // , params.buildPassword, params.buildUpdateDescription 49 | // , params.buildInstallDate, params.buildChannelShortcut, apk); 50 | uploadPgyQuickWay(params.apiKey, params.appName, params.buildInstallType 51 | , params.buildPassword, params.buildUpdateDescription 52 | , params.buildInstallDate, params.buildChannelShortcut, apk); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/model/PgyCOSTokenResult.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by center 7 | * 2021-09-03. 8 | * 调用蒲公英getCOSToken接口后返回的数据 9 | * 10 | */ 11 | public class PgyCOSTokenResult extends BasePgyResult { 12 | 13 | 14 | @SerializedName("data") 15 | private DataDTO data; 16 | 17 | public DataDTO getData() { 18 | return data; 19 | } 20 | 21 | public void setData(DataDTO data) { 22 | this.data = data; 23 | } 24 | 25 | public static class DataDTO { 26 | @SerializedName("params") 27 | private ParamsDTO params; 28 | @SerializedName("key") 29 | private String key; 30 | @SerializedName("endpoint") 31 | private String endpoint; 32 | 33 | public ParamsDTO getParams() { 34 | return params; 35 | } 36 | 37 | public void setParams(ParamsDTO params) { 38 | this.params = params; 39 | } 40 | 41 | public String getKey() { 42 | return key; 43 | } 44 | 45 | public void setKey(String key) { 46 | this.key = key; 47 | } 48 | 49 | public String getEndpoint() { 50 | return endpoint; 51 | } 52 | 53 | public void setEndpoint(String endpoint) { 54 | this.endpoint = endpoint; 55 | } 56 | 57 | public static class ParamsDTO { 58 | @SerializedName("signature") 59 | private String signature; 60 | @SerializedName("x-cos-security-token") 61 | private String xcossecuritytoken; 62 | @SerializedName("key") 63 | private String key; 64 | 65 | public String getSignature() { 66 | return signature; 67 | } 68 | 69 | public void setSignature(String signature) { 70 | this.signature = signature; 71 | } 72 | 73 | public String getXcossecuritytoken() { 74 | return xcossecuritytoken; 75 | } 76 | 77 | public void setXcossecuritytoken(String xcossecuritytoken) { 78 | this.xcossecuritytoken = xcossecuritytoken; 79 | } 80 | 81 | public String getKey() { 82 | return key; 83 | } 84 | 85 | public void setKey(String key) { 86 | this.key = key; 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/task/OnlyUploadTask.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.task; 2 | 3 | 4 | import net.center.upload_plugin.PluginUtils; 5 | import net.center.upload_plugin.params.UploadPgyParams; 6 | 7 | import org.gradle.api.GradleException; 8 | import org.gradle.api.tasks.TaskAction; 9 | 10 | import java.io.File; 11 | import java.util.Objects; 12 | 13 | /** 14 | * Created by Android-ZX 15 | * 2021/9/3. 16 | */ 17 | public class OnlyUploadTask extends BaseTask { 18 | 19 | @TaskAction 20 | public void uploadToPGY() { 21 | UploadPgyParams params = UploadPgyParams.getConfig(mTargetProject); 22 | if (PluginUtils.isEmpty(params.uploadApkFilePath)) { 23 | System.out.println("The configured APK upload path (uploadApkFilePath) is empty"); 24 | throw new GradleException("The configured APK upload path (uploadApkFilePath) is empty!"); 25 | // return; 26 | } 27 | File apkDir = new File(params.uploadApkFilePath); 28 | if (!apkDir.exists()) { 29 | throw new GradleException("The configured APK upload file does not exist!"); 30 | } 31 | System.out.println("ApkDir path: " + apkDir.getAbsolutePath()); 32 | File apk = null; 33 | if (apkDir.getName().endsWith(".apk")) { 34 | apk = apkDir; 35 | } else { 36 | if (apkDir.listFiles() != null) { 37 | for (int i = Objects.requireNonNull(apkDir.listFiles()).length - 1; i >= 0; i--) { 38 | File apkFile = Objects.requireNonNull(apkDir.listFiles())[i]; 39 | if (apkFile != null && apkFile.exists() && apkFile.getName().endsWith(".apk")) { 40 | apk = apkFile; 41 | break; 42 | } 43 | } 44 | } 45 | } 46 | if (apk == null || !apk.exists()) { 47 | throw new GradleException("The configured APK upload file does not exist!"); 48 | } 49 | System.out.println("Final upload apk path: " + apk.getAbsolutePath()); 50 | // uploadPgyAndSendMessage(params.apiKey, params.appName, params.buildInstallType 51 | // , params.buildPassword, params.buildUpdateDescription 52 | // , params.buildInstallDate, params.buildChannelShortcut, apk); 53 | uploadPgyQuickWay(params.apiKey, params.appName, params.buildInstallType 54 | , params.buildPassword, params.buildUpdateDescription 55 | , params.buildInstallDate, params.buildChannelShortcut, apk); 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/params/UploadPgyParams.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.params; 2 | 3 | import org.gradle.api.Project; 4 | 5 | /** 6 | * Created by Android-ZX 7 | * 2021/9/3. 8 | * 9 | * 蒲公英上传参数设置 10 | */ 11 | public class UploadPgyParams { 12 | 13 | //(必填) API Key 点击获取_api_key 14 | public String apiKey; 15 | public String appName; 16 | public String buildTypeName;//非蒲公英参数,用于控制打包release还是debug,默认1是release,2是debug 17 | //(选填)应用安装方式,值为(1,2,3,默认为1 公开安装)。1:公开安装,2:密码安装,3:邀请安装 18 | public int buildInstallType = 1; 19 | //(选填) 设置App安装密码,密码为空时默认公开安装 20 | public String buildPassword; 21 | //(选填) 版本更新描述,请传空字符串,或不传。 22 | public String buildUpdateDescription; 23 | //(选填)是否设置安装有效期,值为:1 设置有效时间, 2 长期有效,如果不填写不修改上一次的设置 24 | public int buildInstallDate = 2; 25 | //(选填)所需更新的指定渠道的下载短链接,只可指定一个渠道,字符串型,如:abcd 26 | public String buildChannelShortcut; 27 | 28 | public String uploadApkFilePath; 29 | 30 | public UploadPgyParams() { 31 | 32 | } 33 | 34 | public UploadPgyParams(String apiKey) { 35 | this(apiKey, "Release"); 36 | } 37 | 38 | public UploadPgyParams(String apiKey, String buildTypeName) { 39 | this(apiKey, "", buildTypeName, 1, ""); 40 | } 41 | 42 | public UploadPgyParams(String apiKey, String appName, String buildTypeName, int buildInstallType, String buildPassword) { 43 | this(apiKey, appName, buildTypeName, buildInstallType, buildPassword, ""); 44 | } 45 | 46 | public UploadPgyParams(String apiKey, String appName, String buildTypeName, int buildInstallType, String buildPassword, String buildUpdateDescription) { 47 | this(apiKey, appName, buildTypeName, buildInstallType, buildPassword, buildUpdateDescription, 2, ""); 48 | } 49 | 50 | public UploadPgyParams(String apiKey, String appName, String buildTypeName, int buildInstallType, String buildPassword, String buildUpdateDescription, int buildInstallDate, String buildChannelShortcut) { 51 | this.apiKey = apiKey; 52 | this.appName = appName; 53 | this.buildTypeName = buildTypeName; 54 | this.buildInstallType = buildInstallType; 55 | this.buildPassword = buildPassword; 56 | this.buildUpdateDescription = buildUpdateDescription; 57 | this.buildInstallDate = buildInstallDate; 58 | this.buildChannelShortcut = buildChannelShortcut; 59 | } 60 | 61 | public static UploadPgyParams getConfig(Project project) { 62 | UploadPgyParams extension = project.getExtensions().findByType(UploadPgyParams.class); 63 | if (extension == null) { 64 | extension = new UploadPgyParams(); 65 | } 66 | return extension; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/helper/CmdHelper.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.helper; 2 | 3 | import net.center.upload_plugin.params.GitLogParams; 4 | 5 | import org.gradle.api.Project; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.nio.charset.StandardCharsets; 11 | 12 | /** 13 | * Created by 钟新 14 | * Time:2022/9/1. 15 | * Desc: 16 | */ 17 | public class CmdHelper { 18 | private static final int LOG_MAX_COUNT = 50; 19 | private static final int LOG_MIN_COUNT = 10; 20 | private static final String GIT_LOG_BASIC_CMD = "git log --oneline --pretty=format:\"%an—>%s\" --no-merges"; 21 | public static final String gitLogCmd = GIT_LOG_BASIC_CMD + " --since=\"2022-8-30\" --until=\"2022-9-1\""; 22 | /** 23 | * %ai 是时间,格式:2022-08-31 23:18:48 +0800 24 | */ 25 | public static final String gitLogCmd2 = "git log --oneline --pretty=format:\"%ai,%an:%s\" --no-merges --since=2days"; 26 | public static final String gitLogCmd3 = GIT_LOG_BASIC_CMD + " --max-count=10"; 27 | 28 | public static String checkGetGitParamsWithLog(Project project) { 29 | GitLogParams gitLogParams = GitLogParams.getGitParamsConfig(project); 30 | if (gitLogParams == null) { 31 | return ""; 32 | } 33 | return getGitLogByTimeAndCount(gitLogParams.gitLogHistoryDayTime, gitLogParams.gitLogMaxCount); 34 | } 35 | 36 | public static String getGitLogByTimeAndCount(int logDayTime, int logMaxCount) { 37 | StringBuilder logBuilder = new StringBuilder(GIT_LOG_BASIC_CMD); 38 | if (logDayTime >= 1) { 39 | logBuilder.append(" --since=").append(logDayTime).append("days"); 40 | logMaxCount = LOG_MAX_COUNT; 41 | } else { 42 | if (logMaxCount <= 0) { 43 | logMaxCount = LOG_MIN_COUNT; 44 | } else if (logMaxCount > LOG_MAX_COUNT) { 45 | logMaxCount = LOG_MAX_COUNT; 46 | } 47 | } 48 | logBuilder.append(" --max-count=").append(logMaxCount); 49 | return exeCmd(logBuilder.toString()); 50 | } 51 | 52 | public static String exeCmd(String commandStr) { 53 | BufferedReader bufferedReader = null; 54 | Process p = null; 55 | try { 56 | p = Runtime.getRuntime().exec(commandStr); 57 | //Charset.forName("UTF-8") 58 | bufferedReader = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8)); 59 | StringBuilder stringBuilder = new StringBuilder(); 60 | String line; 61 | int count = 0; 62 | while ((line = bufferedReader.readLine()) != null) { 63 | ++count; 64 | stringBuilder.append(count).append(". ").append(line).append("\n "); 65 | } 66 | System.out.println("ExeCmd result:\n" + stringBuilder); 67 | return stringBuilder.toString(); 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | System.out.println(e.getMessage()); 71 | } finally { 72 | if (bufferedReader != null) { 73 | try { 74 | bufferedReader.close(); 75 | } catch (IOException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | if (p != null) { 80 | try { 81 | p.waitFor(); 82 | } catch (InterruptedException e) { 83 | e.printStackTrace(); 84 | } 85 | } 86 | } 87 | return ""; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/UploadApkPlugin.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin; 2 | 3 | import com.android.build.gradle.AppExtension; 4 | import com.android.build.gradle.api.ApplicationVariant; 5 | 6 | import net.center.upload_plugin.params.GitLogParams; 7 | import net.center.upload_plugin.params.SendDingParams; 8 | import net.center.upload_plugin.params.SendFeishuParams; 9 | import net.center.upload_plugin.params.SendWeixinGroupParams; 10 | import net.center.upload_plugin.params.UploadPgyParams; 11 | import net.center.upload_plugin.task.BuildAndUploadTask; 12 | import net.center.upload_plugin.task.OnlyUploadTask; 13 | 14 | import org.gradle.api.DomainObjectSet; 15 | import org.gradle.api.Plugin; 16 | import org.gradle.api.Project; 17 | 18 | /** 19 | * Created by Android-ZX 20 | * 2021/9/3. 21 | */ 22 | public class UploadApkPlugin implements Plugin { 23 | 24 | @Override 25 | public void apply(Project project) { 26 | UploadPgyParams uploadParams = project.getExtensions().create(PluginConstants.UPLOAD_PARAMS_NAME, UploadPgyParams.class); 27 | createParams(project); 28 | dependsOnOnlyUploadTask(uploadParams, project); 29 | // UploadPgyParams ext = (UploadPgyParams) project.property(PluginConstants.UPLOAD_PARAMS_NAME); 30 | // System.out.println("apkfile path1: " + uploadParams.uploadApkFilePath); 31 | // System.out.println("apkfile path2: " + ext.uploadApkFilePath); 32 | project.afterEvaluate(project1 -> { 33 | AppExtension appExtension = ((AppExtension) project1.getExtensions().findByName(PluginConstants.ANDROID_EXTENSION_NAME)); 34 | if (appExtension == null) { 35 | return; 36 | } 37 | DomainObjectSet appVariants = appExtension.getApplicationVariants(); 38 | for (ApplicationVariant applicationVariant : appVariants) { 39 | if (applicationVariant.getBuildType() != null) { 40 | dependsOnTask(applicationVariant, uploadParams, project1); 41 | } 42 | } 43 | }); 44 | } 45 | 46 | private void createParams(Project project) { 47 | project.getExtensions().create(PluginConstants.GIT_LOG_PARAMS_NAME, GitLogParams.class); 48 | project.getExtensions().create(PluginConstants.DING_PARAMS_NAME, SendDingParams.class); 49 | project.getExtensions().create(PluginConstants.FEISHU_PARAMS_NAME, SendFeishuParams.class); 50 | project.getExtensions().create(PluginConstants.WEIXIN_GROUP_PARAMS_NAME, SendWeixinGroupParams.class); 51 | } 52 | 53 | 54 | private void dependsOnTask(ApplicationVariant applicationVariant, UploadPgyParams uploadParams, Project project1) { 55 | String variantName = 56 | applicationVariant.getName().substring(0, 1).toUpperCase() + applicationVariant.getName().substring(1); 57 | if (PluginUtils.isEmpty(variantName)) { 58 | variantName = PluginUtils.isEmpty(uploadParams.buildTypeName) ? "Release" : uploadParams.buildTypeName; 59 | } 60 | //创建我们,上传到蒲公英的task任务 61 | BuildAndUploadTask uploadTask = project1.getTasks() 62 | .create(PluginConstants.TASK_EXTENSION_NAME + variantName, BuildAndUploadTask.class); 63 | uploadTask.init(applicationVariant, project1); 64 | 65 | //依赖关系 。上传依赖打包,打包依赖clean。 66 | applicationVariant.getAssembleProvider().get().dependsOn(project1.getTasks().findByName("clean")); 67 | uploadTask.dependsOn(applicationVariant.getAssembleProvider().get()); 68 | } 69 | 70 | private void dependsOnOnlyUploadTask(UploadPgyParams uploadParams, Project project1) { 71 | //创建我们,上传到蒲公英的task任务 72 | OnlyUploadTask uploadTask = project1.getTasks() 73 | .create(PluginConstants.TASK_EXTENSION_NAME_ONLY_UPLOAD, OnlyUploadTask.class); 74 | uploadTask.init(null, project1); 75 | 76 | // //依赖关系 。上传依赖打包,打包依赖clean。 77 | // applicationVariant.getAssembleProvider().get().dependsOn(project1.getTasks().findByName("clean")); 78 | // uploadTask.dependsOn(applicationVariant.getAssembleProvider().get()); 79 | } 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/model/WXGroupRequestBean.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by center 9 | * 企业微信发送消息请求实体类 10 | */ 11 | public class WXGroupRequestBean { 12 | 13 | @SerializedName("msgtype") 14 | private String msgtype; 15 | 16 | @SerializedName("text") 17 | private TextDTO text; 18 | 19 | @SerializedName("markdown") 20 | private MarkdownDTO markdown; 21 | 22 | @SerializedName("news") 23 | private NewsDTO news; 24 | 25 | 26 | public String getMsgtype() { 27 | return msgtype; 28 | } 29 | 30 | public void setMsgtype(String msgtype) { 31 | this.msgtype = msgtype; 32 | } 33 | 34 | public TextDTO getText() { 35 | return text; 36 | } 37 | 38 | public void setText(TextDTO text) { 39 | this.text = text; 40 | } 41 | 42 | public MarkdownDTO getMarkdown() { 43 | return markdown; 44 | } 45 | 46 | public void setMarkdown(MarkdownDTO markdown) { 47 | this.markdown = markdown; 48 | } 49 | 50 | public NewsDTO getNews() { 51 | return news; 52 | } 53 | 54 | public void setNews(NewsDTO news) { 55 | this.news = news; 56 | } 57 | 58 | public static class NewsDTO { 59 | @SerializedName("articles") 60 | private List articles; 61 | 62 | public List getArticles() { 63 | return articles; 64 | } 65 | 66 | public void setArticles(List articles) { 67 | this.articles = articles; 68 | } 69 | 70 | public static class ArticlesDTO { 71 | @SerializedName("title") 72 | private String title; 73 | @SerializedName("description") 74 | private String description; 75 | @SerializedName("url") 76 | private String url; 77 | @SerializedName("picurl") 78 | private String picurl; 79 | 80 | public String getTitle() { 81 | return title; 82 | } 83 | 84 | public void setTitle(String title) { 85 | this.title = title; 86 | } 87 | 88 | public String getDescription() { 89 | return description; 90 | } 91 | 92 | public void setDescription(String description) { 93 | this.description = description; 94 | } 95 | 96 | public String getUrl() { 97 | return url; 98 | } 99 | 100 | public void setUrl(String url) { 101 | this.url = url; 102 | } 103 | 104 | public String getPicurl() { 105 | return picurl; 106 | } 107 | 108 | public void setPicurl(String picurl) { 109 | this.picurl = picurl; 110 | } 111 | } 112 | } 113 | 114 | /** 115 | * "content": "实时新增用户反馈132例,请相关同事注意。\n 116 | * >类型:用户反馈 117 | * >普通用户反馈:117例 118 | * >VIP用户反馈:15例" 119 | *

120 | * [这是一个链接](http://work.weixin.qq.com/api/doc) 121 | * 122 | * 绿色 123 | * 灰色 124 | * 橙红色 125 | */ 126 | public static class MarkdownDTO { 127 | @SerializedName("content") 128 | private String content; 129 | 130 | public String getContent() { 131 | return content; 132 | } 133 | 134 | public void setContent(String content) { 135 | this.content = content; 136 | } 137 | } 138 | 139 | /** 140 | * { 141 | * "msgtype": "text", 142 | * "text": { 143 | * "content": "广州今日天气:29度,大部分多云,降雨概率:60%", 144 | * "mentioned_list":["wangqing","@all"], 145 | * "mentioned_mobile_list":["13800001111","@all"] 146 | * } 147 | * } 148 | */ 149 | public static class TextDTO { 150 | @SerializedName("content") 151 | private String content; 152 | @SerializedName("mentioned_list") 153 | private List mentioned_list; 154 | @SerializedName("mentioned_mobile_list") 155 | private List mentioned_mobile_list; 156 | 157 | 158 | public String getContent() { 159 | return content; 160 | } 161 | 162 | public void setContent(String content) { 163 | this.content = content; 164 | } 165 | 166 | 167 | public List getMentioned_list() { 168 | return mentioned_list; 169 | } 170 | 171 | public void setMentioned_list(List mentioned_list) { 172 | this.mentioned_list = mentioned_list; 173 | } 174 | 175 | public List getMentioned_mobile_list() { 176 | return mentioned_mobile_list; 177 | } 178 | 179 | public void setMentioned_mobile_list(List mentioned_mobile_list) { 180 | this.mentioned_mobile_list = mentioned_mobile_list; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UploadApkPlugin 2 | 3 | 我们有这样的场景,在项目开发测试阶段,经常会将apk打包后,上传到蒲公英等三方平台,然后再发给公司其他人员进行测试、体验。每次发包的时候要去进行上传、上传完后通知相关人员,导致有点繁琐,浪费时间。此插件为了解决这个问题。 4 | 5 | Gradle插件,依赖进项目工程,编译APK,使用Task命令一键上传apk到三方平台,如:蒲公英。(目前暂时只支持蒲公英,可以进行扩展)。 6 | 上传成功后,如果你需要提醒其他人员进行版本更新,如:钉钉群、飞书群、企业微信群等(还可扩展其他),配置相关参数,自动发送更新消息到群里。 7 | 8 | ## 更新记录: 9 | 10 | 2022-08-22: 11 | 蒲公英上传接口做了调整,原有接口将废弃:https://www.pgyer.com/doc/view/api#fastUploadApp插件已根据文档做升级。 12 | 修复版本v1.0.3 13 | 14 | 2022-09-03: 15 | (1)修复发送消息到飞书出现msg_type参数错误问题; 16 | (2)新增Git提交日志参数,用于发送消息时携带Git日志; 17 | (3)完善飞书、钉钉、微信发送消息的类型并添加Git日志 18 | 修复版本v1.0.6 19 | 20 | 2022-10-30: 21 | 22 | 【新增】根据配置(uploadApkFilePath)要上传的APK文件路径,通过Task直接上传Apk。v1.0.7 23 | 这样减少编译流程和支持上传自己加固后的包。 24 | 25 | 2023-07-01: 26 | 【新增】发送消息内容,title部分新加Flavor信息。 27 | ## 使用步骤 28 | 29 | 1、在项目工程跟目录,工程的build.gradle dependencies中添加: 30 | 31 | `classpath 'com.github.centerzx:UploadApkPlugin:v***'` 32 | 目前版本为: 33 | 34 | `classpath 'com.github.centerzx:UploadApkPlugin:v1.0.7'` 35 | 36 | repositories中添加: 37 | 38 | maven { url "[https://jitpack.io](https://links.jianshu.com/go?to=https%3A%2F%2Fjitpack.io)"} 39 | 40 | 2、在app目录(需要打包上传的APK的Module)的build.gradle中添加引用插件: 41 | 42 | `apply plugin: 'center.uploadpgy.plugin'` 43 | 44 | **引入后,配置自己的相关平台参数:** 45 | 46 | ### (1)上传到蒲公英的相关配置参数 47 | 48 | ``` 49 | uploadPgyParams { 50 | apiKey = "替换为自己蒲公英账户的apiKey" 51 | // apiKey = readProperties("PgyApiKey") 52 | //暂时无用 53 | appName = "TestGradlePlugin" 54 | buildTypeName = "Release" 55 | buildInstallType = 2 56 | buildPassword = "zx" 57 | 58 | uploadApkFilePath = "E:\\Desktop\\release\\app_name-debug.apk" 59 | } 60 | ``` 61 | 62 | ### (2)发送消息到钉钉的相关配置参数 63 | 64 | ``` 65 | buildDingParams { 66 | accessToken = "替换为自己钉钉的token" 67 | // accessToken = readProperties("DingAccessToken") 68 | contentText = "最新开发测试包已经上传至蒲公英, 可以下载使用了" 69 | contentTitle = "开发测试包" 70 | //link类型(link)、markdown类型(markdown)、整体跳转ActionCard类型(actionCard),默认link 71 | msgtype = "actionCard" 72 | //如果使用markdown类型可添加参数是否@全体群人员,默认false:isAtAll = true。其他类型不支持 73 | isAtAll = true 74 | //存在点击时按钮的文案(link类型无) 75 | clickTxt = "点击进行下载" 76 | //是否单独支持发送Git记录数据,在配置了buildGitLogParams前提下有效,默认为true。link字数问题,无法支持 77 | isSupportGitLog = true 78 | } 79 | ``` 80 | 81 | ### (3)发送消息到飞书的相关配置参数 82 | 83 | ``` 84 | buildFeiShuParams { 85 | webHookHostUrl = "https://open.feishu.cn/open-apis/bot/v2/hook/************" 86 | // webHookHostUrl = readProperties("FeiShuWebHookHostUrl") 87 | contentTitle = "开发测试包" 88 | contentText = "最新开发测试包已经上传至蒲公英, 可以下载使用了" 89 | //富文本消息(post)、消息卡片(interactive),默认post 90 | msgtype = "post" 91 | //是否@全体群人员,默认false:isAtAll = true 92 | isAtAll = true 93 | clickTxt = "点击进行下载" 94 | //是否单独支持发送Git记录数据,在配置了buildGitLogParams前提下有效,默认为true 95 | isSupportGitLog = true 96 | } 97 | ``` 98 | 99 | ### (4)发送消息到企业微信群的相关配置参数 100 | 101 | ``` 102 | buildWeixinGroupParams { 103 | // webHookHostUrl = readProperties("WeixinWebHookUrl") 104 | webHookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=************" 105 | //文本(text)、markdown(markdown)、图文(news),默认 markdown。由于字数限制,只有markdown支持携带Git记录 106 | msgtype = "markdown" 107 | //如果使用文本类型(text)可添加参数是否@全体群人员,默认true:isAtAll = true。其他类型不支持 108 | //isAtAll = true 109 | contentTitle = "开发测试包" 110 | contentText = "最新开发测试包已经上传至蒲公英, 可以下载使用了" 111 | //是否单独支持发送Git记录数据,在配置了buildGitLogParams前提下有效,默认为true。只有markdown类型支持 112 | isSupportGitLog = true 113 | } 114 | ``` 115 | 116 | ### (5)发送消息时携带Git提交记录相关配置参数 117 | 118 | ``` 119 | buildGitLogParams { 120 | //是否发送消息是携带Git记录日志,如果配置了这块参数才会携带Git记录,消息里面可以单独设置是否携带Git日志数据 121 | 122 | //获取以当前时间为基准至N天之前的Git记录(限定时间范围),不填或小于等于0为全部记录,会结合数量进行获取 123 | gitLogHistoryDayTime = 3 124 | //显示Git记录的最大数量,值范围1~50,不填默认是10条,最大数量50条 125 | gitLogMaxCount = 20 126 | } 127 | ``` 128 | 129 | 其中各个参数体的名称不能变,否则编译会报错,参数key不能修改,对于的值可以根据自己情况修改。 130 | 参数中的“***************”号是根据你的蒲公英、钉钉、飞书、企业微信群等情况进行配置。 131 | 由于key、token等信息都需要保密,万一泄漏,可能被别有用心的人乱使用,故可以将相关关键信息保存在工程的:local.properties文件里面,此文件一般是根据自己本地进行配置的,不会上传到git。然后进行数据读取,如:readProperties(" 132 | PgyApiKey") 133 | 134 | 将密钥存在本地 防止泄露 local.properties 在Git的时候不会被上传 135 | 136 | ``` 137 | def readProperties(key) { 138 | File file = rootProject.file('local.properties') 139 | if (file.exists()) { 140 | InputStream inputStream = file.newDataInputStream() 141 | Properties properties = new Properties() 142 | properties.load(inputStream) 143 | if (properties.containsKey(key)) { 144 | return properties.getProperty(key) 145 | } 146 | } 147 | } 148 | ``` 149 | 150 | 3、配置完备,进行编译: 此时在studio的右边,tasks里面会新增一个publishToThirdPlatform 151 | 命令组,里面会有3个task:ApkBuildUploadPlatformDebug、ApkBuildUploadPlatformRelease、OnlyUploadApkToPlatform,其中OnlyUploadApkToPlatform为直接上传APK操作,配置uploadApkFilePath路径后有效。gradlew或者点击运行组里面的task命令,则可直接进行编译、打包、传送、发消息等一些列操作。 152 | 在执行task命令时,studio的run窗口会展示执行情况,包括蒲公英上传情况、钉钉飞书等消息发送情况,一目了然。 153 | 154 | 注意:由于这样会导致app的gradle看着很臃肿,因此可以单独新建一个gradle文件进行配置,然后再在app的gradle文件中apply from: "../******.gradle" 155 | 这个新建gradle就行。 156 | 157 | 158 | 159 | ![Task命令生成](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ab3509bdbc7741ac8a2f5ffa6c5a7b75~tplv-k3u1fbpfcp-zoom-1.image) 160 | 161 | 在执行task命令时,studio的run窗口会展示执行情况,包括蒲公英上传情况、钉钉飞书等消息发送情况,一目了然。 162 | 163 | ![Task运行结果输出](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6f94c9a663f9488f9979339df27b5377~tplv-k3u1fbpfcp-zoom-1.image) 164 | 165 | ![钉钉link型消息.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eb0e87a7eec04426a93878855db8cfee~tplv-k3u1fbpfcp-watermark.image?) 166 | 167 | **钉钉link类型消息提醒** 168 | 169 | ![钉钉markdown效果.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c7f454edcdd349e781a81fcb7069c4c6~tplv-k3u1fbpfcp-watermark.image?) 170 | 171 | **钉钉markdown类型消息提醒** 172 | 173 | ![钉钉actionCard效果.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ee5b1a40ed284780b76a0bbbab0a9594~tplv-k3u1fbpfcp-watermark.image?) 174 | 175 | **钉钉actionCard类型消息提醒** 176 | 177 | ![飞书post效果.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c30371f773d4c8f881d2fb3f7da6923~tplv-k3u1fbpfcp-watermark.image?) 178 | **飞书post类型消息提醒** 179 | 180 | ![飞书interactive效果.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1babdc3f9f9645a386ec226379200132~tplv-k3u1fbpfcp-watermark.image?) 181 | 182 | **飞书interactive类型消息提醒** 183 | 184 | ![企微消息.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/78a21577c28641619e083e9368bc02b6~tplv-k3u1fbpfcp-watermark.image?) 185 | 186 | **企业微信群消息** 187 | 188 | 189 | ## 后期展望: 190 | 191 | ``` 192 | 1、使用kotlin 193 | 2、扩展新增firim上传APK包形式(来自:https://github.com/D-zy 建议) 194 | 3、扩展新增发送的消息中自动携带Git的提交记录描述(已完成。来自 https://github.com/alizhijun 建议) 195 | 4、扩展新增多渠道打包(来自:https://github.com/alizhijun 建议) 196 | 5、扩展新增Apk加固(来自:https://github.com/j-gin 建议)——方案:由于每个公司或者项目组可能用的加固方案不同,故使用配置上传APK路径(加固后的APK路径)形式达到目的。 197 | ``` 198 | 199 | 大概情况是这样,如有不足和错误的地方,欢迎大家讨论指正和提建议!感谢来个star。 200 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/model/PgyUploadResult.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by center 7 | * 2021-09-03. 8 | * 蒲公英上传成功后的数据model 9 | *

10 | * buildKey String Build Key是唯一标识应用的索引ID 11 | * buildType Integer 应用类型(1:iOS; 2:Android) 12 | * buildIsFirst Integer 是否是第一个App(1:是; 2:否) 13 | * buildIsLastest Integer 是否是最新版(1:是; 2:否) 14 | * buildFileSize Integer App 文件大小 15 | * buildName String 应用名称 16 | * buildVersion String 版本号, 默认为1.0 (是应用向用户宣传时候用到的标识,例如:1.1、8.2.1等。) 17 | * buildVersionNo String 上传包的版本编号,默认为1 (即编译的版本号,一般来说,编译一次会变动一次这个版本号, 在 Android 上叫 Version Code。对于 iOS 来说,是字符串类型;对于 Android 来说是一个整数。例如:1001,28等。) 18 | * buildBuildVersion Integer 蒲公英生成的用于区分历史版本的build号 19 | * buildIdentifier String 应用程序包名,iOS为BundleId,Android为包名 20 | * buildIcon String 应用的Icon图标key,访问地址为 https://www.pgyer.com/image/view/app_icons/[应用的Icon图标key] 21 | * buildDescription String 应用介绍 22 | * buildUpdateDescription String 应用更新说明 23 | * buildScreenShots String 应用截图的key,获取地址为 https://www.pgyer.com/image/view/app_screenshots/[应用截图的key] 24 | * buildShortcutUrl String 应用短链接 25 | * buildQRCodeURL String 应用二维码地址 26 | * buildCreated String 应用上传时间 27 | * buildUpdated String 应用更新时间 28 | */ 29 | public class PgyUploadResult extends BasePgyResult { 30 | 31 | @SerializedName("data") 32 | private DataDTO data; 33 | 34 | public DataDTO getData() { 35 | return data; 36 | } 37 | 38 | public void setData(DataDTO data) { 39 | this.data = data; 40 | } 41 | 42 | public static class DataDTO { 43 | @SerializedName("buildKey") 44 | private String buildKey; 45 | @SerializedName("buildType") 46 | private String buildType; 47 | @SerializedName("buildIsFirst") 48 | private String buildIsFirst; 49 | @SerializedName("buildIsLastest") 50 | private String buildIsLastest; 51 | @SerializedName("buildFileKey") 52 | private String buildFileKey; 53 | @SerializedName("buildFileName") 54 | private String buildFileName; 55 | @SerializedName("buildFileSize") 56 | private String buildFileSize; 57 | @SerializedName("buildName") 58 | private String buildName; 59 | @SerializedName("buildVersion") 60 | private String buildVersion; 61 | @SerializedName("buildVersionNo") 62 | private String buildVersionNo; 63 | @SerializedName("buildBuildVersion") 64 | private String buildBuildVersion; 65 | @SerializedName("buildIdentifier") 66 | private String buildIdentifier; 67 | @SerializedName("buildIcon") 68 | private String buildIcon; 69 | @SerializedName("buildDescription") 70 | private String buildDescription; 71 | @SerializedName("buildUpdateDescription") 72 | private String buildUpdateDescription; 73 | @SerializedName("buildScreenshots") 74 | private String buildScreenshots; 75 | @SerializedName("buildShortcutUrl") 76 | private String buildShortcutUrl; 77 | @SerializedName("buildCreated") 78 | private String buildCreated; 79 | @SerializedName("buildUpdated") 80 | private String buildUpdated; 81 | @SerializedName("buildQRCodeURL") 82 | private String buildQRCodeURL; 83 | 84 | 85 | public String getBuildKey() { 86 | return buildKey; 87 | } 88 | 89 | public void setBuildKey(String buildKey) { 90 | this.buildKey = buildKey; 91 | } 92 | 93 | public String getBuildType() { 94 | return buildType; 95 | } 96 | 97 | public void setBuildType(String buildType) { 98 | this.buildType = buildType; 99 | } 100 | 101 | public String getBuildIsFirst() { 102 | return buildIsFirst; 103 | } 104 | 105 | public void setBuildIsFirst(String buildIsFirst) { 106 | this.buildIsFirst = buildIsFirst; 107 | } 108 | 109 | public String getBuildIsLastest() { 110 | return buildIsLastest; 111 | } 112 | 113 | public void setBuildIsLastest(String buildIsLastest) { 114 | this.buildIsLastest = buildIsLastest; 115 | } 116 | 117 | public String getBuildFileKey() { 118 | return buildFileKey; 119 | } 120 | 121 | public void setBuildFileKey(String buildFileKey) { 122 | this.buildFileKey = buildFileKey; 123 | } 124 | 125 | public String getBuildFileName() { 126 | return buildFileName; 127 | } 128 | 129 | public void setBuildFileName(String buildFileName) { 130 | this.buildFileName = buildFileName; 131 | } 132 | 133 | public String getBuildFileSize() { 134 | return buildFileSize; 135 | } 136 | 137 | public void setBuildFileSize(String buildFileSize) { 138 | this.buildFileSize = buildFileSize; 139 | } 140 | 141 | public String getBuildName() { 142 | return buildName; 143 | } 144 | 145 | public void setBuildName(String buildName) { 146 | this.buildName = buildName; 147 | } 148 | 149 | public String getBuildVersion() { 150 | return buildVersion; 151 | } 152 | 153 | public void setBuildVersion(String buildVersion) { 154 | this.buildVersion = buildVersion; 155 | } 156 | 157 | public String getBuildVersionNo() { 158 | return buildVersionNo; 159 | } 160 | 161 | public void setBuildVersionNo(String buildVersionNo) { 162 | this.buildVersionNo = buildVersionNo; 163 | } 164 | 165 | public String getBuildBuildVersion() { 166 | return buildBuildVersion; 167 | } 168 | 169 | public void setBuildBuildVersion(String buildBuildVersion) { 170 | this.buildBuildVersion = buildBuildVersion; 171 | } 172 | 173 | public String getBuildIdentifier() { 174 | return buildIdentifier; 175 | } 176 | 177 | public void setBuildIdentifier(String buildIdentifier) { 178 | this.buildIdentifier = buildIdentifier; 179 | } 180 | 181 | public String getBuildIcon() { 182 | return buildIcon; 183 | } 184 | 185 | public void setBuildIcon(String buildIcon) { 186 | this.buildIcon = buildIcon; 187 | } 188 | 189 | public String getBuildDescription() { 190 | return buildDescription; 191 | } 192 | 193 | public void setBuildDescription(String buildDescription) { 194 | this.buildDescription = buildDescription; 195 | } 196 | 197 | public String getBuildUpdateDescription() { 198 | return buildUpdateDescription; 199 | } 200 | 201 | public void setBuildUpdateDescription(String buildUpdateDescription) { 202 | this.buildUpdateDescription = buildUpdateDescription; 203 | } 204 | 205 | public String getBuildScreenshots() { 206 | return buildScreenshots; 207 | } 208 | 209 | public void setBuildScreenshots(String buildScreenshots) { 210 | this.buildScreenshots = buildScreenshots; 211 | } 212 | 213 | public String getBuildShortcutUrl() { 214 | return buildShortcutUrl; 215 | } 216 | 217 | public void setBuildShortcutUrl(String buildShortcutUrl) { 218 | this.buildShortcutUrl = buildShortcutUrl; 219 | } 220 | 221 | public String getBuildCreated() { 222 | return buildCreated; 223 | } 224 | 225 | public void setBuildCreated(String buildCreated) { 226 | this.buildCreated = buildCreated; 227 | } 228 | 229 | public String getBuildUpdated() { 230 | return buildUpdated; 231 | } 232 | 233 | public void setBuildUpdated(String buildUpdated) { 234 | this.buildUpdated = buildUpdated; 235 | } 236 | 237 | public String getBuildQRCodeURL() { 238 | return buildQRCodeURL; 239 | } 240 | 241 | public void setBuildQRCodeURL(String buildQRCodeURL) { 242 | this.buildQRCodeURL = buildQRCodeURL; 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/model/DingDingRequestBean.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by center 9 | * 2021-09-04. 10 | *

11 | * 目前只支持markdown语法的子集,具体支持的元素如下: 12 | *

13 | * 标题 14 | * # 一级标题 15 | * ## 二级标题 16 | * ### 三级标题 17 | * #### 四级标题 18 | * ##### 五级标题 19 | * ###### 六级标题 20 | *

21 | * 引用 22 | * > A man who stands for nothing will fall for anything. 23 | *

24 | * 文字加粗、斜体 25 | * **bold** 26 | * *italic* 27 | *

28 | * 链接 29 | * [this is a link](http://name.com) 30 | *

31 | * 图片 32 | * ![](http://name.com/pic.jpg) 33 | *

34 | * 无序列表 35 | * - item1 36 | * - item2 37 | *

38 | * 有序列表 39 | * 1. item1 40 | * 2. item2 41 | */ 42 | public class DingDingRequestBean { 43 | 44 | 45 | @SerializedName("msgtype") 46 | private String msgtype; 47 | 48 | /** 49 | * link类型的消息 50 | */ 51 | @SerializedName("link") 52 | private LinkDTO link; 53 | 54 | /** 55 | * markdown 类型的消息 56 | */ 57 | @SerializedName("markdown") 58 | private MarkDownDTO markdown; 59 | @SerializedName("at") 60 | private AtDTO at; 61 | 62 | /** 63 | * 整体跳转ActionCard类型和独立跳转ActionCard类型 64 | */ 65 | @SerializedName("actionCard") 66 | private ActionCardDTO actionCard; 67 | 68 | 69 | public String getMsgtype() { 70 | return msgtype; 71 | } 72 | 73 | public void setMsgtype(String msgtype) { 74 | this.msgtype = msgtype; 75 | } 76 | 77 | public LinkDTO getLink() { 78 | return link; 79 | } 80 | 81 | public void setLink(LinkDTO link) { 82 | this.link = link; 83 | } 84 | 85 | public MarkDownDTO getMarkdown() { 86 | return markdown; 87 | } 88 | 89 | public void setMarkdown(MarkDownDTO markdown) { 90 | this.markdown = markdown; 91 | } 92 | 93 | public AtDTO getAt() { 94 | return at; 95 | } 96 | 97 | public void setAt(AtDTO at) { 98 | this.at = at; 99 | } 100 | 101 | public ActionCardDTO getActionCard() { 102 | return actionCard; 103 | } 104 | 105 | public void setActionCard(ActionCardDTO actionCard) { 106 | this.actionCard = actionCard; 107 | } 108 | 109 | public static class LinkDTO { 110 | @SerializedName("text") 111 | private String text; 112 | @SerializedName("title") 113 | private String title; 114 | @SerializedName("picUrl") 115 | private String picUrl; 116 | @SerializedName("messageUrl") 117 | private String messageUrl; 118 | 119 | public String getText() { 120 | return text; 121 | } 122 | 123 | public void setText(String text) { 124 | this.text = text; 125 | } 126 | 127 | public String getTitle() { 128 | return title; 129 | } 130 | 131 | public void setTitle(String title) { 132 | this.title = title; 133 | } 134 | 135 | public String getPicUrl() { 136 | return picUrl; 137 | } 138 | 139 | public void setPicUrl(String picUrl) { 140 | this.picUrl = picUrl; 141 | } 142 | 143 | public String getMessageUrl() { 144 | return messageUrl; 145 | } 146 | 147 | public void setMessageUrl(String messageUrl) { 148 | this.messageUrl = messageUrl; 149 | } 150 | } 151 | 152 | /** 153 | * "markdown": { 154 | * "title":"杭州天气", 155 | * "text": "#### 杭州天气 @150XXXXXXXX \n > 9度,西北风1级,空气良89,相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n" 156 | * }, 157 | */ 158 | public static class MarkDownDTO { 159 | /** 160 | * markdown格式的消息。 161 | */ 162 | @SerializedName("text") 163 | private String text; 164 | @SerializedName("title") 165 | private String title; 166 | 167 | public String getText() { 168 | return text; 169 | } 170 | 171 | public void setText(String text) { 172 | this.text = text; 173 | } 174 | 175 | public String getTitle() { 176 | return title; 177 | } 178 | 179 | public void setTitle(String title) { 180 | this.title = title; 181 | } 182 | } 183 | 184 | /** 185 | * "at": { 186 | * "atMobiles": [ 187 | * "150XXXXXXXX" 188 | * ], 189 | * "atUserIds": [ 190 | * "user123" 191 | * ], 192 | * "isAtAll": false 193 | * } 194 | */ 195 | public static class AtDTO { 196 | @SerializedName("atMobiles") 197 | private List atMobiles; 198 | @SerializedName("atUserIds") 199 | private List atUserIds; 200 | @SerializedName("isAtAll") 201 | private boolean isAtAll; 202 | 203 | public List getAtMobiles() { 204 | return atMobiles; 205 | } 206 | 207 | public void setAtMobiles(List atMobiles) { 208 | this.atMobiles = atMobiles; 209 | } 210 | 211 | public List getAtUserIds() { 212 | return atUserIds; 213 | } 214 | 215 | public void setAtUserIds(List atUserIds) { 216 | this.atUserIds = atUserIds; 217 | } 218 | 219 | public boolean isAtAll() { 220 | return isAtAll; 221 | } 222 | 223 | public void setIsAtAll(boolean atAll) { 224 | isAtAll = atAll; 225 | } 226 | 227 | } 228 | 229 | /** 230 | * "actionCard": { 231 | * "title": "乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身", 232 | * "text": "![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png) 233 | * ### 乔布斯 20 年前想打造的苹果咖啡厅 234 | * Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", 235 | * "btnOrientation": "0", 236 | * "singleTitle" : "阅读全文", 237 | * "singleURL" : "https://www.dingtalk.com/" 238 | * }, 239 | *

240 | *

241 | * "actionCard": { 242 | * "title": "我 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身", 243 | * "text": "![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \n\n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", 244 | * "btnOrientation": "0", 245 | * "btns": [{ 246 | * "title": "内容不错", 247 | * "actionURL": "https://www.dingtalk.com/" 248 | * }, 249 | * { 250 | * "title": "不感兴趣", 251 | * "actionURL": "https://www.dingtalk.com/" 252 | * } 253 | * ] 254 | * } 255 | */ 256 | public static class ActionCardDTO { 257 | @SerializedName("title") 258 | private String title; 259 | @SerializedName("text") 260 | private String text; 261 | @SerializedName("btnOrientation") 262 | private String btnOrientation; 263 | 264 | /** 265 | * 整体跳转ActionCard类型 使用 266 | */ 267 | @SerializedName("singleTitle") 268 | private String singleTitle; 269 | @SerializedName("singleURL") 270 | private String singleURL; 271 | 272 | /** 273 | * 独立跳转ActionCard类型 使用 274 | */ 275 | @SerializedName("btns") 276 | private List btns; 277 | 278 | public static class BtnsDTO { 279 | @SerializedName("title") 280 | private String title; 281 | @SerializedName("actionURL") 282 | private String actionURL; 283 | 284 | public String getTitle() { 285 | return title; 286 | } 287 | 288 | public void setTitle(String title) { 289 | this.title = title; 290 | } 291 | 292 | public String getActionURL() { 293 | return actionURL; 294 | } 295 | 296 | public void setActionURL(String actionURL) { 297 | this.actionURL = actionURL; 298 | } 299 | } 300 | 301 | public String getTitle() { 302 | return title; 303 | } 304 | 305 | public void setTitle(String title) { 306 | this.title = title; 307 | } 308 | 309 | public String getText() { 310 | return text; 311 | } 312 | 313 | public void setText(String text) { 314 | this.text = text; 315 | } 316 | 317 | public String getBtnOrientation() { 318 | return btnOrientation; 319 | } 320 | 321 | public void setBtnOrientation(String btnOrientation) { 322 | this.btnOrientation = btnOrientation; 323 | } 324 | 325 | public String getSingleTitle() { 326 | return singleTitle; 327 | } 328 | 329 | public void setSingleTitle(String singleTitle) { 330 | this.singleTitle = singleTitle; 331 | } 332 | 333 | public String getSingleURL() { 334 | return singleURL; 335 | } 336 | 337 | public void setSingleURL(String singleURL) { 338 | this.singleURL = singleURL; 339 | } 340 | 341 | public List getBtns() { 342 | return btns; 343 | } 344 | 345 | public void setBtns(List btns) { 346 | this.btns = btns; 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/model/feishu/FeiShuRequestBean.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.model.feishu; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by center 9 | * 2021-09-04. 10 | */ 11 | public class FeiShuRequestBean { 12 | 13 | @SerializedName("msg_type") 14 | private String msg_type; 15 | @SerializedName("content") 16 | private ContentDTO content; 17 | @SerializedName("card") 18 | private CardDTO card; 19 | 20 | public String getMsg_type() { 21 | return msg_type; 22 | } 23 | 24 | public void setMsg_type(String msg_type) { 25 | this.msg_type = msg_type; 26 | } 27 | 28 | public ContentDTO getContent() { 29 | return content; 30 | } 31 | 32 | public void setContent(ContentDTO content) { 33 | this.content = content; 34 | } 35 | 36 | public CardDTO getCard() { 37 | return card; 38 | } 39 | 40 | public void setCard(CardDTO card) { 41 | this.card = card; 42 | } 43 | 44 | public static class ContentDTO { 45 | @SerializedName("post") 46 | private PostDTO post; 47 | 48 | public PostDTO getPost() { 49 | return post; 50 | } 51 | 52 | public void setPost(PostDTO post) { 53 | this.post = post; 54 | } 55 | 56 | public static class PostDTO { 57 | @SerializedName("zh_cn") 58 | public ZhCnDTO zh_cn; 59 | 60 | public ZhCnDTO getZh_cn() { 61 | return zh_cn; 62 | } 63 | 64 | public void setZh_cn(ZhCnDTO zh_cn) { 65 | this.zh_cn = zh_cn; 66 | } 67 | 68 | public static class ZhCnDTO { 69 | @SerializedName("title") 70 | private String title; 71 | @SerializedName("content") 72 | private List> content; 73 | 74 | public String getTitle() { 75 | return title; 76 | } 77 | 78 | public void setTitle(String title) { 79 | this.title = title; 80 | } 81 | 82 | public List> getContent() { 83 | return content; 84 | } 85 | 86 | public void setContent(List> content) { 87 | this.content = content; 88 | } 89 | 90 | public static class ContentBean { 91 | @SerializedName("tag") 92 | private String tag; 93 | @SerializedName("text") 94 | private String text; 95 | @SerializedName("href") 96 | private String href; 97 | @SerializedName("user_id") 98 | private String user_id; 99 | 100 | public String getTag() { 101 | return tag; 102 | } 103 | 104 | public void setTag(String tag) { 105 | this.tag = tag; 106 | } 107 | 108 | public String getText() { 109 | return text; 110 | } 111 | 112 | public void setText(String text) { 113 | this.text = text; 114 | } 115 | 116 | public String getHref() { 117 | return href; 118 | } 119 | 120 | public void setHref(String href) { 121 | this.href = href; 122 | } 123 | 124 | public String getUser_id() { 125 | return user_id; 126 | } 127 | 128 | public void setUser_id(String user_id) { 129 | this.user_id = user_id; 130 | } 131 | } 132 | } 133 | } 134 | 135 | } 136 | 137 | public static class CardDTO { 138 | @SerializedName("config") 139 | private ConfigDTO config; 140 | @SerializedName("header") 141 | private HeaderDTO header; 142 | @SerializedName("elements") 143 | private List elements; 144 | 145 | public ConfigDTO getConfig() { 146 | return config; 147 | } 148 | 149 | public void setConfig(ConfigDTO config) { 150 | this.config = config; 151 | } 152 | 153 | public HeaderDTO getHeader() { 154 | return header; 155 | } 156 | 157 | public void setHeader(HeaderDTO header) { 158 | this.header = header; 159 | } 160 | 161 | public List getElements() { 162 | return elements; 163 | } 164 | 165 | public void setElements(List elements) { 166 | this.elements = elements; 167 | } 168 | 169 | public static class PostDTO { 170 | @SerializedName("zh_cn") 171 | public ZhCnDTO zh_cn; 172 | 173 | public ZhCnDTO getZh_cn() { 174 | return zh_cn; 175 | } 176 | 177 | public void setZh_cn(ZhCnDTO zh_cn) { 178 | this.zh_cn = zh_cn; 179 | } 180 | 181 | public static class ZhCnDTO { 182 | @SerializedName("title") 183 | private String title; 184 | @SerializedName("content") 185 | private List> content; 186 | 187 | public String getTitle() { 188 | return title; 189 | } 190 | 191 | public void setTitle(String title) { 192 | this.title = title; 193 | } 194 | 195 | public List> getContent() { 196 | return content; 197 | } 198 | 199 | public void setContent(List> content) { 200 | this.content = content; 201 | } 202 | 203 | public static class ContentBean { 204 | @SerializedName("tag") 205 | private String tag; 206 | @SerializedName("text") 207 | private String text; 208 | @SerializedName("href") 209 | private String href; 210 | @SerializedName("user_id") 211 | private String user_id; 212 | 213 | public String getTag() { 214 | return tag; 215 | } 216 | 217 | public void setTag(String tag) { 218 | this.tag = tag; 219 | } 220 | 221 | public String getText() { 222 | return text; 223 | } 224 | 225 | public void setText(String text) { 226 | this.text = text; 227 | } 228 | 229 | public String getHref() { 230 | return href; 231 | } 232 | 233 | public void setHref(String href) { 234 | this.href = href; 235 | } 236 | 237 | public String getUser_id() { 238 | return user_id; 239 | } 240 | 241 | public void setUser_id(String user_id) { 242 | this.user_id = user_id; 243 | } 244 | } 245 | } 246 | } 247 | 248 | public static class ConfigDTO { 249 | @SerializedName("wide_screen_mode") 250 | private boolean wide_screen_mode; 251 | @SerializedName("enable_forward") 252 | private boolean enable_forward; 253 | 254 | public boolean isWide_screen_mode() { 255 | return wide_screen_mode; 256 | } 257 | 258 | public void setWide_screen_mode(boolean wide_screen_mode) { 259 | this.wide_screen_mode = wide_screen_mode; 260 | } 261 | 262 | public boolean isEnable_forward() { 263 | return enable_forward; 264 | } 265 | 266 | public void setEnable_forward(boolean enable_forward) { 267 | this.enable_forward = enable_forward; 268 | } 269 | } 270 | 271 | public static class HeaderDTO { 272 | @SerializedName("title") 273 | private TextDTO title; 274 | /** 275 | * 颜色值 276 | *

277 | * blue 278 | * wathet 279 | * turquoise 280 | * green 281 | * yellow 282 | * orange 283 | * red 284 | * carmine 285 | * violet 286 | * purple 287 | * indigo 288 | * grey 289 | */ 290 | @SerializedName("template") 291 | private String template; 292 | 293 | public TextDTO getTitle() { 294 | return title; 295 | } 296 | 297 | public void setTitle(TextDTO title) { 298 | this.title = title; 299 | } 300 | 301 | public String getTemplate() { 302 | return template; 303 | } 304 | 305 | public void setTemplate(String template) { 306 | this.template = template; 307 | } 308 | } 309 | 310 | 311 | } 312 | 313 | } 314 | -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/task/BaseTask.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.task; 2 | 3 | 4 | import com.android.build.gradle.api.BaseVariant; 5 | import com.google.gson.Gson; 6 | 7 | import net.center.upload_plugin.PluginConstants; 8 | import net.center.upload_plugin.PluginUtils; 9 | import net.center.upload_plugin.helper.CmdHelper; 10 | import net.center.upload_plugin.helper.HttpHelper; 11 | import net.center.upload_plugin.helper.SendMsgHelper; 12 | import net.center.upload_plugin.model.PgyCOSTokenResult; 13 | import net.center.upload_plugin.model.PgyUploadResult; 14 | 15 | import org.gradle.api.DefaultTask; 16 | import org.gradle.api.Project; 17 | 18 | import java.io.File; 19 | import java.util.Map; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import okhttp3.FormBody; 23 | import okhttp3.HttpUrl; 24 | import okhttp3.MediaType; 25 | import okhttp3.MultipartBody; 26 | import okhttp3.Request; 27 | import okhttp3.RequestBody; 28 | import okhttp3.Response; 29 | 30 | /** 31 | * Created by Android-ZX 32 | * 2021/9/3. 33 | */ 34 | public class BaseTask extends DefaultTask { 35 | 36 | protected BaseVariant mVariant; 37 | protected Project mTargetProject; 38 | 39 | public void init(BaseVariant variant, Project project) { 40 | this.mVariant = variant; 41 | this.mTargetProject = project; 42 | setDescription(PluginConstants.TASK_DES); 43 | setGroup(PluginConstants.TASK_GROUP_NAME); 44 | } 45 | 46 | /** 47 | * 本接口上传速度很慢,即将废弃,强烈建议您使用 快速上传App 中的方式来替代。 48 | * https://www.pgyer.com/doc/view/api#fastUploadApp 49 | * 50 | * @param apiKey 51 | * @param appName 52 | * @param installType 53 | * @param buildPassword 54 | * @param buildUpdateDescription 55 | * @param buildInstallDate 56 | * @param buildChannelShortcut 57 | * @param apkFile 58 | */ 59 | public void uploadPgyAndSendMessage(String apiKey, String appName, int installType, String buildPassword, String buildUpdateDescription, int buildInstallDate, String buildChannelShortcut, File apkFile) { 60 | //builder 61 | MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); 62 | bodyBuilder.addFormDataPart("_api_key", apiKey); 63 | if (!PluginUtils.isEmpty(buildUpdateDescription)) { 64 | bodyBuilder.addFormDataPart("buildUpdateDescription", buildUpdateDescription); 65 | } 66 | if (installType != 1) { 67 | bodyBuilder.addFormDataPart("buildInstallType", installType + ""); 68 | } 69 | if (installType == 2 && !PluginUtils.isEmpty(buildPassword)) { 70 | bodyBuilder.addFormDataPart("buildPassword", buildPassword); 71 | } 72 | bodyBuilder.addFormDataPart("buildInstallDate", buildInstallDate + ""); 73 | if (!PluginUtils.isEmpty(buildChannelShortcut)) { 74 | bodyBuilder.addFormDataPart("buildChannelShortcut", buildChannelShortcut); 75 | } 76 | //add file 77 | bodyBuilder.addFormDataPart("file", apkFile.getName(), RequestBody 78 | .create(MediaType.parse("*/*"), apkFile)); 79 | //request 80 | Request request = getRequestBuilder() 81 | .url("https://www.pgyer.com/apiv2/app/upload") 82 | .post(bodyBuilder.build()) 83 | .build(); 84 | try { 85 | Response response = HttpHelper.getOkHttpClient().newCall(request).execute(); 86 | if (response.isSuccessful() && response.body() != null) { 87 | String result = response.body().string(); 88 | System.out.println("upload pgy result: " + result); 89 | if (!PluginUtils.isEmpty(result)) { 90 | PgyUploadResult uploadResult = new Gson().fromJson(result, PgyUploadResult.class); 91 | if (uploadResult.getCode() != 0) { 92 | System.out.println("upload pgy result error msg: " + uploadResult.getMessage()); 93 | return; 94 | } 95 | if (uploadResult.getData() != null) { 96 | String url = "https://www.pgyer.com/" + uploadResult.getData().getBuildShortcutUrl(); 97 | System.out.println("上传成功,应用链接: " + url); 98 | String gitLog = CmdHelper.checkGetGitParamsWithLog(mTargetProject); 99 | SendMsgHelper.sendMsgToDingDing(mVariant, mTargetProject, uploadResult.getData(), gitLog); 100 | SendMsgHelper.sendMsgToFeishu(mVariant, mTargetProject, uploadResult.getData(), gitLog); 101 | SendMsgHelper.sendMsgToWeiXinGroup(mVariant, mTargetProject, uploadResult.getData(), gitLog); 102 | } else { 103 | System.out.println("upload pgy result error : data is empty"); 104 | } 105 | } 106 | } else { 107 | System.out.println("upload pgy failure"); 108 | } 109 | System.out.println("******************* upload finish *******************"); 110 | } catch (Exception e) { 111 | System.out.println("upload pgy failure " + e); 112 | } 113 | } 114 | 115 | /** 116 | * 快速上传方式 获取上传的 token 117 | * 118 | * @param apiKey 119 | * @param appName 120 | * @param installType 121 | * @param buildPassword 122 | * @param buildUpdateDescription 123 | * @param buildInstallDate 124 | * @param buildChannelShortcut 125 | * @param apkFile 126 | */ 127 | public void uploadPgyQuickWay(String apiKey, String appName, int installType, String buildPassword, String buildUpdateDescription, int buildInstallDate, String buildChannelShortcut, File apkFile) { 128 | //builder 129 | MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); 130 | bodyBuilder.addFormDataPart("_api_key", apiKey); 131 | bodyBuilder.addFormDataPart("buildType", "android"); 132 | if (!PluginUtils.isEmpty(buildUpdateDescription)) { 133 | bodyBuilder.addFormDataPart("buildUpdateDescription", buildUpdateDescription); 134 | } 135 | if (installType != 1) { 136 | bodyBuilder.addFormDataPart("buildInstallType", installType + ""); 137 | } 138 | if (installType == 2 && !PluginUtils.isEmpty(buildPassword)) { 139 | bodyBuilder.addFormDataPart("buildPassword", buildPassword); 140 | } 141 | bodyBuilder.addFormDataPart("buildInstallDate", buildInstallDate + ""); 142 | if (!PluginUtils.isEmpty(buildChannelShortcut)) { 143 | bodyBuilder.addFormDataPart("buildChannelShortcut", buildChannelShortcut); 144 | } 145 | System.out.println("upload pgy --- 快速上传方式接口:Start getCOSToken"); 146 | Request request = getRequestBuilder() 147 | .url("https://www.pgyer.com/apiv2/app/getCOSToken") 148 | .post(bodyBuilder.build()) 149 | .build(); 150 | try { 151 | Response response = HttpHelper.getOkHttpClient().newCall(request).execute(); 152 | if (response.isSuccessful() && response.body() != null) { 153 | String result = response.body().string(); 154 | System.out.println("upload pgy --- getCOSToken result: " + result); 155 | if (!PluginUtils.isEmpty(result)) { 156 | PgyCOSTokenResult pgyCOSTokenResult = new Gson().fromJson(result, PgyCOSTokenResult.class); 157 | if (pgyCOSTokenResult.getCode() != 0 || pgyCOSTokenResult.getData() == null) { 158 | System.out.println("upload pgy --- getCOSToken result error msg: " + pgyCOSTokenResult.getMessage()); 159 | return; 160 | } 161 | uploadFileToPgy(pgyCOSTokenResult, apkFile, apiKey); 162 | } 163 | } else { 164 | System.out.println("upload pgy ---- request getCOSToken call failed"); 165 | } 166 | System.out.println("******************* getCOSToken: finish *******************"); 167 | } catch (Exception e) { 168 | System.out.println("upload pgy ---- request getCOSToken call failed " + e); 169 | } 170 | } 171 | 172 | /** 173 | * 上传文件到第上一步获取的 URL 174 | * 175 | * @param tokenResult 176 | * @param apkFile 177 | * @param apiKey 178 | */ 179 | private void uploadFileToPgy(PgyCOSTokenResult tokenResult, File apkFile, String apiKey) { 180 | if (PluginUtils.isEmpty(tokenResult.getData().getEndpoint())) { 181 | System.out.println("upload pgy --- endpoint url is empty"); 182 | return; 183 | } 184 | if (tokenResult.getData().getParams() == null) { 185 | System.out.println("upload pgy --- endpoint params is empty"); 186 | return; 187 | } 188 | //builder 189 | MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); 190 | bodyBuilder.addFormDataPart("key", tokenResult.getData().getParams().getKey()); 191 | bodyBuilder.addFormDataPart("signature", tokenResult.getData().getParams().getSignature()); 192 | bodyBuilder.addFormDataPart("x-cos-security-token", tokenResult.getData().getParams().getXcossecuritytoken()); 193 | //add file 194 | bodyBuilder.addFormDataPart("file", apkFile.getName(), RequestBody 195 | .create(MediaType.parse("*/*"), apkFile)); 196 | System.out.println("upload pgy --- Start endpoint : " + tokenResult.getData().getEndpoint()); 197 | Request request = getRequestBuilder() 198 | .url(tokenResult.getData().getEndpoint()) 199 | .post(bodyBuilder.build()) 200 | .build(); 201 | try { 202 | Response response = HttpHelper.getOkHttpClient().newCall(request).execute(); 203 | System.out.println("upload pgy --- endpoint: upload apkFile to pgy response: " + response.isSuccessful()); 204 | if (response.isSuccessful() && response.body() != null) { 205 | String result = response.body().string(); 206 | System.out.println("upload pgy --- endpoint: upload apkFile to pgy result: " + result); 207 | // if (!PluginUtils.isEmpty(result)) { 208 | // BasePgyResult uploadResult = new Gson().fromJson(result, BasePgyResult.class); 209 | // if (uploadResult.getCode() != 204) { 210 | // System.out.println("endpoint: upload apkFile to pgy result error msg: " + uploadResult.getMessage()); 211 | // return; 212 | // } 213 | // checkPgyUploadBuildInfo(apiKey, tokenResult.getData().getKey()); 214 | // } 215 | checkPgyUploadBuildInfo(apiKey, tokenResult.getData().getKey()); 216 | } else { 217 | System.out.println("upload pgy --- endpoint: upload apkFile to pgy result failure"); 218 | } 219 | System.out.println("******************* endpoint: finish *******************"); 220 | } catch (Exception e) { 221 | System.out.println("upload pgy --- endpoint: upload apkFile to pgy result failure " + e); 222 | } 223 | } 224 | 225 | /** 226 | * 检测应用是否发布完成,并获取发布应用的信息 227 | * 228 | * @param apiKey 229 | * @param buildKey 发布成功失败返回数据 230 | * code Integer 错误码,1216 应用发布失败 231 | * message String 信息提示 232 | *

233 | * 正在发布返回数据 234 | * code Integer 错误码,1246 应用正在发布中 235 | * message String 信息提示 236 | * 如果返回 code = 1246 ,可间隔 3s ~ 5s 重新调用 URL 进行检测,直到返回成功或失败。 237 | *

238 | * buildInfo: upload pgy buildInfo result: {"code":1247,"message":"App is uploded, please wait"} 239 | */ 240 | private void checkPgyUploadBuildInfo(String apiKey, String buildKey) { 241 | // Map paramsMap = new HashMap<>(2); 242 | // paramsMap.put("_api_key", apiKey); 243 | // paramsMap.put("buildKey", buildKey); 244 | // String url = "https://www.pgyer.com/apiv2/app/buildInfo"; 245 | // System.out.println("upload pgy --- Start buildInfo"); 246 | // //request 247 | // Request request = getRequestBuilder("get", url, paramsMap) 248 | // .build(); 249 | String url = "https://www.pgyer.com/apiv2/app/buildInfo?_api_key=" + apiKey + "&buildKey=" + buildKey; 250 | System.out.println("upload pgy --- Start buildInfo : " + url); 251 | Request request = getRequestBuilder() 252 | .url(url) 253 | .get() 254 | .build(); 255 | try { 256 | Response response = HttpHelper.getOkHttpClient().newCall(request).execute(); 257 | if (response.isSuccessful() && response.body() != null) { 258 | String result = response.body().string(); 259 | System.out.println("upload pgy --- buildInfo: upload pgy buildInfo result: " + result); 260 | if (!PluginUtils.isEmpty(result)) { 261 | PgyUploadResult uploadResult = new Gson().fromJson(result, PgyUploadResult.class); 262 | if (uploadResult.getCode() == 0) { 263 | if (uploadResult.getData() != null) { 264 | String apkDownUrl = "https://www.pgyer.com/" + uploadResult.getData().getBuildShortcutUrl(); 265 | System.out.println("上传成功,应用链接: " + apkDownUrl); 266 | String gitLog = CmdHelper.checkGetGitParamsWithLog(mTargetProject); 267 | SendMsgHelper.sendMsgToDingDing(mVariant, mTargetProject, uploadResult.getData(), gitLog); 268 | SendMsgHelper.sendMsgToFeishu(mVariant, mTargetProject, uploadResult.getData(), gitLog); 269 | SendMsgHelper.sendMsgToWeiXinGroup(mVariant, mTargetProject, uploadResult.getData(), gitLog); 270 | } else { 271 | System.out.println("upload pgy --- buildInfo: upload pgy result error : data is empty"); 272 | } 273 | } else if (uploadResult.getCode() == 1246 || uploadResult.getCode() == 1247) { 274 | //正在发布返回数据 275 | System.out.println("upload pgy --- buildInfo: upload pgy buildInfo code( " + uploadResult.getCode() + "):" + uploadResult.getMessage()); 276 | // checkPgyUploadBuildInfo(apiKey, buildKey); 277 | pgyUploadBuildInfoTimer(apiKey, buildKey); 278 | } else { 279 | System.out.println("upload pgy --- buildInfo: upload pgy buildInfo result error msg: " + uploadResult.getMessage()); 280 | } 281 | } 282 | } else { 283 | System.out.println("upload pgy --- buildInfo: upload pgy buildInfo result failure"); 284 | } 285 | System.out.println("******************* buildInfo: finish *******************"); 286 | } catch (Exception e) { 287 | System.out.println("upload pgy --- buildInfo: upload pgy buildInfo result failure " + e); 288 | } 289 | } 290 | 291 | private void pgyUploadBuildInfoTimer(String apiKey, String buildKey) { 292 | System.out.println("upload pgy --- buildInfo: upload pgy buildInfo request again(pgyUploadBuildInfoTimer)"); 293 | // if (executorService == null) { 294 | // executorService = new ScheduledThreadPoolExecutor(1); 295 | // } 296 | // executorService.scheduleWithFixedDelay(() -> { 297 | // try { 298 | // checkPgyUploadBuildInfo(apiKey, buildKey); 299 | // } catch (Exception e) { 300 | // e.printStackTrace(); 301 | // } 302 | // }, 0, 3, TimeUnit.SECONDS); 303 | 304 | // if (mTimer == null) { 305 | // mTimer = new Timer(); 306 | // } 307 | // mTimer.schedule(new TimerTask() { 308 | // @Override 309 | // public void run() { 310 | // try { 311 | // System.out.println("buildInfo: upload pgy buildInfo request again"); 312 | // checkPgyUploadBuildInfo(apiKey, buildKey); 313 | // } catch (Exception e) { 314 | // e.printStackTrace(); 315 | // } 316 | // } 317 | // }, 3000); 318 | try { 319 | TimeUnit.SECONDS.sleep(3); 320 | // Thread.sleep(3000); 321 | checkPgyUploadBuildInfo(apiKey, buildKey); 322 | } catch (InterruptedException e) { 323 | e.printStackTrace(); 324 | } 325 | } 326 | 327 | 328 | private Request.Builder getRequestBuilder() { 329 | return new Request.Builder() 330 | .addHeader("Connection", "Keep-Alive") 331 | .addHeader("Charset", "UTF-8"); 332 | } 333 | 334 | 335 | private Request.Builder getRequestBuilder(String method, String url, Map params) { 336 | HttpUrl httpUrl = HttpUrl.parse(url); 337 | if (httpUrl == null) { 338 | return new Request.Builder(); 339 | } 340 | HttpUrl.Builder httpBuilder = httpUrl.newBuilder(); 341 | if (params != null) { 342 | for (Map.Entry param : params.entrySet()) { 343 | httpBuilder.addQueryParameter(param.getKey(), param.getValue()); 344 | } 345 | } 346 | return new Request.Builder() 347 | .url(httpBuilder.build()) 348 | .method(method, new FormBody.Builder().build()) 349 | .addHeader("Connection", "Keep-Alive") 350 | .addHeader("Charset", "UTF-8"); 351 | } 352 | } -------------------------------------------------------------------------------- /app/src/main/java/net/center/upload_plugin/helper/SendMsgHelper.java: -------------------------------------------------------------------------------- 1 | package net.center.upload_plugin.helper; 2 | 3 | import com.android.build.gradle.api.BaseVariant; 4 | 5 | import net.center.upload_plugin.PluginUtils; 6 | import net.center.upload_plugin.model.DingDingRequestBean; 7 | import net.center.upload_plugin.model.PgyUploadResult; 8 | import net.center.upload_plugin.model.WXGroupRequestBean; 9 | import net.center.upload_plugin.model.feishu.ElementsDTO; 10 | import net.center.upload_plugin.model.feishu.FeiShuRequestBean; 11 | import net.center.upload_plugin.model.feishu.TextDTO; 12 | import net.center.upload_plugin.params.SendDingParams; 13 | import net.center.upload_plugin.params.SendFeishuParams; 14 | import net.center.upload_plugin.params.SendWeixinGroupParams; 15 | 16 | import org.gradle.api.Project; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import groovy.json.JsonOutput; 22 | import okhttp3.MediaType; 23 | import okhttp3.Request; 24 | import okhttp3.RequestBody; 25 | import okhttp3.Response; 26 | 27 | public class SendMsgHelper { 28 | 29 | private static final String defaultTitle = "测试包"; 30 | private static final String defaultText = "最新开发测试包已上传 "; 31 | private static final String defaultClickText = "点我进行下载"; 32 | private static final String defaultLogTitle = "更新内容:\n "; 33 | 34 | /** 35 | * 发送消息到钉钉 36 | * 37 | * @param project 38 | * @param dataDTO 39 | */ 40 | public static void sendMsgToDingDing(BaseVariant variant, Project project, PgyUploadResult.DataDTO dataDTO, String gitLog) { 41 | SendDingParams dingParams = SendDingParams.getDingParamsConfig(project); 42 | if (PluginUtils.isEmpty(dingParams.accessToken)) { 43 | System.out.println("send to Dingding failure:accessToken is empty"); 44 | return; 45 | } 46 | String flavorStr = getFlavorInfo(variant); 47 | DingDingRequestBean requestBean = new DingDingRequestBean(); 48 | String title = dingParams.contentTitle; 49 | if (PluginUtils.isEmpty(title)) { 50 | title = defaultTitle; 51 | } 52 | StringBuilder titleStr = new StringBuilder(dataDTO.getBuildName()).append(" V").append(dataDTO.getBuildVersion()).append(flavorStr).append(" ").append(title); 53 | String text = dingParams.contentText; 54 | if (PluginUtils.isEmpty(text)) { 55 | text = defaultText; 56 | } 57 | if ("markdown".equals(dingParams.msgtype)) { 58 | requestBean.setMsgtype("markdown"); 59 | DingDingRequestBean.MarkDownDTO markDownBean = new DingDingRequestBean.MarkDownDTO(); 60 | markDownBean.setTitle(titleStr.toString()); 61 | StringBuilder contentStr = new StringBuilder("**").append(titleStr).append("** \n ").append(text).append(" "); 62 | contentStr.append("*").append(dataDTO.getBuildCreated()).append("*").append("\n"); 63 | String clickTxt = dingParams.clickTxt; 64 | if (PluginUtils.isEmpty(clickTxt)) { 65 | clickTxt = defaultClickText; 66 | } 67 | contentStr.append("[").append(clickTxt).append("](https://www.pgyer.com/").append(dataDTO.getBuildShortcutUrl()).append(")\n"); 68 | contentStr.append("![二维码](").append(dataDTO.getBuildQRCodeURL()).append(")\n"); 69 | if (!PluginUtils.isEmpty(gitLog) && dingParams.isSupportGitLog) { 70 | contentStr.append("### ").append(defaultLogTitle).append(gitLog); 71 | } 72 | markDownBean.setText(contentStr.toString()); 73 | DingDingRequestBean.AtDTO atBean = new DingDingRequestBean.AtDTO(); 74 | atBean.setIsAtAll(dingParams.isAtAll); 75 | requestBean.setAt(atBean); 76 | requestBean.setMarkdown(markDownBean); 77 | } else if ("actionCard".equals(dingParams.msgtype)) { 78 | requestBean.setMsgtype("actionCard"); 79 | DingDingRequestBean.ActionCardDTO actionCardBean = new DingDingRequestBean.ActionCardDTO(); 80 | actionCardBean.setTitle(titleStr.toString()); 81 | StringBuilder contentStr = new StringBuilder("**").append(titleStr).append("** \n ").append(text).append(" "); 82 | contentStr.append("*").append(dataDTO.getBuildCreated()).append("*").append("\n"); 83 | contentStr.append("![二维码](").append(dataDTO.getBuildQRCodeURL()).append(")\n"); 84 | if (!PluginUtils.isEmpty(gitLog) && dingParams.isSupportGitLog) { 85 | contentStr.append("### ").append(defaultLogTitle).append(gitLog); 86 | } 87 | actionCardBean.setText(contentStr.toString()); 88 | //0:按钮竖直排列 1:按钮横向排列 89 | actionCardBean.setBtnOrientation("0"); 90 | String clickTxt = dingParams.clickTxt; 91 | if (PluginUtils.isEmpty(clickTxt)) { 92 | clickTxt = defaultClickText; 93 | } 94 | actionCardBean.setSingleTitle(clickTxt); 95 | actionCardBean.setSingleURL("https://www.pgyer.com/" + dataDTO.getBuildShortcutUrl()); 96 | requestBean.setActionCard(actionCardBean); 97 | } else { 98 | requestBean.setMsgtype("link"); 99 | DingDingRequestBean.LinkDTO linkBean = new DingDingRequestBean.LinkDTO(); 100 | StringBuilder contentStr = new StringBuilder(text).append(dataDTO.getBuildCreated()); 101 | if (!PluginUtils.isEmpty(gitLog) && dingParams.isSupportGitLog) { 102 | contentStr.append("\n ").append(defaultLogTitle).append(gitLog); 103 | } 104 | linkBean.setText(contentStr.toString()); 105 | linkBean.setTitle(titleStr.toString()); 106 | linkBean.setPicUrl(dataDTO.getBuildQRCodeURL()); 107 | linkBean.setMessageUrl("https://www.pgyer.com/" + dataDTO.getBuildShortcutUrl()); 108 | requestBean.setLink(linkBean); 109 | } 110 | 111 | 112 | String json = JsonOutput.toJson(requestBean); 113 | System.out.println("send to Dingding request json:" + json); 114 | RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), json); 115 | Request request = new Request.Builder() 116 | .addHeader("Connection", "Keep-Alive") 117 | .addHeader("Charset", "UTF-8") 118 | .url("https://oapi.dingtalk.com/robot/send?access_token=" + dingParams.accessToken) 119 | .post(requestBody) 120 | .build(); 121 | try { 122 | Response response = HttpHelper.getOkHttpClient().newCall(request).execute(); 123 | if (response.isSuccessful() && response.body() != null) { 124 | String result = response.body().string(); 125 | System.out.println("send to Dingding result:" + result); 126 | } else { 127 | System.out.println("send to Dingding failure"); 128 | } 129 | System.out.println("*************** sendMsgToDing finish ***************"); 130 | } catch (Exception e) { 131 | System.out.println("send to Dingding failure " + e); 132 | } 133 | } 134 | 135 | /** 136 | * 发送消息到飞书 137 | * 138 | * @param project 139 | * @param dataDTO 140 | */ 141 | public static void sendMsgToFeishu(BaseVariant variant, Project project, PgyUploadResult.DataDTO dataDTO, String gitLog) { 142 | SendFeishuParams feishuParams = SendFeishuParams.getFeishuParamsConfig(project); 143 | String webHookHostUrl = feishuParams.webHookHostUrl; 144 | if (PluginUtils.isEmpty(webHookHostUrl)) { 145 | System.out.println("send to feishu failure:webHookHostUrl is empty"); 146 | return; 147 | } 148 | String flavorStr = getFlavorInfo(variant); 149 | String title = feishuParams.contentTitle; 150 | if (PluginUtils.isEmpty(title)) { 151 | title = defaultTitle; 152 | } 153 | StringBuilder titleStr = new StringBuilder(dataDTO.getBuildName()).append(" V").append(dataDTO.getBuildVersion()).append(flavorStr).append(" ").append(title); 154 | String text = feishuParams.contentText; 155 | if (PluginUtils.isEmpty(text)) { 156 | text = defaultText; 157 | } 158 | StringBuilder textStr = new StringBuilder("**").append(text).append("** ").append(dataDTO.getBuildCreated()).append(" \n"); 159 | FeiShuRequestBean feiShuRequestBean = new FeiShuRequestBean(); 160 | if ("interactive".equals(feishuParams.msgtype)) { 161 | FeiShuRequestBean.CardDTO cardDTO = new FeiShuRequestBean.CardDTO(); 162 | 163 | feiShuRequestBean.setMsg_type("interactive"); 164 | FeiShuRequestBean.CardDTO.ConfigDTO cardConfigBean = new FeiShuRequestBean.CardDTO.ConfigDTO(); 165 | cardConfigBean.setWide_screen_mode(true); 166 | cardConfigBean.setEnable_forward(true); 167 | cardDTO.setConfig(cardConfigBean); 168 | FeiShuRequestBean.CardDTO.HeaderDTO cardHeaderBean = new FeiShuRequestBean.CardDTO.HeaderDTO(); 169 | cardHeaderBean.setTemplate("green"); 170 | TextDTO cardHeaderTitleBean = new TextDTO(); 171 | cardHeaderTitleBean.setTag("plain_text"); 172 | cardHeaderTitleBean.setContent("\uD83D\uDE18 " + titleStr); 173 | cardHeaderBean.setTitle(cardHeaderTitleBean); 174 | cardDTO.setHeader(cardHeaderBean); 175 | List elementsDTOList = new ArrayList<>(); 176 | ElementsDTO elements1 = new ElementsDTO(); 177 | elements1.setTag("div"); 178 | TextDTO elements1TextBean = new TextDTO(); 179 | elements1TextBean.setTag("lark_md"); 180 | textStr.append("[").append(PluginUtils.isEmpty(feishuParams.clickTxt) ? defaultClickText : feishuParams.clickTxt) 181 | .append("]").append("(https://www.pgyer.com/").append(dataDTO.getBuildShortcutUrl()).append(")").append(" "); 182 | textStr.append("[查看下载二维码]").append("(").append(dataDTO.getBuildQRCodeURL()).append(")"); 183 | if (feishuParams.isAtAll) { 184 | textStr.append(" \n").append(""); 185 | } 186 | elements1TextBean.setContent(textStr.toString()); 187 | elements1.setText(elements1TextBean); 188 | elementsDTOList.add(elements1); 189 | ElementsDTO elements2 = new ElementsDTO(); 190 | elements2.setTag("hr"); 191 | elementsDTOList.add(elements2); 192 | if (!PluginUtils.isEmpty(gitLog) && feishuParams.isSupportGitLog) { 193 | ElementsDTO elements3 = new ElementsDTO(); 194 | elements3.setTag("div"); 195 | TextDTO elements3TextBean = new TextDTO(); 196 | elements3TextBean.setTag("lark_md"); 197 | StringBuilder logStrBuilder = new StringBuilder("**").append(defaultLogTitle).append("**").append(gitLog); 198 | elements3TextBean.setContent(logStrBuilder.toString()); 199 | elements3.setText(elements3TextBean); 200 | elementsDTOList.add(elements3); 201 | ElementsDTO elements4 = new ElementsDTO(); 202 | elements4.setTag("hr"); 203 | elementsDTOList.add(elements4); 204 | } 205 | ElementsDTO elements5 = new ElementsDTO(); 206 | elements5.setTag("action"); 207 | List actionsList = new ArrayList<>(); 208 | ElementsDTO actionBtnDownBtn = new ElementsDTO(); 209 | actionBtnDownBtn.setTag("button"); 210 | actionBtnDownBtn.setType("primary"); 211 | actionBtnDownBtn.setUrl("https://www.pgyer.com/" + dataDTO.getBuildShortcutUrl()); 212 | TextDTO actionDownBtnText = new TextDTO(); 213 | actionDownBtnText.setTag("plain_text"); 214 | actionDownBtnText.setContent(PluginUtils.isEmpty(feishuParams.clickTxt) ? defaultClickText : feishuParams.clickTxt); 215 | actionBtnDownBtn.setText(actionDownBtnText); 216 | actionsList.add(actionBtnDownBtn); 217 | 218 | ElementsDTO actionBtnQRCodeBtn = new ElementsDTO(); 219 | actionBtnQRCodeBtn.setTag("button"); 220 | actionBtnQRCodeBtn.setType("primary"); 221 | actionBtnQRCodeBtn.setUrl(dataDTO.getBuildQRCodeURL()); 222 | TextDTO actionQRCodeBtnText = new TextDTO(); 223 | actionQRCodeBtnText.setTag("plain_text"); 224 | actionQRCodeBtnText.setContent("查看下载二维码"); 225 | actionBtnQRCodeBtn.setText(actionQRCodeBtnText); 226 | actionsList.add(actionBtnQRCodeBtn); 227 | elements5.setActions(actionsList); 228 | elementsDTOList.add(elements5); 229 | cardDTO.setElements(elementsDTOList); 230 | feiShuRequestBean.setCard(cardDTO); 231 | } else { 232 | FeiShuRequestBean.ContentDTO contentDTO = new FeiShuRequestBean.ContentDTO(); 233 | 234 | feiShuRequestBean.setMsg_type("post"); 235 | FeiShuRequestBean.ContentDTO.PostDTO postDTO = new FeiShuRequestBean.ContentDTO.PostDTO(); 236 | 237 | FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO.ContentBean contentBeanText = new FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO.ContentBean(); 238 | contentBeanText.setTag("text"); 239 | contentBeanText.setText(textStr.toString()); 240 | FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO.ContentBean contentBeanA = new FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO.ContentBean(); 241 | contentBeanA.setTag("a"); 242 | contentBeanA.setText(PluginUtils.isEmpty(feishuParams.clickTxt) ? defaultClickText : feishuParams.clickTxt); 243 | contentBeanA.setHref("https://www.pgyer.com/" + dataDTO.getBuildShortcutUrl()); 244 | List contentBeans = new ArrayList<>(); 245 | contentBeans.add(contentBeanText); 246 | contentBeans.add(contentBeanA); 247 | if (feishuParams.isAtAll) { 248 | FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO.ContentBean contentBeanAt = new FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO.ContentBean(); 249 | contentBeanAt.setTag("at"); 250 | contentBeanAt.setUser_id("all"); 251 | contentBeans.add(contentBeanAt); 252 | } 253 | List> zhCnContentList = new ArrayList<>(); 254 | zhCnContentList.add(contentBeans); 255 | if (!PluginUtils.isEmpty(gitLog) && feishuParams.isSupportGitLog) { 256 | List contentGitLogBeans = new ArrayList<>(); 257 | FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO.ContentBean contentGitLogBeanText = new FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO.ContentBean(); 258 | contentGitLogBeanText.setTag("text"); 259 | contentGitLogBeanText.setText("** " + defaultLogTitle + gitLog); 260 | contentGitLogBeans.add(contentGitLogBeanText); 261 | zhCnContentList.add(contentGitLogBeans); 262 | } 263 | 264 | FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO zhCnDTO = new FeiShuRequestBean.ContentDTO.PostDTO.ZhCnDTO(); 265 | zhCnDTO.setTitle(titleStr.toString()); 266 | zhCnDTO.setContent(zhCnContentList); 267 | postDTO.setZh_cn(zhCnDTO); 268 | contentDTO.setPost(postDTO); 269 | feiShuRequestBean.setContent(contentDTO); 270 | } 271 | 272 | 273 | /** 274 | * text 文本 275 | * post 富文本 发送富文本消息 276 | * image 图片 上传图片 277 | * share_chat 分享群名片 群名片 278 | * interactive 消息卡片 消息卡片消息 279 | */ 280 | String json = JsonOutput.toJson(feiShuRequestBean); 281 | System.out.println("send to feishu request json:" + json); 282 | RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), json); 283 | Request request = new Request.Builder() 284 | .addHeader("Connection", "Keep-Alive") 285 | .addHeader("Charset", "UTF-8") 286 | .url(webHookHostUrl) 287 | .post(requestBody) 288 | .build(); 289 | try { 290 | Response response = HttpHelper.getOkHttpClient().newCall(request).execute(); 291 | if (response.isSuccessful() && response.body() != null) { 292 | String result = response.body().string(); 293 | System.out.println("send to feishu result:" + result); 294 | } else { 295 | System.out.println("send to feishu failure"); 296 | } 297 | System.out.println("*************** sendMsgToFeishu finish ***************"); 298 | } catch (Exception e) { 299 | System.out.println("send to feishu failure " + e); 300 | } 301 | } 302 | 303 | /** 304 | * 发送消息到微信群 305 | * 306 | * @param project 307 | * @param dataDTO 308 | */ 309 | public static void sendMsgToWeiXinGroup(BaseVariant variant, Project project, PgyUploadResult.DataDTO dataDTO, String gitLog) { 310 | SendWeixinGroupParams weixinGroupParams = SendWeixinGroupParams.getWeixinGroupConfig(project); 311 | String webHookUrl = weixinGroupParams.webHookUrl; 312 | if (PluginUtils.isEmpty(webHookUrl)) { 313 | System.out.println("send to weixin group failure:webHookUrl is empty"); 314 | return; 315 | } 316 | String flavorStr = getFlavorInfo(variant); 317 | String contentTitle = weixinGroupParams.contentTitle; 318 | if (PluginUtils.isEmpty(contentTitle)) { 319 | contentTitle = defaultTitle; 320 | } 321 | String contentText = weixinGroupParams.contentText; 322 | if (PluginUtils.isEmpty(contentText)) { 323 | contentText = defaultText; 324 | } 325 | WXGroupRequestBean wxGroupRequestBean = new WXGroupRequestBean(); 326 | StringBuilder markStr = new StringBuilder(); 327 | if ("text".equals(weixinGroupParams.msgtype)) { 328 | wxGroupRequestBean.setMsgtype("text"); 329 | WXGroupRequestBean.TextDTO textDTO = new WXGroupRequestBean.TextDTO(); 330 | markStr.append(dataDTO.getBuildName()).append(" V").append(dataDTO.getBuildVersion()); 331 | markStr.append(flavorStr).append(contentTitle).append("\n").append("下载链接:https://www.pgyer.com/") 332 | .append(dataDTO.getBuildShortcutUrl()).append("\n").append(contentText); 333 | textDTO.setContent(markStr.toString()); 334 | if (weixinGroupParams.isAtAll) { 335 | List mentionedList = new ArrayList<>(); 336 | mentionedList.add("@all"); 337 | textDTO.setMentioned_list(mentionedList); 338 | textDTO.setMentioned_mobile_list(mentionedList); 339 | } 340 | wxGroupRequestBean.setText(textDTO); 341 | } else if ("news".equals(weixinGroupParams.msgtype)) { 342 | wxGroupRequestBean.setMsgtype("news"); 343 | WXGroupRequestBean.NewsDTO newsDTO = new WXGroupRequestBean.NewsDTO(); 344 | WXGroupRequestBean.NewsDTO.ArticlesDTO articlesDTO = new WXGroupRequestBean.NewsDTO.ArticlesDTO(); 345 | markStr.append(dataDTO.getBuildName()).append(" V").append(dataDTO.getBuildVersion()); 346 | markStr.append(flavorStr).append(" ").append(dataDTO.getBuildCreated()); 347 | articlesDTO.setTitle(markStr.toString()); 348 | String desStr = "最新开发测试包已上传,请下载测试吧! "; 349 | if (!PluginUtils.isEmpty(contentTitle) && !PluginUtils.isEmpty(contentText)) { 350 | desStr = contentTitle + " \n" + contentText; 351 | } else if (!PluginUtils.isEmpty(contentTitle)) { 352 | desStr = contentTitle; 353 | } else if (!PluginUtils.isEmpty(contentText)) { 354 | desStr = contentText; 355 | } 356 | articlesDTO.setDescription(desStr); 357 | articlesDTO.setUrl("https://www.pgyer.com/" + dataDTO.getBuildShortcutUrl()); 358 | articlesDTO.setPicurl(dataDTO.getBuildQRCodeURL()); 359 | List articlesDTOList = new ArrayList<>(); 360 | articlesDTOList.add(articlesDTO); 361 | newsDTO.setArticles(articlesDTOList); 362 | wxGroupRequestBean.setNews(newsDTO); 363 | } else { 364 | wxGroupRequestBean.setMsgtype("markdown"); 365 | WXGroupRequestBean.MarkdownDTO markdownDTO = new WXGroupRequestBean.MarkdownDTO(); 366 | markStr.append("**").append(dataDTO.getBuildName()).append("** V").append(dataDTO.getBuildVersion()) 367 | .append(flavorStr).append(" ").append(dataDTO.getBuildCreated()).append(" \n") 368 | .append(contentTitle).append(" \n").append(contentText).append(" \n") 369 | .append("[下载链接,点击下载](https://www.pgyer.com/") 370 | .append(dataDTO.getBuildShortcutUrl()).append(")"); 371 | if (!PluginUtils.isEmpty(gitLog) && weixinGroupParams.isSupportGitLog) { 372 | markStr.append(" \n").append("**").append(defaultLogTitle).append("**").append(gitLog); 373 | } 374 | markdownDTO.setContent(markStr.toString()); 375 | wxGroupRequestBean.setMarkdown(markdownDTO); 376 | } 377 | String json = JsonOutput.toJson(wxGroupRequestBean); 378 | System.out.println("send to WeiXin group request json:" + json); 379 | RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), json); 380 | Request request = new Request.Builder() 381 | .addHeader("Connection", "Keep-Alive") 382 | .addHeader("Charset", "UTF-8") 383 | .url(webHookUrl) 384 | .post(requestBody) 385 | .build(); 386 | try { 387 | Response response = HttpHelper.getOkHttpClient().newCall(request).execute(); 388 | if (response.isSuccessful() && response.body() != null) { 389 | String result = response.body().string(); 390 | System.out.println("send to WeiXin group result:" + result); 391 | } else { 392 | System.out.println("send to WeiXin group failure"); 393 | } 394 | System.out.println("*************** sendMsgToWeiXinGroup finish ***************"); 395 | } catch (Exception e) { 396 | System.out.println("send to WeiXin group failure " + e); 397 | } 398 | 399 | } 400 | 401 | private static String getFlavorInfo(BaseVariant variant) { 402 | String flavor = PluginUtils.isEmpty(variant.getName()) ? variant.getFlavorName() : variant.getName(); 403 | return PluginUtils.isEmpty(flavor) ? "" : "(FlavorName:" + flavor + ")"; 404 | } 405 | } 406 | --------------------------------------------------------------------------------