preface

We have learned a lot about Groovy basics, Gradle tasks and projects, and now we can use these basics to customize plug-ins.

I believe many companies have such a plug-in in their projects, which can realize automatic packaging, reinforcement and upload to a platform (generally companies will build their own FTP server) and send relevant links to the nail group messages through the nail robot.

The reinforcement adopted by the author here is 360 reinforcement. The reinforced APK will be uploaded to dandelion, and then relevant information returned after uploading to dandelion will be sent to Dingding. Therefore, there are three steps in total, namely, three tasks will be defined, which are as follows:

  • strengthening
  • Upload dandelions
  • Send to the pin message

Create plug-in Module

  • AS version: Arctic fox
  • AGP version: 4.2.0
  • Development language: Groovy
  • The Groovy Version: 3.0.9

BuildSrc = ‘buildSrc’; buildSrc = ‘buildSrc’; buildSrc = ‘buildSrc’;

Keep the.gitignore file in the red box if you need to upload it to a remote repository. Otherwise, when creating a Module, delete all files except the SRC directory and build.gradle. Then create the directory and code as SRC -> main -> groovy -> package name, and delete all the code under build.gradle.

Here I’m going to add all the build.gradle code directly, and the comments will be very detailed:

plugins {
    id 'groovy'
    id 'maven-publish'
    id 'java-gradle-plugin'
}

dependencies {
    // Introduce AGP, then use AppExtension
    implementation 'com. Android. Tools. Build: gradle: 4.2.0'
    // Retrofit is used because network requests are required
    implementation 'com. Squareup. Retrofit2: retrofit: 2.9.0'
    implementation 'com. Squareup. Retrofit2: converter - gson: 2.9.0'
}

gradlePlugin {
    plugins {
        standaloneGradlePlugins {
            // Configure the plugin ID. The configured ID will generate an xxx.properties file at compile time, which is the plugin ID that you need to import when using the plugin
            id = "com.imiyar.upload"
            // Configure the plug-in's implementation class
            implementationClass = "com.imiyar.upload.UploadApkPlugin"
        }
    }

}

