×

Android Sutdio ( Intellij ) 插件开发

96
JerryShen
2017.03.02 17:24* 字数 1711

先给代码:
https://github.com/shenjiajun53/TinyPic 喜欢请给个star

TinyPic 介绍

一个Intellij的插件,不仅仅是在Android Studio中使用,可以在所有Jetbrains的IDE中使用
功能:压缩图片资源,一次最多压缩500张
压缩的核心功能是TinyPng这个网站提供的

https://tinypng.com/

但是这个网站一次只能上传20张图片,所以你需要上传下载,上传下载重复工作。
好在这个网站提供了api可以压缩图片。

在开发者页面下申请api key。对于一个key,每月有500次的免费压缩额度,如果压缩超过了
500张图片,就不能使用了。需要另外付费。但是申请这个api特别简单,填下邮箱,用户名就行,多申请
两个邮箱。1000张图片也妥妥够了。
这里推荐google个十分钟邮箱,不需要注册,只能使用十分钟,用来收一下验证码很方便

tinypng_develop.png

使用方式

1.在File->Settings->Plugins里下载插件 TinyPic

2.安装完后重启,在Tools目录下找到TinyPic

location.png

3.输入在 https://tinypng.com/developers 申请的api key

input_key.png

4.选择图片,可以选择图片,或者选择文件夹或者同时选中,反正是遍历文件夹下的图片,筛选jpg和png
,key的剩余次数

select_images.png

5.压缩进度

progress.png

6.超过500次的提示(后续会考虑加入 生成压缩的信息的文件,因为大家都用git,其实也不是很必要)

warning.png

为什么做这个插件

之前在做apk瘦身的时候考虑到,相比代码,图片才是占了更多大小,减少图片大小有两步。
1.用Android Studio的lint工具,自动删除没有引用的资源,这一步需要注意,有些名字是拼接起来的资源也可能被删除。
2.压缩图片,但是考虑到没有好用的工具,我决定自己开发个插件

下面是开发过程

http://www.jetbrains.org/intellij/sdk/docs/index.html 这个是官方文档

这是我看过的最烂的文档之一,但是Intellij插件开发这方面文档很少,将就着看吧

1.下载Intellij社区版,社区版是免费的,而且调试插件更方便,所以推荐这个

https://www.jetbrains.com/idea/

2.new project 选择 Plugin

new_project.png

3.项目右击->Open Module Settings->SDKs 选择Intellij安装目录

add_sdk.png

4.添加action,这个action其实就是个用户操作的事件,比如说点击按钮,
需要填几个信息,Id,Name,插件位置

add_action.png

创建完之后生成了一个Action类,另外在plugin.xml文件下,多了一段代码

<actions>
        <!-- Add your actions here -->
        <action id="com.shenjiajun.TinyAction" class="com.shenjiajun.TinyAction" text="TinyPic" description="com.shenjiajun.TinyAction"
                icon="/icons/drawable-mdpi/ic_photo_size_select_large_pink_500_18dp.png">
            <add-to-group group-id="ToolsMenu" anchor="last"/>
        </action>
    </actions>

class 就是真的Class名字,icon是我自己添加的,加个图标更显眼,group-id是插件位置,这里是Tools目录下,最后一个

回到TinyAction中

 String api = Messages.showInputDialog(project, "请输入API KEY", "TinyPic", Messages.getQuestionIcon());
        if (TextUtils.isEmpty(api)) {
            return;
        }

会弹出一个Dialog,输入申请的key,点击确定,返回String,参数project是个上下文对象?context类似的吧。

接下来就要用到TinyPNG这个网站提供的API了,真正的压缩功能就是他们提供的 https://tinypng.com/developers/reference/java

还是 项目右击->Open Module Settings->Libraries->From Maven,
搜索tinify,选择最新的,下载到项目中。
完全没Android Studio的Gradle依赖简单。
现在就能引用到这个API了,首先初始化

  Tinify.setKey(api);
FileChooserDescriptor descriptor = new FileChooserDescriptor(true, true, false, false, false, true);
        VirtualFile[] selectedFiles = FileChooser.chooseFiles(descriptor, project, null);
        if (selectedFiles.length == 0) {
            return;
        }

这段代码会跳出一个文件选择Dialog,注意参数,选择文件或者文件夹,可以多选。
返回结果是这个plugin sdk的VirtualFile类,因为是多选,返回的是个数组。这个VirtualFile可以是文件,或者文件夹,所以下一步很关键了。

    private void filterAllPictures(VirtualFile[] selectedFiles) {
        for (int i = 0; i < selectedFiles.length; i++) {
            VirtualFile selectedFile = selectedFiles[i];
            if (selectedFile.isDirectory()) {
                VirtualFile[] directoryChildren = selectedFile.getChildren();
                filterAllPictures(directoryChildren);
            } else if (selectedFile.getName().endsWith("jpg") || selectedFile.getName().endsWith("png")) {
                logger.info("path=" + selectedFile.getPath());
                pictureFiles.add(selectedFile);
                if (i >= selectedFiles.length - 1) {
                    return;
                }
            }
        }
    }