publishing {
    publications {
        / / configure the plug-in id and version number: com. Imiyar. Upload: uploadApk: 1.0.0
        // See the official documentation link below for details
        maven(MavenPublication) {
            groupId = 'com.imiyar.upload'
            artifactId = 'uploadApk'
            version = '1.0.0'

            from components.java // Upload the source jar package}}}Copy the code

This section focuses on the three plugins used in plugins closures, the rest of which are related configuration code

  • Groovy: This is a minor issue, and is intended to support the Groovy language.

  • Maven-publish: Publishes plug-in code to Maven.The official documentation

  • Java-gradle-plugin: Used to assist in the development of Gradle plug-ins. It will automatically apply the Java library plug-ingradleApi()Dependencies are added to the configuration and executed during the taskapiPerforms validation of plug-in metadata during.

strengthening

After creating the plug-in Module, we can start writing code by creating a Plugin class that implements the Plugin interface and implementing the apply method for that interface:

UploadApkPlugin

class UploadApkPlugin implements Plugin<Project> {
    // AppExtension extension name in AGP
    static final String ANDROID_EXTENSION = "android"
    // Customize an extension to configure the information required by the custom plug-in
    static final String UPLOAD_APK_EXTENSION = "uploadApk"

    @Override
    void apply(Project project) {
        . / / when in build gradle configured in UploadApkExtension attribute, if directly through UploadApkExtension getXxx () is unable to get get value
        AfterEvaluate is called. This closure will be called after gradle configuration is complete, i.e. after the build.gradle file has been parsed
        project.afterEvaluate {

        }
    }
}
Copy the code

The apply method is called when the plugin ID is defined in build.gradle, so if we need the extension attribute we need to get it in the project.afterEvaluate closure, otherwise it will be empty.

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'com.imiyar.upload'
}

uploadApk {
    
}
Copy the code

For example, when we call id ‘com.imiyar. Upload ‘, we execute the apply method and wait for Gradle configuration to complete before we get the properties we defined in the uploadApk closure (now empty, Because we haven’t started defining our own extensions yet).

Next we customize an extension class:

UploadApkExtension

class UploadApkExtension {

    // -------------- Hardening related extensions --------------
    String reinforceUserName // 360 Hardened user name
    String reinforcePassword // 360 Hardening password
    String reinforceFilePath // 360 Hardening jar package path
    String outputDirectory    // Output apK directory after hardening
    boolean isOpenReinforce = true // Whether hardening is required. The default value is true

}
Copy the code

After defining our extension class and the required attributes, we need to create our extension in Plugin and start writing our hardened task-related logic, so we can continue writing the above plug-in class

UploadApkPlugin

class UploadApkPlugin implements Plugin<Project> {.@Override
    void apply(Project project) {
        // Create the uploadApk extension
        project.extensions.create(UPLOAD_APK_EXTENSION, UploadApkExtension.class)
        
        project.afterEvaluate {
            // AppExtension is an extension created by the Android plugin, corresponding to the Android {} closure in the App Module
            AppExtension androidExtension = project.extensions.findByName(ANDROID_EXTENSION)
            
            // Get variants of the APK package. ApplicationVariants will come with debug and Release variants by default
            androidExtension.applicationVariants.all { ApplicationVariant variant ->
                if (variant.name.equalsIgnoreCase("release")) {
                    // Create a Task for hardening
                    ReinforceTask reinforceTask = project.tasks.create("reinforceRelease", ReinforceTask)
                    reinforceTask.init(variant)
                }
            }
        }
    }
}
Copy the code

Note that there are two variants of APK by default: DEBUG and Release. Of course, you can also create multiple variants by yourself. This is only for the hardening operation of the release package, so when creating the Task, only check whether the variant name is “release”. Next we will write the logic of hardening Task.

Before writing the hardening logic, you can download the compression package of the corresponding platform from 360 hardening, because we need the jiagu.jar package inside. After downloading, we can take a look at the decompression file directory:

Jar and help.txt. You can open help.txt to read the corresponding code later. If I don’t want to open it, I can also put screenshots here:

Note: If you find error messages such as insufficient permissions when using this JAR package later, you can download the old version of 360 hardening to test.

ReinforceTask

class ReinforceTask extends DefaultTask {

    @Internal
    ApplicationVariant variant // Get the variant of APK

    void init(ApplicationVariant variant) {
        this.variant = variant
        description = "Reinforce Release Apk"
        // Define the group to which the task belongs. If you do not define a group, the task will be in the Other group
        group = "uploadApk"
    }

    @TaskAction
    void action() {
        UploadApkExtension uploadApkExtension = project.extensions.findByName(UploadApkPlugin.UPLOAD_APK_EXTENSION)
        // Get the signature information of the variant for later re-signing
        SigningConfig signingConfig = variant.signingConfig
        String apkFilePath
        variant.outputs.all { BaseVariantOutput output ->
            Outputs apK -> outputs APk -> outputs apK -> release
            apkFilePath = output.outputFile.absolutePath
        }

        // Invoke the cli tool to perform 360 hardening login
        project.exec { ExecSpec spec ->
            spec.commandLine(
                    "java"."-jar", uploadApkExtension.reinforceFilePath,
                    "-login", uploadApkExtension.reinforceUserName, uploadApkExtension.reinforcePassword)
        }

        // Invoke the cli tool to obtain the signature information in 360 hardening
        if (signingConfig) {
            project.exec { ExecSpec spec ->
                spec.commandLine("java"."-jar", uploadApkExtension.reinforceFilePath,
                        "-importsign", signingConfig.storeFile.absolutePath, signingConfig.storePassword,
                        signingConfig.keyAlias, signingConfig.keyPassword)
            }
        }

        // Use the cli tool to perform 360 Hardening
        project.exec { ExecSpec spec ->
            spec.commandLine("java"."-jar", uploadApkExtension.reinforceFilePath,
                    "-jiagu", apkFilePath, uploadApkExtension.outputDirectory, "-autosign")}}}Copy the code

Creating a Task first inherits from DefaultTask, and the @TaskAction annotation is used here to indicate that the method is the entry point for executing the Task, and that the logic will be executed when the Task is double-clicked.

The relevant codes for calling the command line in the following are compiled according to the help document in help.txt just mentioned. The only thing to note here is that 360 hardening requires you to get your signature information first, and then re-sign after hardening, so it must be inThe build of the app. GradleFill in the relevant ones belowsigningConfigsOtherwise, the hardened file will be an unsigned APK file.

Now that the ReinforceTask is done, let’s fix the code for UploadApkPlugin:

class UploadApkPlugin implements Plugin<Project> {.@Override
    void apply(Project project) {
        ...
        project.afterEvaluate {
            ...
            androidExtension.applicationVariants.all { ApplicationVariant variant ->
                if (variant.name.equalsIgnoreCase("release")) {
                    // Define a Task for hardening
                    ReinforceTask reinforceTask = project.tasks.create("reinforceRelease", ReinforceTask)
                    reinforceTask.init(variant)
                    
                    // Modify task dependencies
                    variant.getAssembleProvider().get().dependsOn(project.getTasks().findByName("clean"))
                    reinforceTask.dependsOn(variant.getAssembleProvider().get())
                }
            }
        }
    }
}
Copy the code

An assembleRelease must be mandatory or mandatory. An assembleRelease must be mandatory or mandatory. So finally we just need to hit the reinforceTask and do the packing and reinforcement.

Next we can open Gradle in the upper right corner of the AS and see the following directory:

Click on publishToMavenLocal to publish the plugin to the local Maven repository. If you open the local Maven repository, you can see the newly published plugin.

  • For Mac: The default path to the local Maven repository isM2 / repository/package name
  • For Windows: The default path to the local Maven repository isC: / / UsersAdministrator/m2 / epository/package name

To configure our plugin, open build.gradle in app Module and add the following script:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    // The plug-in ID just defined
    id 'com.imiyar.upload'
}

uploadApk {
    // 360 is related to hardening
    reinforceUserName "360 hardened account"
    reinforcePassword "360 Hardened password"
    reinforceFilePath "The absolute path to the 360 hardened JAR package just downloaded"
    // Directory output after hardening
    outputDirectory "/Users/sherry/ASProjects/UploadApkPlugin/app/build/outputs/apk/release/"
    // Whether to enable hardening
    isOpenReinforce true
}

// Then add signature-related information to the Android closure
android {
    signingConfigs {
        release {
            storeFile file('.. /upload.keystore')
            storePassword '123456'
            keyAlias 'upload'
            keyPassword '123456'
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}}Copy the code

Finally, we need to configure the plugin in the root directory of the project and add the mavenLocal() plugin to load the local Maven repository:

buildscript {
    repositories {
        ...
        mavenLocal()
    }
    dependencies {
        ...
        classpath "Com. Imiyar. Upload: uploadApk: 1.0.0"
    }
}

allprojects {
    repositories {
        ...
        mavenLocal()
    }
}
Copy the code

After adding this information, we can click Sync Now and see the Task we just defined in Gradle to the right of AS

Double-click the reinforceRelease, you can do the packing and reinforcement. Here are some information printed during the reinforcement. If the reinforcement fails, it will also print an error message.

Let’s take a look at the reinforced APK:

You can also try to see if the installation is successful or decompile to see if it is truly hardened. I have tested these and now we are done

Upload dandelions

After the reinforcement, we need to upload it to the distribution platform, whether it is your own or a third party’s. Only after uploading it to the distribution platform can we get the link of apK for others to download and install. Here we use dandelion.

So how do you upload a packed APK to a dandelion? Dandelion document portal

UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension

UploadApkExtension

class UploadApkExtension {.// ------------ Upload dandelion related extensions ------------
    String apiKey  // API Key
    String appName // Application name
}
Copy the code

Create a Task that implements the logic of uploading dandelions. This Task is named PgyUploadTask.

class PgyUploadTask extends DefaultTask {

    @Internal
    ApplicationVariant variant

    void init(ApplicationVariant variant) {
        this.variant = variant
        description = "Upload apk to Pgyer"
        group = "uploadApk"
    }

    @TaskAction
    void action() {
        // Get our custom extension
        UploadApkExtension uploadApkExtension = project.extensions.findByName(UploadApkPlugin.UPLOAD_APK_EXTENSION)
        // Get the Android extension AppExtension
        AppExtension appExtension = project.extensions.findByName(UploadApkPlugin.ANDROID_EXTENSION)

        // This is just normal printing, nothing special, just convenient to see related information when executing task
        println("# # # # # # # # # # # # # # # # # # # # # # # # # # # # to upload the dandelion # # # # # # # # # # # # # # # # # # # # # # # # # # # # #")
        println("# applicationId : " + variant.getApplicationId())
        println("# versionName : " + appExtension.defaultConfig.versionName)
        println("# versionCode : " + appExtension.defaultConfig.versionCode)
        println("# appName : " + uploadApkExtension.appName)
        println("# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #")

        File outputFile
        // Get the output directory defined when hardening
        File directory = new File(uploadApkExtension.outputDirectory)
        // Check if it is a directory. If so, traverse the files in the directory and get the hardened APK
        if (directory.isDirectory()) {
            File[] files = directory.listFiles()
            for (File file : files) {
                // If hardening is enabled, check whether the APK file in the output path is the hardened APK file
                // Since we do not know the name rules generated by 360 after the reinforcement, we can distinguish them by matching "jiagu" and ".apk"
                if (uploadApkExtension.isOpenReinforce) {
                    if (file.getName().contains("jiagu") && file.getName().endsWith(".apk")) {
                        outputFile = file
                        break}}else {
                    // If hardening is not enabled, there will be only one. Apk file in this directory
                    if (file.getName().endsWith(".apk")) {
                        outputFile = file
                        break
                    }
                }
            }
        }
    }
}

Copy the code

Here we also create a Task that inherits from DefaultTask and start defining our upload logic. AssembleRelease contains only the apK files in the output directory, so you don’t have to worry about contains matching files that are incorrect.

Next we need to use the network request, the author for convenience, just a simple Java language encapsulation, do not consider any performance issues, directly related to the code posted and do not explain, I believe that the network request Retrofit part of the world is already familiar.

ApiFactory

public class ApiFactory {

    private static volatile ApiFactory mInstance;

    private ApiFactory(a) {}public static ApiFactory getInstance(a) {
        if (mInstance == null) {
            synchronized (ApiFactory.class) {
                if (mInstance == null) {
                    mInstance = newApiFactory(); }}}return mInstance;
    }

    public <T> T create(String baseUrl, Class<T> clazz) {
        return new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(newClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(clazz);
    }

    private OkHttpClient newClient(a) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS);
        return builder.build();
    }

    public MultipartBody.Part getFilePart(String mediaType, File file) {
        return MultipartBody.Part.createFormData("file", file.getAbsoluteFile().getName(),
                RequestBody.create(MediaType.parse(mediaType), file));
    }

    public RequestBody getTextBody(String text) {
        return RequestBody.create(MediaType.parse("text/plain"), text); }}Copy the code

ApiConstants

public interface ApiConstants {
    String PGY_BASE_URL = "https://www.pgyer.com/apiv2/";
}
Copy the code

PgyUploadService

public interface PgyUploadService {

    @Multipart
    @POST("app/upload")
    Call<ResponseBody> uploadFile(@Part("_api_key") RequestBody key,
                                  @Part("buildName") RequestBody buildName,
                                  @Part MultipartBody.Part file);
}

Copy the code

Now that we have simply wrapped the Retrofit-related code and the interface that needs to be called, we are ready to perform the actual upload operation, and we will now add the upload code to the PgyUploadTask above:

PgyUploadTask

class PgyUploadTask extends DefaultTask {
    @TaskAction
    void action() {
        ...
        // Add the following code at the end
        if(outputFile ! =null) {
            // This part needs no further explanation
            PgyUploadService pgyService = ApiFactory.getInstance().create(ApiConstants.PGY_BASE_URL, PgyUploadService.class)
            // Upload the file
            Response<ResponseBody> appResponse = pgyService.uploadFile(
                ApiFactory.getInstance().getTextBody(uploadApkExtension.apiKey), 
                ApiFactory.getInstance().getTextBody(uploadApkExtension.appName),
                ApiFactory.getInstance().getFilePart("application/vnd.android.package-archive", outputFile)
            ).execute()

            // Prints the result returned after upload
            println("\nappResponse: ${appResponse.body().string()}")}else {
            println("Could not found the apk file")}}}Copy the code

The parameters that need to be passed in here are explained in the document above. If you don’t understand the function of parameters, you can refer to the document of dandelion

After defining the Task we need to go back to UploadApkPlugin and create our Task:

class UploadApkPlugin implements Plugin<Project> {.@Override
    void apply(Project project) {
        ...
        project.afterEvaluate {
            ...
            androidExtension.applicationVariants.all { ApplicationVariant variant ->
                if (variant.name.equalsIgnoreCase("release")) {
                    // Define a Task for hardening
                    ReinforceTask reinforceTask = project.tasks.create("reinforceRelease", ReinforceTask)
                    reinforceTask.init(variant)
                    
                    // Define a Task for uploading APK to dandelion
                    PgyUploadTask pgyUploadTask = project.tasks.create("pgyUploadRelease", PgyUploadTask)
                    pgyUploadTask.init(variant)
                    
                    // Modify task dependencies
                    variant.getAssembleProvider().get().dependsOn(project.getTasks().findByName("clean"))
                    reinforceTask.dependsOn(variant.getAssembleProvider().get())
                    Make the task depend on the reinforceTask
                    pgyUploadTask.dependsOn(reinforceTask)
                }
            }
        }
    }
}
Copy the code

It should be mentioned here because we don’t want to reinforce every package, so if we don’t need to reinforce, we can set isOpenReinforce in UploadApkExtension to false and add it from the dependencies above.

Next we just need to modify our app build.gradle and add the extension we just set up

build.gradle

uploadApk {
    // 360 is related to hardening
    reinforceUserName "360 hardened account"
    reinforcePassword "360 Hardened password"
    reinforceFilePath "The absolute path to the 360 hardened JAR package just downloaded"
    // Directory output after hardening
    outputDirectory "/Users/sherry/ASProjects/UploadApkPlugin/app/build/outputs/apk/release/"
    // Whether to enable hardening
    isOpenReinforce true
    
    // Upload dandelion
    apiKey "If there is no apiKey, you need to apply on the official document given above."
    appName "UploadApkPlugin"
}
Copy the code

Click on publishToMavenLocal to publish the plugin id to the local Maven repository and Sync Now. This is the only way to keep the plug-in code in the Maven repository up to date.

Now let’s do pgyUploadRelease, because we’re defining pgyUploadRelease dependsOn reinforceRelease So we will do the reinforceRelease first when we do the pgyUploadRelease, so don’t worry about not doing the reinforcement.

Take a look at the relevant log printed after uploading to dandelion, we will return the file link of APK after uploading successfully, because the printed log is too long, the screenshot cannot be taken later, you can look at the document, and put the document portal again here

Send to the pin message

When upload the dandelion we must hope to share out to let everyone download, so here we use nailing for sharing, this advantage when we pack in the implementation of the time we also don’t need to always staring at the packaging progress, because when finished packing after upload messages will be automatically sent to the nailing group, So we know that we’re done packing.

First, we need to look at the document portal that connects to the nail. According to the document, we need to first build a group in the nail, and then customize a robot, and then call the interface and pass the parameter according to the document.

It is important to note that in the security Settings section, if you select “checkmark”, you will have to read another document when accessing the interface.Secure the portal, the security Settings used by the author are user-defined keywords, which means that only the parameters you send contain this keyword can be sent successfully.

UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension UploadApkExtension

class UploadApkExtension {
    // ----------- Send pin messages related extensions -----------
    String webHook // pin the WebHook address of the robot
}
Copy the code

Here, it’s easy to just add webhooks, and if you check in your security Settings, you’ll need to add Secret. With the extension defined, we can start defining our Task that sends a pin message, named SendMsgToDingTalkTask:

class SendMsgToDingTalkTask extends DefaultTask {

    @Internal
    ApplicationVariant variant
    
    // Need to send a short link to pin for everyone to open, need to be concatenated with "http://www.pgyer.com/"
    static String mShortCutUrl
    // Need to send to Nail nail for everyone to scan the download of the TWO-DIMENSIONAL code picture link
    static String mQRCodeURL

    void init(ApplicationVariant variant) {
        this.variant = variant
        description = "Send message to DingTalk"
        group = "uploadApk"
    }
    
    // Upload the short link returned after successful upload to dandelion and download the QR code link
    static void setUrl(String shortCutUrl, String qrUrl) {
        mShortCutUrl = shortCutUrl
        mQRCodeURL = qrUrl
    }
    
    @TaskAction
    void action() {
        UploadApkExtension uploadApkExtension = project.extensions.findByName(UploadApkPlugin.UPLOAD_APK_EXTENSION)
        AppExtension appExtension = project.extensions.findByName(UploadApkPlugin.ANDROID_EXTENSION)
        
        // If you don't understand the meaning of these parameters, you can look at the following parameters
        Link link = new Link()
        link.picUrl = mQRCodeURL
        link.messageUrl = "http://www.pgyer.com/" + mShortCutUrl
        link.title = uploadApkExtension.appName + "Official edition"
        link.text = Version of the "${appExtension. DefaultConfig. VersionName}"

        DingTalkRequest request = new DingTalkRequest(link, "link")

        // Just like apK uploading to dandelion, we need to define an interface before performing network operations
        The SendDingTalkService code is shown below
        SendDingTalkService dingTalkService = ApiFactory.getInstance().create(ApiConstants.DING_TALK_BASE_URL, SendDingTalkService.class)
        Response<ResponseBody> appResponse = dingTalkService.sendMsgToDingTalk(uploadApkExtension.webHook, request)
                .execute()

        // Print here to see the result
        println("\nDingTalkMsgResponse:" + new Gson().toJson(appResponse.body().string()))
    }
    
    // Define the parameters that need to be passed to send the pin message
    static class DingTalkRequest {
        String msgtype
        Link link

        DingTalkRequest(Link link, String msgtype) {
            this.link = link
            this.msgtype = msgtype
        }
    }

    static class Link {
        String picUrl
        String messageUrl
        String title
        String text
    }
    
}
Copy the code

Just like the task created before, we need to get the extension we defined and the Android extension in AGP first. Here we pass the Link type. For details, you can see the Link type in the above document, which has the corresponding parameter description. Take a screenshot of the parameter description:

Let’s define the interface we use, as follows:

ApiConstants

public interface ApiConstants {
    String DING_TALK_BASE_URL = "https://oapi.dingtalk.com/";
}
Copy the code

SendDingTalkService

public interface SendDingTalkService {

    @POST("robot/send")
    Call<ResponseBody> sendMsgToDingTalk(
            @Query("access_token") String accessToken,
            @Body SendMsgToDingTalkTask.DingTalkRequest request
    );

}
Copy the code

We have finished the code of sending the pins. Now we need to modify the code of PgyUploadTask. After all, we need to send the short link and the TWO-DIMENSIONAL code link.

PgyUploadTask

class PgyUploadTask extends DefaultTask {

    @TaskAction
    void action() {
        // Now we can modify this part of the code, you can compare the modification
        if(outputFile ! =null) {
            PgyUploadService pgyService = ApiFactory.getInstance().create(ApiConstants.PGY_BASE_URL, PgyUploadService.class)
            Response<ResponseBody> appResponse = pgyService.uploadFile(ApiFactory.getInstance().getTextBody(uploadApkExtension.apiKey), ApiFactory.getInstance().getTextBody(uploadApkExtension.appName),
                    ApiFactory.getInstance().getFilePart("application/vnd.android.package-archive", outputFile))
                    .execute()
            
            // The following is the code that needs to be modified
            String result = appResponse.body().string()
            // Use Gson to convert the returned data into the desired object
            PgyResponse response = new Gson().fromJson(result, PgyResponse.class)
            if(response ! =null) {
                // Pass the relevant information to SendMsgToDingTalkTask
                SendMsgToDingTalkTask.setUrl(response.data.buildShortcutUrl, response.data.buildQRCodeURL)
            }
        } else {
            println("Could not found the apk file")}}// Add the object definition to the end of PgyUploadTask
    // This object corresponds to the data returned after successfully uploading the dandelion
    static class PgyResponse {
        public int code
        public String message
        public PgyDetail data

        // It returns a lot of data, which is not needed in this article
        static class PgyDetail {
            public String buildShortcutUrl
            public String buildQRCodeURL
            public String buildIcon
        }
    }
}
Copy the code

After modifying PgyUploadTask, we also need to modify our UploadApkPlugin, after all, we haven’t created the task yet, just add a few lines of code

class UploadApkPlugin implements Plugin<Project> {.@Override
    void apply(Project project) {
        ...
        project.afterEvaluate {
            ...
            androidExtension.applicationVariants.all { ApplicationVariant variant ->
                if (variant.name.equalsIgnoreCase("release")) {
                    // Define a Task for hardening
                    ReinforceTask reinforceTask = project.tasks.create("reinforceRelease", ReinforceTask)
                    reinforceTask.init(variant)
                    
                    // Define a Task for uploading APK to dandelion
                    PgyUploadTask pgyUploadTask = project.tasks.create("pgyUploadRelease", PgyUploadTask)
                    pgyUploadTask.init(variant)
                    
                    // Define the task that sends the pin message
                    SendMsgToDingTalkTask dingTalkTask = project.tasks.create("sendMsgRelease", SendMsgToDingTalkTask)
                    dingTalkTask.init()
                    
                    // Modify task dependencies
                    variant.getAssembleProvider().get().dependsOn(project.getTasks().findByName("clean"))
                    reinforceTask.dependsOn(variant.getAssembleProvider().get())
                    pgyUploadTask.dependsOn(reinforceTask)
                    // Depends on pgyUploadTask
                    dingTalkTask.dependsOn(pgyUploadTask)
                }
            }
        }
    }
}
Copy the code

We need to add the extension webHook~ we just defined

build.gradle

uploadApk {
    ...
    
    // Pin the robot
    webHook "Go to the robot management page you just created and you can see this webHook."
}
Copy the code

Okay, remember what we were talking about? Comment out the related plugin id and extension code before releasing the plugin. Instead of repeating this step, let’s look at the result of executing the task:

Click sengMsgRelease:

Take a look at the print:

Messages received from Nail:

Click on this message to go to the download link for dandelion

All right, let’s finish

Finally, post the Github address for all the code in this article