这段代码就比较复杂了啊,功能是筛选出所有的图片文件,放在ArrayList中。
首先循环一下,
1.如果是文件夹,获取这个文件夹下的所有文件,又可能是文件或文件夹,递归
2.如果是文件,判断文件后缀是不是jpg或png,跳出

接下来新建个类,继承自plugin sdk的DialogWraper,就是自定义Dialog了,
功能很简单,就是加个进度条和500次使用超出的文字说明

因为是Java代码,用的是JavaSE的Swing,有没有回到上古时代的感觉。Intelij的IDE全是用Java开发的,这么恶心的东西。。我也是服了

    @Nullable
    @Override
    protected JComponent createCenterPanel() {
        jPanel = new JPanel();

        jProgressBar = new JProgressBar(SwingConstants.HORIZONTAL, 0, maxImages);
        jProgressBar.setString(currentIndex + "/" + maxImages);
        jProgressBar.setValue(currentIndex);
        jProgressBar.setStringPainted(true);

        reminderText = new JTextArea("每月500张图片限制已用完,请获取新KEY");
        reminderText.setVisible(false);
//        reminderText.setForeground(Color.CYAN);
        reminderText.setBackground(new Color(255, 255, 255, 0));

        jPanel.setLayout(new GridLayout(2, 1));
        jPanel.add(jProgressBar);
        jPanel.add(reminderText);
        return jPanel;
    }

重写这个方法,就是Dialog里显示的内容。
JPanel就是一个Layout,添加个ProgressBar和TextArea,TextArea默认不显示,跟Android还是挺像的

        filterAllPictures(selectedFiles);
        tinyFiles();

        progressDialog = new ProgressDialog(project);
        progressDialog.setTitle("上传进度");
        progressDialog.setMaxImages(pictureFiles.size());
        progressDialog.revalidate();

        progressDialog.show();

先筛选出图片,然后 tinyFiles()里开线程发送压缩图片的请求,
Dialog的显示要放在后面。在Plugin里Dialog会阻塞主线程,如果先显示Dialog,后面代码就要在Dialog消失后再执行
考虑到前面的代码,先弹出输入Key的Dialog,点击确定之后Tinify初始化
弹出文件选择Dialog,选择之后才执行筛选图片代码。
Android的Dialog虽然会让activity pause,但不会把activity卡住啊,真是坑

    private void tinyFiles() {
        cancelTiny = false;
        for (int i = 0; i < pictureFiles.size(); i++) {
            currentIndex = 0;
            VirtualFile virtualFile = pictureFiles.get(i);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Source source = null;
//                    logger.info("path=" + virtualFile.getPath());
                    try {
                        if (!cancelTiny) {
                            source = Tinify.fromFile(virtualFile.getPath());
                            Result result = source.result();
                            logger.info("result size=" + result.size() + " mediaType=" + result.mediaType() + " " + result.toString());
                            source.toFile(virtualFile.getPath());

                            currentIndex++;
                            progressDialog.setCurrentIndex(currentIndex);
                            progressDialog.revalidate();

                        }
                    } catch (Exception e1) {
//                        logger.warning(e1.toString());
                        e1.printStackTrace();
                        if (e1.toString().contains("AccountException")) {
                            cancelTiny = true;
                            progressDialog.showError();
                        }
                    }
                }
            }).start();
        }
    }

压缩图片就简单了,这个api不是异步的,所以要放在新线程里,然后更新下progress,如果报错了,大概就是500次免费次数到了。

发布

1.在plugin.xml里填写信息,版本号之类的
注意添加depends,如果不添加,这个插件只能在Intelij和AndroidStudio中使用

    <depends>com.intellij.modules.lang</depends>

Module  Product
com.intellij.modules.java   IntelliJ IDEA
com.intellij.modules.ultimate   IntelliJ IDEA Ultimate Edition
com.intellij.modules.androidstudio  Android Studio
com.intellij.modules.appcode    AppCode
com.intellij.modules.cidr.lang  AppCode, CLion
com.intellij.modules.cidr.debugger  AppCode, CLion, RubyMotion
com.intellij.modules.clion  CLion
com.intellij.modules.database   IntelliJ IDEA Ultimate Edition, PhpStorm, RubyMine, PyCharm, DataGrip
com.intellij.modules.python PyCharm
com.intellij.modules.ruby   RubyMine

这个是插件兼容性的表,如果用了某些功能,就只能对应IDE使用

2.打包,Build->Prepare Plugin XXX For Deployment 生成Jar包
3.发布,https://plugins.jetbrains.com/ 登录,上传插件,审核一般要两天

说下感悟:
1.有什么事真的是想做就去做,以前我一直觉得开发插件挺高端了,但是从零开始开发一个这样简单的插件就花了一天时间。
2.作为一个Android开发,表示Java学的真是烂,或者说Android提供了好多方便的api,AsyncTask,Handler之类的。有很多Java基础的知识平时工作中根本遇不到,跟JavaEE同事聊天中发现他们接触的很多,我希望过时的东西慢慢被淘汰,哈哈

日记本
Web note ad 1