├── .gitignore ├── META-INF └── plugin.xml ├── demo ├── clk.gif ├── findViewById.gif ├── inflateFindViewById.gif └── toast.gif ├── readme.md └── src ├── org └── sssta │ └── androidtools │ ├── action │ ├── AbstractFvbiGeneratorAction.java │ ├── FieldsFvbiGeneratorAction.java │ ├── LocalFvbiGeneratorAction.java │ └── generator │ │ ├── AbstractFvbiGenerator.java │ │ ├── FieldsFvbiGenerator.java │ │ └── LocalFvbiGenerator.java │ ├── model │ └── ViewModel.java │ ├── posfix │ ├── AndroidPostfixTemplateProvider.java │ ├── internal │ │ ├── AbstractRichStringBasedPostfixTemplate.java │ │ ├── RichChooserStringBasedPostfixTemplate.java │ │ └── RichTopmostStringBasedPostfixTemplate.java │ ├── macro │ │ ├── TagMacro.java │ │ └── ViewListenerMacro.java │ └── template │ │ ├── ClickListenerPostfixTemplate.java │ │ └── ToastPostfixTemplate.java │ └── util │ ├── AndroidFQClass.java │ ├── AndroidPostfixTemplatesUtils.java │ ├── CommonUtil.java │ ├── ImportUtil.java │ ├── LayoutUtil.java │ └── Levenshtein.java └── postfixTemplates ├── ClickListenerPostfixTemplate └── description.html └── ToastPostfixTemplate └── description.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion 4 | 5 | *.iml 6 | 7 | ## Directory-based project format: 8 | .idea/ 9 | # if you remove the above rule, at least ignore the following: 10 | 11 | # User-specific stuff: 12 | # .idea/workspace.xml 13 | # .idea/tasks.xml 14 | # .idea/dictionaries 15 | 16 | # Sensitive or high-churn files: 17 | # .idea/dataSources.ids 18 | # .idea/dataSources.xml 19 | # .idea/sqlDataSources.xml 20 | # .idea/dynamic.xml 21 | # .idea/uiDesigner.xml 22 | 23 | # Gradle: 24 | # .idea/gradle.xml 25 | # .idea/libraries 26 | 27 | # Mongo Explorer plugin: 28 | # .idea/mongoSettings.xml 29 | 30 | ## File-based project format: 31 | *.ipr 32 | *.iws 33 | 34 | ## Plugin-specific files: 35 | 36 | # IntelliJ 37 | /out/ 38 | 39 | # mpeltonen/sbt-idea plugin 40 | .idea_modules/ 41 | 42 | # JIRA plugin 43 | atlassian-ide-plugin.xml 44 | 45 | # Crashlytics plugin (for Android Studio and IntelliJ) 46 | com_crashlytics_export_strings.xml 47 | crashlytics.properties 48 | crashlytics-build.properties 49 | 50 | 51 | ### Java template 52 | *.class 53 | 54 | # Mobile Tools for Java (J2ME) 55 | .mtj.tmp/ 56 | 57 | # Package Files # 58 | *.jar 59 | *.war 60 | *.ear 61 | 62 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 63 | hs_err_pid* 64 | 65 | 66 | -------------------------------------------------------------------------------- /META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | org.sssta.androidtools 3 | Android Tools 4 | 1.2 5 | SSSTA 6 | 7 | 9 | Supporting:
10 | 1.generate Show Toast by toast postfix
11 | 2.generate findViewById according layout
12 | ]]>
13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | com.intellij.modules.lang 26 | org.jetbrains.android 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 |
-------------------------------------------------------------------------------- /demo/clk.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cauchywei/AndroidToolsPlugin/b400765045aa81483f4a2af577003dce1d9d700e/demo/clk.gif -------------------------------------------------------------------------------- /demo/findViewById.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cauchywei/AndroidToolsPlugin/b400765045aa81483f4a2af577003dce1d9d700e/demo/findViewById.gif -------------------------------------------------------------------------------- /demo/inflateFindViewById.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cauchywei/AndroidToolsPlugin/b400765045aa81483f4a2af577003dce1d9d700e/demo/inflateFindViewById.gif -------------------------------------------------------------------------------- /demo/toast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cauchywei/AndroidToolsPlugin/b400765045aa81483f4a2af577003dce1d9d700e/demo/toast.gif -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #Android Tools ( developing... ) 2 | ==================== 3 |
4 | 5 | Android Tools is an Android Studio plugin helping developer reducing heavy and repeat works in development. 6 | 7 |
8 | 9 | ####DEMO 10 | 11 | ![Tost](demo/toast.gif) 12 | ![ClickListener](demo/clk.gif) 13 | ![fvbi](demo/findViewById.gif) 14 | ![inflate fvbi](demo/inflateFindViewById.gif) 15 | 16 |
17 | 18 | ####**Postfix Extension** 19 | 20 | Postfix |Comment 21 | ----------|--------- 22 | `.toast` |generate `Toast.makeText(this, msg,Toast.LENGTH_SHORT).show();` 23 | `.clk` |generate `view.onClickListener(listener);` according to context 24 | ... | 25 | 26 | 27 | 28 | ####**FindViewById Generating** 29 | You can generate two type `findViewById` from `setContentView(R.layout.activity_login)` statement or `layoutInflater.inflate(R.layout.activity_login,parent)` statement 30 | 31 | * **local** :e.g. generating `TextView usernameTextView = (TextView)findViewById(R.id.textView_username)` 32 | * **field** :e.g. generating `private TextView mUsernameTextView;` and
33 | `mUsernameTextView = (TextView)findViewById(R.id.textView_username);` two parts 34 | 35 | ######**Naming Rule** 36 | * **Activity/Fragment**: `_` e.g. `LoginActivity` 37 | * **field**: `m__` e.g. `mUsernameTextView` 38 | * **local var**: `_` e.g. `usernameTextView` 39 | * **resourse id**: `__` e.g. `textView_login_username` 40 | 41 | Assuming we have an Activity named `LoginActivity` whose's layout file named `activiy_login.xml` 42 | 43 | A TextView in `activity_layout.xml` 44 | 45 | ```xml 46 | 50 | ``` 51 | 52 | ######**The process of name-converting is as follow** 53 | 54 | 1. Remove the type prefix of view id according to tag name 55 | * `textView_login_username` -> `login_username` 56 | * abbreviattional prefix also works e.g. `tv_login_username` -> `login_username` 57 | 2. Remove the module according to layout file (`activity_login.xml`) name 58 | * `login_username` -> `username` 59 | * abbreviattional prefix also works e.g. `lgn_username` -> `username` 60 | 61 | 3. Append ClassName according to tag name 62 | * `username` -> `usernameTextView` or `mUsernameTextView` 63 | 64 | 65 | finally. `textView_login_username` -> `usernameTextView` or `mUsernameTextView` 66 | 67 | Next Plan 68 | ---------- 69 | ####**Generate Adapter and Model according item layout** 70 | 71 | 72 | License 73 | ----------- 74 | ``` 75 | Copyright (c) 2015 [cauchywei@gmail.com] 76 | 77 | Licensed under the Apache License, Version 2.0 (the "License”); 78 | you may not use this file except in compliance with the License. 79 | You may obtain a copy of the License at 80 | 81 | http://www.apache.org/licenses/LICENSE-2.0 82 | 83 | Unless required by applicable law or agreed to in writing, software 84 | distributed under the License is distributed on an "AS IS" BASIS, 85 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 86 | See the License for the specific language governing permissions and 87 | limitations under the License. 88 | ``` -------------------------------------------------------------------------------- /src/org/sssta/androidtools/action/AbstractFvbiGeneratorAction.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.action; 2 | 3 | import com.intellij.codeInsight.CodeInsightActionHandler; 4 | import com.intellij.codeInsight.generation.actions.BaseGenerateAction; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.util.TextRange; 8 | import com.intellij.patterns.PlatformPatterns; 9 | import com.intellij.psi.*; 10 | import com.intellij.psi.util.PsiTreeUtil; 11 | import com.intellij.psi.util.PsiUtilBase; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.sssta.androidtools.util.LayoutUtil; 14 | 15 | /** 16 | * Created by cauchywei on 15/8/17. 17 | */ 18 | public abstract class AbstractFvbiGeneratorAction extends BaseGenerateAction { 19 | 20 | public AbstractFvbiGeneratorAction() { 21 | super(null); 22 | } 23 | 24 | public AbstractFvbiGeneratorAction(CodeInsightActionHandler handler) { 25 | super(handler); 26 | } 27 | 28 | 29 | @Override 30 | protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { 31 | 32 | int offset = editor.getCaretModel().getOffset(); 33 | PsiElement psiElement = file.findElementAt(offset); 34 | 35 | if(!PlatformPatterns.psiElement().inside(PsiMethodCallExpression.class).accepts(psiElement)) { 36 | return false; 37 | } 38 | 39 | PsiMethodCallExpression psiMethodCallExpression = PsiTreeUtil.getParentOfType(psiElement, PsiMethodCallExpression.class); 40 | if(psiMethodCallExpression == null) { 41 | return false; 42 | } 43 | 44 | PsiMethod psiMethod = psiMethodCallExpression.resolveMethod(); 45 | if(psiMethod == null) { 46 | return false; 47 | } 48 | 49 | if (psiMethod.getName().equals("setContentView") || psiMethod.getName().equals("inflate")){ 50 | return true; 51 | } 52 | 53 | return super.isValidForFile(project, editor, file); 54 | } 55 | 56 | @Override 57 | public void actionPerformedImpl(@NotNull Project project,@NotNull Editor editor) { 58 | 59 | PsiFile layoutXmlFile = LayoutUtil.findLayoutXmlFile(project, editor); 60 | generate(project,editor,layoutXmlFile); 61 | 62 | } 63 | 64 | public void generate(@NotNull Project project, @NotNull Editor editor, PsiFile xmlFile) { 65 | 66 | final PsiElement context = PsiUtilBase.getElementAtCaret(editor); 67 | 68 | 69 | PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(context,PsiMethodCallExpression.class); 70 | 71 | boolean found = false; 72 | PsiElement psiElement = callExpression; 73 | 74 | while(psiElement.getNextSibling()!=null){ 75 | psiElement = psiElement.getNextSibling(); 76 | if (psiElement.getText().trim().equals(";")){ 77 | found = true; 78 | break; 79 | }else if (!psiElement.getText().trim().equals("")){ 80 | break; 81 | } 82 | } 83 | 84 | TextRange textRange = psiElement.getTextRange(); 85 | editor.getCaretModel().moveToOffset(found?textRange.getEndOffset():textRange.getStartOffset()); 86 | 87 | 88 | insertStatement(project,editor,context,xmlFile); 89 | 90 | } 91 | 92 | public abstract void insertStatement(@NotNull Project project,@NotNull Editor editor,PsiElement context,PsiFile xmlFile); 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/action/FieldsFvbiGeneratorAction.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.action; 2 | 3 | import com.intellij.codeInsight.template.Template; 4 | import com.intellij.codeInsight.template.TemplateEditingAdapter; 5 | import com.intellij.codeInsight.template.TemplateManager; 6 | import com.intellij.openapi.actionSystem.ActionManager; 7 | import com.intellij.openapi.actionSystem.ActionPlaces; 8 | import com.intellij.openapi.actionSystem.AnAction; 9 | import com.intellij.openapi.actionSystem.impl.ActionManagerImpl; 10 | import com.intellij.openapi.command.WriteCommandAction; 11 | import com.intellij.openapi.editor.Editor; 12 | import com.intellij.openapi.project.Project; 13 | import com.intellij.openapi.ui.playback.commands.ActionCommand; 14 | import com.intellij.psi.*; 15 | import com.intellij.psi.util.PsiTreeUtil; 16 | import org.jetbrains.annotations.NotNull; 17 | import org.sssta.androidtools.action.generator.FieldsFvbiGenerator; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * Created by cauchywei on 15/8/17. 23 | */ 24 | public class FieldsFvbiGeneratorAction extends AbstractFvbiGeneratorAction { 25 | 26 | FieldsFvbiGenerator fieldsFvbiGenerator = new FieldsFvbiGenerator(); 27 | 28 | 29 | @Override 30 | public void insertStatement(@NotNull Project project,@NotNull Editor editor,PsiElement context,PsiFile xmlFile) { 31 | 32 | String viewParentName = null; 33 | PsiLocalVariable variable = PsiTreeUtil.getParentOfType(context, PsiLocalVariable.class); 34 | if (variable != null) { 35 | viewParentName = variable.getName(); 36 | } 37 | 38 | Template template = fieldsFvbiGenerator.generator(project, editor, xmlFile, viewParentName); 39 | final List psiFields = fieldsFvbiGenerator.generatorFields(project, editor, xmlFile,context); 40 | final PsiClass psiClass = PsiTreeUtil.getTopmostParentOfType(context,PsiClass.class); 41 | 42 | WriteCommandAction.runWriteCommandAction(project, new Runnable() { 43 | @Override 44 | public void run() { 45 | PsiField[] fields = psiClass.getFields(); 46 | 47 | for (PsiField newField : psiFields) { 48 | boolean exist = false; 49 | for (PsiField field : fields) { 50 | if (field.getName().equals(newField.getName())) { 51 | exist = true; 52 | break; 53 | } 54 | } 55 | 56 | if (!exist) { 57 | psiClass.add(newField); 58 | } 59 | } 60 | } 61 | }); 62 | 63 | 64 | TemplateManager.getInstance(project).startTemplate(editor, template, new TemplateEditingAdapter() { 65 | @Override 66 | public void templateFinished(Template template, boolean brokenOff) { 67 | 68 | 69 | 70 | 71 | // // format and add ; 72 | final ActionManager actionManager = ActionManagerImpl.getInstance(); 73 | final String editorCompleteStatementText = "EditorCompleteStatement"; 74 | final AnAction action = actionManager.getAction(editorCompleteStatementText); 75 | actionManager.tryToExecute(action, ActionCommand.getInputEvent(editorCompleteStatementText), null, ActionPlaces.UNKNOWN, true); 76 | } 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/action/LocalFvbiGeneratorAction.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.action; 2 | 3 | import com.intellij.codeInsight.template.Template; 4 | import com.intellij.codeInsight.template.TemplateEditingAdapter; 5 | import com.intellij.codeInsight.template.TemplateManager; 6 | import com.intellij.openapi.actionSystem.ActionManager; 7 | import com.intellij.openapi.actionSystem.ActionPlaces; 8 | import com.intellij.openapi.actionSystem.AnAction; 9 | import com.intellij.openapi.actionSystem.impl.ActionManagerImpl; 10 | import com.intellij.openapi.editor.Editor; 11 | import com.intellij.openapi.project.Project; 12 | import com.intellij.openapi.ui.playback.commands.ActionCommand; 13 | import com.intellij.psi.PsiElement; 14 | import com.intellij.psi.PsiFile; 15 | import com.intellij.psi.PsiLocalVariable; 16 | import com.intellij.psi.util.PsiTreeUtil; 17 | import org.jetbrains.annotations.NotNull; 18 | import org.sssta.androidtools.action.generator.LocalFvbiGenerator; 19 | 20 | /** 21 | * Created by cauchywei on 15/8/17. 22 | */ 23 | public class LocalFvbiGeneratorAction extends AbstractFvbiGeneratorAction { 24 | 25 | LocalFvbiGenerator localFvbiGenerator = new LocalFvbiGenerator(); 26 | 27 | 28 | @Override 29 | public void insertStatement(@NotNull Project project,@NotNull Editor editor,PsiElement context,PsiFile xmlFile) { 30 | 31 | String viewParentName = null; 32 | PsiLocalVariable variable = PsiTreeUtil.getParentOfType(context, PsiLocalVariable.class); 33 | if (variable != null) { 34 | viewParentName = variable.getName(); 35 | } 36 | 37 | Template template = localFvbiGenerator.generator(project, editor, xmlFile, viewParentName); 38 | 39 | TemplateManager.getInstance(project).startTemplate(editor, template, new TemplateEditingAdapter() { 40 | @Override 41 | public void templateFinished(Template template, boolean brokenOff) { 42 | // // format and add ; 43 | final ActionManager actionManager = ActionManagerImpl.getInstance(); 44 | final String editorCompleteStatementText = "EditorCompleteStatement"; 45 | final AnAction action = actionManager.getAction(editorCompleteStatementText); 46 | actionManager.tryToExecute(action, ActionCommand.getInputEvent(editorCompleteStatementText), null, ActionPlaces.UNKNOWN, true); 47 | } 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/action/generator/AbstractFvbiGenerator.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.action.generator; 2 | 3 | import com.intellij.codeInsight.template.Template; 4 | import com.intellij.codeInsight.template.TemplateManager; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiFile; 8 | import org.sssta.androidtools.model.ViewModel; 9 | import org.sssta.androidtools.util.LayoutUtil; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Created by cauchywei on 15/8/17. 15 | */ 16 | public abstract class AbstractFvbiGenerator { 17 | 18 | public abstract String getFvbiFormat(); 19 | 20 | public abstract String getFvbiStatementTemplate(String format, ViewModel view,String viewParentName); 21 | 22 | public String processTemplateString(String template){ 23 | return template; 24 | } 25 | 26 | public Template generateTemplate(Project project ,String template){ 27 | TemplateManager templateManager = TemplateManager.getInstance(project); 28 | return templateManager.createTemplate("", "",processTemplateString(template)); 29 | } 30 | 31 | public Template generator(Project project, Editor editor, PsiFile xmlFile, String viewParentName) { 32 | 33 | 34 | List views = LayoutUtil.getContainingIdViewsInXml(xmlFile); 35 | viewParentName = viewParentName == null?"":viewParentName + "."; 36 | 37 | String statementFormat = getFvbiFormat(); 38 | StringBuilder templateString = new StringBuilder("\n"); 39 | 40 | for (ViewModel view:views){ 41 | String fvbiStatement = getFvbiStatementTemplate(statementFormat,view,viewParentName); 42 | templateString.append(fvbiStatement); 43 | } 44 | 45 | templateString.append("$end$"); 46 | 47 | return generateTemplate(project,templateString.toString()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/action/generator/FieldsFvbiGenerator.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.action.generator; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.*; 6 | import com.intellij.psi.util.PsiTreeUtil; 7 | import org.sssta.androidtools.model.ViewModel; 8 | import org.sssta.androidtools.util.LayoutUtil; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by cauchywei on 15/8/18. 15 | */ 16 | public class FieldsFvbiGenerator extends AbstractFvbiGenerator { 17 | 18 | @Override 19 | public String getFvbiFormat() { 20 | return "%s = (%s) %sfindViewById(%s);\n"; 21 | } 22 | 23 | @Override 24 | public String getFvbiStatementTemplate(String statementFormat,ViewModel view, String viewParentName) { 25 | String varName = view.getFieldName(); 26 | String clazz = view.getFqClazz(); 27 | String fqId = view.getFqId(); 28 | return String.format(statementFormat,varName,clazz,viewParentName,fqId); 29 | } 30 | 31 | public List generatorFields(Project project, Editor editor, PsiFile xmlFile,PsiElement context) { 32 | 33 | List views = LayoutUtil.getContainingIdViewsInXml(xmlFile); 34 | PsiClass psiClass = PsiTreeUtil.getParentOfType(context, PsiClass.class); 35 | String fieldFormat = "private %s %s;\n"; 36 | List fieldStatements = new ArrayList<>(); 37 | PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); 38 | 39 | for (ViewModel view:views){ 40 | String fvbiStatement = String.format(fieldFormat,view.getClazz(),view.getFieldName()); 41 | fieldStatements.add(factory.createFieldFromText(fvbiStatement, psiClass)); 42 | } 43 | 44 | return fieldStatements; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/action/generator/LocalFvbiGenerator.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.action.generator; 2 | 3 | import org.sssta.androidtools.model.ViewModel; 4 | 5 | /** 6 | * Created by cauchywei on 15/8/17. 7 | */ 8 | public class LocalFvbiGenerator extends AbstractFvbiGenerator { 9 | 10 | @Override 11 | public String getFvbiFormat() { 12 | return "%s %s = (%s) %sfindViewById(%s);\n"; 13 | } 14 | 15 | @Override 16 | public String getFvbiStatementTemplate(String statementFormat,ViewModel view, String viewParentName) { 17 | String varName = view.getLocalVarName(); 18 | String clazz = view.getFqClazz(); 19 | return String.format(statementFormat,view.getClazz(),varName,clazz,viewParentName,view.getFqId()); 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/model/ViewModel.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.model; 2 | 3 | import com.intellij.openapi.util.text.StringUtil; 4 | import org.sssta.androidtools.util.CommonUtil; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by cauchywei on 15/8/17. 13 | */ 14 | public class ViewModel { 15 | 16 | private String id; 17 | private String fqId; 18 | 19 | private String clazz; 20 | private String fqClazz; 21 | 22 | private String from; 23 | 24 | 25 | public ViewModel(String from,String id, String fqClazz) { 26 | setFrom(from); 27 | setId(id); 28 | setFqClazz(fqClazz); 29 | } 30 | 31 | public String getId() { 32 | return id; 33 | } 34 | 35 | public void setId(String id) { 36 | 37 | if (id.matches("^@\\+?id/.*")){ 38 | this.id = id.substring(id.indexOf('/')+1,id.length()); 39 | } 40 | 41 | if (this.id.contains(".")){ 42 | this.id = this.id.substring(this.id.lastIndexOf('.'),this.id.length()); 43 | } 44 | 45 | setFqId("R.id."+this.id); 46 | } 47 | 48 | public String getClazz() { 49 | return clazz; 50 | } 51 | 52 | public void setClazz(String clazz) { 53 | this.clazz = clazz; 54 | } 55 | 56 | public String getFrom() { 57 | return from; 58 | } 59 | 60 | public void setFrom(String from) { 61 | if (from.contains(".")){ 62 | this.from = from.split("\\.")[0]; 63 | } 64 | this.from = from; 65 | } 66 | 67 | public String getFqId() { 68 | return fqId; 69 | } 70 | 71 | public void setFqId(String fqId) { 72 | this.fqId = fqId; 73 | } 74 | 75 | public String getFqClazz() { 76 | return fqClazz; 77 | } 78 | 79 | public void setFqClazz(String fqClazz) { 80 | 81 | if (fqClazz.contains(".")) { 82 | this.fqClazz = fqClazz; 83 | }else if ((fqClazz.equals("View")) || (fqClazz.equals("ViewGroup"))) { 84 | this.fqClazz = "android.view." + fqClazz; 85 | }else { 86 | this.fqClazz = "android.widget." + fqClazz; 87 | } 88 | 89 | String[] split = fqClazz.split("\\."); 90 | setClazz(split[split.length - 1]); 91 | } 92 | 93 | public String getLocalVarName(){ 94 | // List clazzNames = CommonUtil.splitCamelName(getClazz()); 95 | String clazzName = getClazz(); 96 | List splitIds = CommonUtil.splitUnderscoreName(getId()); 97 | List splitFrom = CommonUtil.splitUnderscoreName(getFrom()); 98 | List result; 99 | 100 | if (splitIds.isEmpty()) 101 | return getClazz(); 102 | 103 | do { 104 | 105 | //remove the type prefix e.g. activity_login -> login 106 | if (!splitFrom.isEmpty()) { 107 | if (CommonUtil.nameMatch(splitFrom.get(0), "activity") || CommonUtil.nameMatch(splitFrom.get(0), "fragment") || CommonUtil.nameMatch(splitFrom.get(0), "item")) { 108 | splitFrom.remove(0); 109 | } 110 | } 111 | 112 | result = new ArrayList<>(Arrays.asList(new String[splitIds.size()])); 113 | Collections.copy(result,splitIds); 114 | 115 | //remove the type prefix e.g TextView: textView_login_user_name -> login_user_name 116 | if (CommonUtil.nameMatch(clazzName.toLowerCase(),splitIds.get(0).toLowerCase())){ 117 | splitIds.remove(0); 118 | } 119 | // for (String clazzName : clazzNames) { 120 | // if (CommonUtil.nameMatch(splitIds.get(0), clazzName)) { 121 | // splitIds.remove(0); 122 | // } 123 | // } 124 | 125 | 126 | if (splitIds.isEmpty()){ 127 | break; 128 | } 129 | 130 | //remove scope name e.g. login_user_name (from activity_login) -> user_name 131 | int minLen = Math.min(splitFrom.size(), splitIds.size()); 132 | result = new ArrayList<>(Arrays.asList(new String[splitIds.size()])); 133 | Collections.copy(result,splitIds); 134 | 135 | for (int i = 0; i < minLen; i++) { 136 | if (CommonUtil.nameMatch(splitFrom.get(0), splitIds.get(0))) { 137 | splitIds.remove(0); 138 | } else { 139 | break; 140 | } 141 | } 142 | 143 | if (splitIds.isEmpty()){ 144 | break; 145 | }else { 146 | result = splitIds; 147 | } 148 | 149 | 150 | }while (false); 151 | 152 | StringBuilder stringBuilder = new StringBuilder(); 153 | for (String s:result) { 154 | stringBuilder.append(StringUtil.capitalize(s)); 155 | } 156 | 157 | stringBuilder.append(getClazz()); 158 | 159 | return StringUtil.decapitalize(stringBuilder.toString()); 160 | 161 | } 162 | 163 | public String getFieldName(){ 164 | return "m" + StringUtil.capitalize(getLocalVarName()); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/posfix/AndroidPostfixTemplateProvider.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.posfix; 2 | 3 | import com.intellij.codeInsight.template.postfix.templates.JavaPostfixTemplateProvider; 4 | import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate; 5 | import com.intellij.util.containers.ContainerUtil; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.sssta.androidtools.posfix.template.ClickListenerPostfixTemplate; 8 | import org.sssta.androidtools.posfix.template.ToastPostfixTemplate; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * Created by cauchywei on 15/8/15. 15 | */ 16 | public class AndroidPostfixTemplateProvider extends JavaPostfixTemplateProvider { 17 | private final HashSet templates; 18 | 19 | public AndroidPostfixTemplateProvider() { 20 | templates = ContainerUtil.newHashSet( 21 | new ToastPostfixTemplate(), 22 | new ClickListenerPostfixTemplate() 23 | ); 24 | } 25 | 26 | @NotNull 27 | @Override 28 | public Set getTemplates() { 29 | return templates; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/posfix/internal/AbstractRichStringBasedPostfixTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Bob Browning 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.sssta.androidtools.posfix.internal; 17 | 18 | import com.intellij.codeInsight.template.Template; 19 | import com.intellij.codeInsight.template.TemplateEditingAdapter; 20 | import com.intellij.codeInsight.template.TemplateManager; 21 | import com.intellij.codeInsight.template.impl.TextExpression; 22 | import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateExpressionSelector; 23 | import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateWithExpressionSelector; 24 | import com.intellij.codeInsight.template.postfix.templates.PostfixTemplatesUtils; 25 | import com.intellij.openapi.actionSystem.ActionManager; 26 | import com.intellij.openapi.actionSystem.ActionPlaces; 27 | import com.intellij.openapi.actionSystem.AnAction; 28 | import com.intellij.openapi.actionSystem.impl.ActionManagerImpl; 29 | import com.intellij.openapi.editor.Document; 30 | import com.intellij.openapi.editor.Editor; 31 | import com.intellij.openapi.project.Project; 32 | import com.intellij.openapi.ui.playback.commands.ActionCommand; 33 | import com.intellij.psi.PsiElement; 34 | import org.jetbrains.annotations.NotNull; 35 | import org.jetbrains.annotations.Nullable; 36 | import org.sssta.androidtools.util.AndroidPostfixTemplatesUtils; 37 | 38 | /** 39 | * Base class that is a modified form of {@link com.intellij.codeInsight.template.postfix.templates.StringBasedPostfixTemplate} that passes the project to {@link 40 | * AbstractRichStringBasedPostfixTemplate#createTemplate} to allow querying of project properties. 41 | * 42 | * @author Bob Browning 43 | */ 44 | public abstract class AbstractRichStringBasedPostfixTemplate extends PostfixTemplateWithExpressionSelector { 45 | 46 | protected AbstractRichStringBasedPostfixTemplate(@NotNull String name, 47 | @NotNull String example, 48 | @NotNull PostfixTemplateExpressionSelector selector) { 49 | super(name, example, selector); 50 | } 51 | 52 | @Override 53 | protected final void expandForChooseExpression(@NotNull PsiElement expr, @NotNull final Editor editor) { 54 | Project project = expr.getProject(); 55 | Document document = editor.getDocument(); 56 | PsiElement elementForRemoving = shouldRemoveParent() ? expr.getParent() : expr; 57 | document.deleteString(elementForRemoving.getTextRange().getStartOffset(), 58 | elementForRemoving.getTextRange().getEndOffset()); 59 | TemplateManager manager = TemplateManager.getInstance(project); 60 | 61 | String templateString = getTemplateString(expr); 62 | if (templateString == null) { 63 | PostfixTemplatesUtils.showErrorHint(expr.getProject(), editor); 64 | return; 65 | } 66 | 67 | Template template = createTemplate(project, manager, templateString); 68 | 69 | if (shouldAddExpressionToContext()) { 70 | addExprVariable(expr, template); 71 | } 72 | 73 | setVariables(template, expr); 74 | manager.startTemplate(editor, template, new TemplateEditingAdapter() { 75 | @Override 76 | public void templateFinished(Template template, boolean brokenOff) { 77 | // format and add ; 78 | final ActionManager actionManager = ActionManagerImpl.getInstance(); 79 | final String editorCompleteStatementText = "EditorCompleteStatement"; 80 | final AnAction action = actionManager.getAction(editorCompleteStatementText); 81 | actionManager.tryToExecute(action, ActionCommand.getInputEvent(editorCompleteStatementText), null, ActionPlaces.UNKNOWN, true); 82 | } 83 | }); 84 | } 85 | 86 | protected void addExprVariable(@NotNull PsiElement expr, Template template) { 87 | template.addVariable("expr", new TextExpression(expr.getText()), false); 88 | } 89 | 90 | /** 91 | * Add custom variables to the template. 92 | * 93 | * @param template The template 94 | * @param element The expression being replaced 95 | */ 96 | protected void setVariables(@NotNull Template template, @NotNull PsiElement element) { 97 | } 98 | 99 | @Nullable 100 | public abstract String getTemplateString(@NotNull PsiElement element); 101 | 102 | /** 103 | * Returns true if the {@code expr} variable should be added to the template by default. 104 | */ 105 | protected boolean shouldAddExpressionToContext() { 106 | return true; 107 | } 108 | 109 | /** 110 | * Returns true if the formatting manager should be applied to the generated code block. 111 | */ 112 | protected boolean shouldReformat() { 113 | return true; 114 | } 115 | 116 | /** 117 | * Returns true if the parent element should be removed, for example for topmost expression. 118 | */ 119 | protected abstract boolean shouldRemoveParent(); 120 | 121 | /** 122 | * Create a new instance of a code template for the current postfix template. 123 | * 124 | * @param project The current project 125 | * @param manager The template manager 126 | * @param templateString The template string 127 | */ 128 | protected Template createTemplate(Project project, TemplateManager manager, String templateString) { 129 | Template template = manager.createTemplate("", "", templateString); 130 | template.setToReformat(shouldReformat()); 131 | template.setValue(Template.Property.USE_STATIC_IMPORT_IF_POSSIBLE, false); 132 | return template; 133 | } 134 | 135 | 136 | /** 137 | * Gets the static method prefix for the android static method. 138 | * 139 | * @param className The android class name 140 | * @param methodName The method name 141 | * @param context The context element 142 | */ 143 | protected String getStaticMethodPrefix(@NotNull String className, 144 | @NotNull String methodName, 145 | @NotNull PsiElement context) { 146 | return AndroidPostfixTemplatesUtils.getStaticMethodPrefix(className, methodName, context); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/posfix/internal/RichChooserStringBasedPostfixTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Bob Browning 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.sssta.androidtools.posfix.internal; 17 | 18 | import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils; 19 | import com.intellij.openapi.util.Condition; 20 | import com.intellij.psi.PsiElement; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | /** 24 | * @author Bob Browning 25 | */ 26 | public abstract class RichChooserStringBasedPostfixTemplate extends AbstractRichStringBasedPostfixTemplate { 27 | 28 | protected RichChooserStringBasedPostfixTemplate(@NotNull String name, 29 | @NotNull String example, 30 | @NotNull Condition typeChecker) { 31 | super(name, example, JavaPostfixTemplatesUtils.selectorAllExpressionsWithCurrentOffset(typeChecker)); 32 | } 33 | 34 | @Override 35 | protected boolean shouldRemoveParent() { 36 | return false; 37 | } 38 | 39 | @Override 40 | protected boolean shouldReformat() { 41 | return false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/posfix/internal/RichTopmostStringBasedPostfixTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Bob Browning 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.sssta.androidtools.posfix.internal; 17 | 18 | import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils; 19 | import com.intellij.openapi.util.Condition; 20 | import com.intellij.psi.PsiElement; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | /** 24 | * @author Bob Browning 25 | */ 26 | public abstract class RichTopmostStringBasedPostfixTemplate extends AbstractRichStringBasedPostfixTemplate { 27 | 28 | protected RichTopmostStringBasedPostfixTemplate(@NotNull String name, 29 | @NotNull String example, 30 | @NotNull Condition typeChecker) { 31 | super(name, example, JavaPostfixTemplatesUtils.selectorTopmost(typeChecker)); 32 | } 33 | 34 | @Override 35 | protected boolean shouldRemoveParent() { 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/posfix/macro/TagMacro.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.posfix.macro; 2 | 3 | import com.intellij.codeInsight.template.*; 4 | import com.intellij.codeInsight.template.macro.ClassNameMacro; 5 | import com.intellij.codeInsight.template.macro.MacroUtil; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.*; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | /** 11 | * macro for android log TAG parameter. 12 | * 13 | * @author takahirom 14 | */ 15 | public class TagMacro extends Macro { 16 | 17 | 18 | public String getName() { 19 | return "tag"; 20 | } 21 | 22 | public String getPresentableName() { 23 | return "tag"; 24 | } 25 | 26 | @Nullable 27 | @Override 28 | public Result calculateResult(Expression[] expressions, ExpressionContext context) { 29 | if (isContainTagField(context)) { 30 | return new TextResult("TAG"); 31 | } else { 32 | String className = new ClassNameMacro().calculateResult(new Expression[]{}, context).toString(); 33 | if (className.length() > 23) { 34 | className = className.substring(0, 23); 35 | } 36 | return new TextResult("\"" + className + "\""); 37 | } 38 | } 39 | 40 | public boolean isAcceptableInContext(TemplateContextType context) { 41 | return context instanceof JavaCodeContextType; 42 | } 43 | 44 | 45 | public boolean isContainTagField(ExpressionContext context) { 46 | Project project = context.getProject(); 47 | int offset = context.getStartOffset(); 48 | PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(context.getEditor().getDocument()); 49 | PsiElement place = file.findElementAt(offset); 50 | PsiVariable[] variables = MacroUtil.getVariablesVisibleAt(place, ""); 51 | for (PsiVariable variable : variables) { 52 | if (variable instanceof PsiField && variable.hasModifierProperty("static")) { 53 | PsiField psiField = (PsiField) variable; 54 | if ("TAG".equals(psiField.getName())) { 55 | return true; 56 | } 57 | } 58 | 59 | } 60 | 61 | return false; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/posfix/macro/ViewListenerMacro.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.posfix.macro; 2 | 3 | import com.intellij.codeInsight.template.*; 4 | import com.intellij.psi.JavaPsiFacade; 5 | import com.intellij.psi.PsiElementFactory; 6 | import com.intellij.psi.PsiStatement; 7 | import com.intellij.psi.codeStyle.CodeStyleManager; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | /** 11 | * Created by cauchywei on 15/8/25. 12 | */ 13 | public class ViewListenerMacro extends Macro { 14 | @Override 15 | public String getName() { 16 | return "new click listener"; 17 | } 18 | 19 | @Override 20 | public String getPresentableName() { 21 | return "new click listener"; 22 | } 23 | 24 | @Nullable 25 | @Override 26 | public Result calculateResult(Expression[] expressions, ExpressionContext expressionContext) { 27 | 28 | PsiElementFactory factory = JavaPsiFacade.getInstance(expressionContext.getProject()).getElementFactory(); 29 | String listener = "new View.OnClickListener() {\n\t@Override\npublic void onClick(View v) {\n\n}\n}"; 30 | PsiStatement statementFromText = factory.createStatementFromText(listener, null); 31 | statementFromText = (PsiStatement) CodeStyleManager.getInstance(expressionContext.getProject()).reformat(statementFromText); 32 | return new JavaPsiElementResult(statementFromText); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/posfix/template/ClickListenerPostfixTemplate.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.posfix.template; 2 | 3 | import com.intellij.codeInsight.template.Template; 4 | import com.intellij.codeInsight.template.impl.ConstantNode; 5 | import com.intellij.codeInsight.template.impl.MacroCallNode; 6 | import com.intellij.codeInsight.template.macro.VariableOfTypeMacro; 7 | import com.intellij.codeInsight.template.postfix.templates.StringBasedPostfixTemplate; 8 | import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils; 9 | import com.intellij.psi.PsiClass; 10 | import com.intellij.psi.PsiClassType; 11 | import com.intellij.psi.PsiElement; 12 | import com.intellij.psi.PsiField; 13 | import com.intellij.psi.util.InheritanceUtil; 14 | import com.intellij.psi.util.PsiTreeUtil; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | import org.sssta.androidtools.util.AndroidFQClass; 18 | import org.sssta.androidtools.util.AndroidPostfixTemplatesUtils; 19 | 20 | /** 21 | * Created by cauchywei on 15/8/25. 22 | */ 23 | public class ClickListenerPostfixTemplate extends StringBasedPostfixTemplate { 24 | 25 | private boolean isNewListener = true; 26 | 27 | public ClickListenerPostfixTemplate() { 28 | super("clk", "view.setOnClickListener(listener)",JavaPostfixTemplatesUtils.selectorTopmost(AndroidPostfixTemplatesUtils.IS_VIEW)); 29 | } 30 | 31 | 32 | @Nullable 33 | @Override 34 | public String getTemplateString(@NotNull PsiElement psiElement) { 35 | 36 | PsiClass topmostParentOfType = PsiTreeUtil.getTopmostParentOfType(psiElement, PsiClass.class); 37 | 38 | if (topmostParentOfType != null) { 39 | PsiClassType[] implementsListTypes = topmostParentOfType.getImplementsListTypes(); 40 | for (PsiClassType implementsListType : implementsListTypes) { 41 | if (InheritanceUtil.isInheritor(implementsListType,AndroidFQClass.VIEW_ON_CLICK_LISTENER)){ 42 | isNewListener = false; 43 | break; 44 | } 45 | } 46 | 47 | if (isNewListener){ 48 | PsiField[] fields = topmostParentOfType.getFields(); 49 | for (PsiField field : fields) { 50 | if (InheritanceUtil.isInheritor(field.getType(),AndroidFQClass.VIEW_ON_CLICK_LISTENER)) { 51 | isNewListener = false; 52 | break; 53 | } 54 | } 55 | } 56 | } 57 | 58 | String listener; 59 | if(isNewListener){ 60 | listener = "new View.OnClickListener() {\n\t@Override\npublic void onClick(View v) {\n$END$\n}\n}"; 61 | }else { 62 | listener = "$listener$"; 63 | } 64 | 65 | return "$expr$.setOnClickListener(" + listener + ");"; 66 | 67 | } 68 | 69 | @Override 70 | public void setVariables(@NotNull Template template, @NotNull PsiElement element) { 71 | super.setVariables(template, element); 72 | //old implement 73 | // MacroCallNode node = new MacroCallNode(new VariableOfTypeMacro()); 74 | // node.addParameter(new ConstantNode(AndroidFQClass.VIEW_ON_CLICK_LISTENER)); 75 | // 76 | // MacroCallNode defNode = new MacroCallNode(new ViewListenerMacro()); 77 | // 78 | // template.addVariable("listener", node, defNode, true); 79 | if (!isNewListener){ 80 | MacroCallNode node = new MacroCallNode(new VariableOfTypeMacro()); 81 | node.addParameter(new ConstantNode(AndroidFQClass.VIEW_ON_CLICK_LISTENER)); 82 | template.addVariable("listener", node, new ConstantNode(""), true); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/posfix/template/ToastPostfixTemplate.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.posfix.template; 2 | 3 | import com.intellij.codeInsight.template.Template; 4 | import com.intellij.codeInsight.template.impl.ConstantNode; 5 | import com.intellij.codeInsight.template.impl.MacroCallNode; 6 | import com.intellij.codeInsight.template.macro.VariableOfTypeMacro; 7 | import com.intellij.psi.PsiElement; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.sssta.androidtools.posfix.internal.RichChooserStringBasedPostfixTemplate; 10 | import org.sssta.androidtools.util.AndroidFQClass; 11 | import org.sssta.androidtools.util.AndroidPostfixTemplatesUtils; 12 | 13 | /** 14 | * Created by cauchywei on 15/8/15. 15 | */ 16 | public class ToastPostfixTemplate extends RichChooserStringBasedPostfixTemplate { 17 | 18 | public ToastPostfixTemplate() { 19 | this("toast"); 20 | } 21 | 22 | public ToastPostfixTemplate(@NotNull String alias) { 23 | super(alias, "Toast.makeText(context, expr, Toast.LENGTH_SHORT).show();", AndroidPostfixTemplatesUtils.IS_NON_NULL); 24 | } 25 | 26 | @Override 27 | public String getTemplateString(@NotNull PsiElement element) { 28 | return getStaticMethodPrefix(AndroidFQClass.TOAST, "makeText", element) + "($context$, $expr$, Toast.LENGTH_SHORT).show()$END$"; 29 | } 30 | 31 | @Override 32 | protected void setVariables(@NotNull Template template, @NotNull PsiElement element) { 33 | 34 | MacroCallNode node = new MacroCallNode(new VariableOfTypeMacro()); 35 | node.addParameter(new ConstantNode(AndroidFQClass.CONTEXT)); 36 | template.addVariable("context", node, new ConstantNode(""), false); 37 | 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/util/AndroidFQClass.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.util; 2 | 3 | /** 4 | * Created by cauchywei on 15/8/16. 5 | */ 6 | public class AndroidFQClass { 7 | public static final String CONTEXT = "android.content.Context"; 8 | public static final String TOAST = "android.widget.Toast"; 9 | public static final String VIEW = "android.view.View"; 10 | public static final String VIEW_ON_CLICK_LISTENER = "android.view.View.OnClickListener"; 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/util/AndroidPostfixTemplatesUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Bob Browning 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.sssta.androidtools.util; 17 | 18 | import com.intellij.codeInsight.NullableNotNullManager; 19 | import com.intellij.openapi.util.Condition; 20 | import com.intellij.psi.*; 21 | import com.intellij.psi.util.InheritanceUtil; 22 | import com.intellij.psi.util.PsiTreeUtil; 23 | import com.siyeh.ig.psiutils.ParenthesesUtils; 24 | import org.jetbrains.annotations.Contract; 25 | import org.jetbrains.annotations.NonNls; 26 | import org.jetbrains.annotations.NotNull; 27 | import org.jetbrains.annotations.Nullable; 28 | 29 | import static com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils.*; 30 | 31 | /** 32 | * Collection of helper methods for surrounding an expression with android preconditions. 33 | * 34 | * @author Bob Browning 35 | */ 36 | public class AndroidPostfixTemplatesUtils { 37 | 38 | /** 39 | * Condition that returns true if the element is not null. 40 | */ 41 | 42 | public static final Condition IS_NON_NULL = new Condition() { 43 | @Override 44 | public boolean value(PsiElement element) { 45 | return IS_NON_VOID.value(element) && !AndroidPostfixTemplatesUtils.isAnnotatedNullable(element); 46 | } 47 | 48 | }; 49 | 50 | /** 51 | * Condition that return true if the element instanceof android.view.View 52 | */ 53 | public static final Condition IS_VIEW = new Condition() { 54 | @Override 55 | public boolean value(PsiElement element) { 56 | if (element != null && element instanceof PsiExpression) { 57 | PsiType type = ((PsiExpression) element).getType(); 58 | return InheritanceUtil.isInheritor(type,AndroidFQClass.VIEW); 59 | } else { 60 | return false; 61 | } 62 | } 63 | 64 | }; 65 | 66 | /** 67 | * Condition that returns true if the element is a {@link CharSequence}. 68 | */ 69 | public static final Condition IS_CHAR_SEQUENCE = new Condition() { 70 | @Override 71 | public boolean value(PsiElement element) { 72 | return element instanceof PsiExpression 73 | && isCharSequence(((PsiExpression) element).getType()); 74 | } 75 | }; 76 | 77 | /** 78 | * Condition that returns true if the element is a {@link java.util.Map}. 79 | */ 80 | public static final Condition IS_MAP = new Condition() { 81 | @Override 82 | public boolean value(PsiElement element) { 83 | return element instanceof PsiExpression 84 | && InheritanceUtil.isInheritor(((PsiExpression) element).getType(), CommonClassNames.JAVA_UTIL_MAP); 85 | } 86 | }; 87 | 88 | /** 89 | * Condition that returns true if the element is an iterable. 90 | */ 91 | public static final Condition IS_ITERABLE = new Condition() { 92 | @Override 93 | public boolean value(PsiElement element) { 94 | if (element instanceof PsiExpression) { 95 | PsiType type = ((PsiExpression) element).getType(); 96 | return isIterable(type); 97 | } 98 | return false; 99 | } 100 | }; 101 | 102 | /** 103 | * Condition that returns true if the element is an iterable or an iterator or an array. 104 | */ 105 | public static final Condition IS_ARRAY_OR_ITERABLE_OR_ITERATOR = new Condition() { 106 | @Override 107 | public boolean value(PsiElement element) { 108 | if (element instanceof PsiExpression) { 109 | PsiType type = ((PsiExpression) element).getType(); 110 | return isIterator(type) || isArray(type) || isIterable(type); 111 | } 112 | return false; 113 | } 114 | }; 115 | 116 | /** 117 | * Condition that returns true if the element is an iterable or an iterator or an object array or a collection. 118 | */ 119 | public static final Condition IS_NON_PRIMITIVE_ARRAY_OR_ITERABLE_OR_ITERATOR = 120 | new Condition() { 121 | @Override 122 | public boolean value(PsiElement element) { 123 | if (element instanceof PsiExpression) { 124 | PsiType type = ((PsiExpression) element).getType(); 125 | return isObjectArrayTypeExpression(type) 126 | || isIterable(type) 127 | || isIterator(type); 128 | } 129 | return false; 130 | } 131 | }; 132 | 133 | @NonNls 134 | private static final String JAVA_LANG_CHAR_SEQUENCE = "java.lang.CharSequence"; 135 | 136 | @Contract("null -> false") 137 | public static boolean isCollection(@Nullable PsiType type) { 138 | return type != null && InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_COLLECTION); 139 | } 140 | 141 | @Contract("null -> false") 142 | public static boolean isIterator(@Nullable PsiType type) { 143 | return type != null && InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_ITERATOR); 144 | } 145 | 146 | 147 | @Contract("null -> false") 148 | public static boolean isCharSequence(@Nullable PsiType type) { 149 | return type != null && InheritanceUtil.isInheritor(type, JAVA_LANG_CHAR_SEQUENCE); 150 | } 151 | 152 | @Contract("null -> false") 153 | public static boolean isObjectArrayTypeExpression(@Nullable PsiType type) { 154 | return type instanceof PsiArrayType && 155 | !(((PsiArrayType) type).getComponentType() instanceof PsiPrimitiveType); 156 | } 157 | 158 | /** 159 | * Gets the expression for acquiring the size of the specified expression. 160 | * 161 | * @param expr The expression to evaluate 162 | */ 163 | @Nullable 164 | public static String getExpressionNumberOrArrayOrIterableBound(@NotNull PsiExpression expr) { 165 | PsiType type = expr.getType(); 166 | if (isNumber(type)) { 167 | return expr.getText(); 168 | } else if (isArray(type)) { 169 | return expr.getText() + ".length"; 170 | } else if (isCollection(type)) { 171 | return expr.getText() + ".size()"; 172 | } else if (isIterable(type)) { 173 | return "com.google.common.collect.Iterables.size(" + expr.getText() + ")"; 174 | } 175 | return null; 176 | } 177 | 178 | /** 179 | * Get the prefix required for the specified imported static method within the current context. 180 | * 181 | * @param fqClassName The qualified class name 182 | * @param methodName The method name 183 | * @param context The current context 184 | */ 185 | public static String getStaticMethodPrefix(@NotNull String fqClassName, @NotNull String methodName, 186 | @NotNull PsiElement context) { 187 | return ImportUtil.hasImportStatic(fqClassName, methodName, context) ? methodName : (fqClassName + "." + methodName); 188 | } 189 | 190 | public static boolean isTopmostExpression(@NotNull PsiElement element) { 191 | return element.equals(getTopmostExpression(element)); 192 | } 193 | 194 | @Contract("null -> false") 195 | public static boolean isSemicolonNeeded(PsiElement context) { 196 | PsiStatement statement = PsiTreeUtil.getParentOfType(context, PsiStatement.class); 197 | return statement != null && statement.getLastChild() instanceof PsiErrorElement; 198 | } 199 | 200 | public static boolean isAnnotatedNullable(PsiElement element) { 201 | PsiExpression expression; 202 | if (element instanceof PsiExpression) { 203 | expression = (PsiExpression) element; 204 | } else { 205 | expression = PsiTreeUtil.getParentOfType(element, PsiExpression.class, true); 206 | if (expression == null) { 207 | return false; 208 | } 209 | } 210 | expression = ParenthesesUtils.stripParentheses(expression); 211 | if (!(expression instanceof PsiReferenceExpression)) { 212 | return false; 213 | } 214 | final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) expression; 215 | final PsiElement target = referenceExpression.resolve(); 216 | if (!(target instanceof PsiModifierListOwner)) { 217 | return false; 218 | } 219 | final PsiModifierListOwner modifierListOwner = (PsiModifierListOwner) target; 220 | return NullableNotNullManager.isNullable(modifierListOwner); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/util/CommonUtil.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.util; 2 | 3 | import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.psi.*; 7 | import com.intellij.psi.util.InheritanceUtil; 8 | import com.intellij.refactoring.util.CommonRefactoringUtil; 9 | import org.jetbrains.annotations.Contract; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.LinkedList; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by cauchywei on 15/8/16. 18 | */ 19 | public class CommonUtil { 20 | 21 | public static void showErrorHint(Project project, Editor editor) { 22 | CommonRefactoringUtil.showErrorHint(project, editor, "Can't perform postfix completion", "Can't perform postfix completion", ""); 23 | } 24 | 25 | public static void createSimpleStatement(@NotNull PsiElement context, @NotNull Editor editor, @NotNull String text) { 26 | PsiExpression expr = JavaPostfixTemplatesUtils.getTopmostExpression(context); 27 | PsiElement parent = expr != null ? expr.getParent() : null; 28 | assert parent instanceof PsiStatement; 29 | PsiElementFactory factory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory(); 30 | PsiStatement assertStatement = factory.createStatementFromText(text + " " + expr.getText() + ";", parent); 31 | PsiElement replace = parent.replace(assertStatement); 32 | editor.getCaretModel().moveToOffset(replace.getTextRange().getEndOffset()); 33 | } 34 | 35 | @Contract("null -> false") 36 | public static boolean isIterable(@Nullable PsiType type) { 37 | return type != null && InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ITERABLE); 38 | } 39 | 40 | @Contract("null -> false") 41 | public static boolean isArray(@Nullable PsiType type) { 42 | return type != null && type instanceof PsiArrayType; 43 | } 44 | 45 | @Contract("null -> false") 46 | public static boolean isBoolean(@Nullable PsiType type) { 47 | return type != null && (PsiType.BOOLEAN.equals(type) || PsiType.BOOLEAN.equals(PsiPrimitiveType.getUnboxedType(type))); 48 | } 49 | 50 | @Contract("null -> false") 51 | public static boolean isNumber(@Nullable PsiType type) { 52 | if (type == null) { 53 | return false; 54 | } 55 | if (PsiType.INT.equals(type) || PsiType.BYTE.equals(type) || PsiType.LONG.equals(type)) { 56 | return true; 57 | } 58 | 59 | PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type); 60 | return PsiType.INT.equals(unboxedType) || PsiType.BYTE.equals(unboxedType) || PsiType.LONG.equals(unboxedType); 61 | } 62 | 63 | @Contract("null -> false") 64 | public static boolean isString(@Nullable PsiType type) { 65 | return type != null && type.equalsToText(CommonClassNames.JAVA_LANG_STRING); 66 | } 67 | 68 | // public static List splitViewId(String name){ 69 | // if (name.contains("_")){ 70 | // return splitUnderscoreName(name); 71 | // }else { 72 | // return splitCamelName(name); 73 | // } 74 | // } 75 | 76 | public static List splitCamelName(String name){ 77 | List words = new LinkedList<>(); 78 | 79 | int start = 0; 80 | for (int i = 0; i < name.length(); i++) { 81 | char ch = name.charAt(i); 82 | if (Character.isUpperCase(ch)){ 83 | if (start != i){ 84 | words.add(name.substring(start,i)); 85 | } 86 | start = i; 87 | } 88 | } 89 | 90 | if (start < name.length()){ 91 | words.add(name.substring(start,name.length())); 92 | } 93 | 94 | return words; 95 | } 96 | 97 | public static List splitUnderscoreName(final String name){ 98 | return new LinkedList(){{ 99 | String[] names = name.split("_"); 100 | for (String name:names){ 101 | add(name); 102 | } 103 | }}; 104 | } 105 | 106 | /** 107 | * name match means t is s' common substring 108 | * @param s 109 | * @param t 110 | * @return 111 | */ 112 | public static boolean nameMatch(@NotNull String s, @NotNull String t){ 113 | 114 | if (s.length() == 0 || t.length() == 0) 115 | return false; 116 | 117 | t = t.toLowerCase(); 118 | s = s.toLowerCase(); 119 | 120 | int i, j = 0; 121 | int minLen; 122 | 123 | if (s.length() < t.length()){ 124 | String tmp = s; 125 | s = t; 126 | t = tmp; 127 | 128 | } 129 | 130 | for (i = 0; i < s.length(); i++) { 131 | if (s.charAt(i) == t.charAt(j)){ 132 | j++; 133 | if (j == t.length()) 134 | return true; 135 | } 136 | } 137 | 138 | return false; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/util/ImportUtil.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.util; 2 | 3 | import com.intellij.psi.*; 4 | import com.intellij.psi.search.GlobalSearchScope; 5 | import com.siyeh.ig.psiutils.ClassUtils; 6 | 7 | public class ImportUtil { 8 | 9 | /** 10 | * Check whether the current context has a static member import, either on-demand or explicit. 11 | * 12 | * @param fqClassName The class to import from 13 | * @param memberName The class member to import 14 | * @param context The context to be imported into 15 | */ 16 | public static boolean hasImportStatic(String fqClassName, String memberName, PsiElement context) { 17 | final PsiFile file = context.getContainingFile(); 18 | if (!(file instanceof PsiJavaFile)) { 19 | return false; 20 | } 21 | final PsiJavaFile javaFile = (PsiJavaFile) file; 22 | final PsiImportList importList = javaFile.getImportList(); 23 | if (importList == null) { 24 | return false; 25 | } 26 | final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements(); 27 | for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { 28 | if (importStaticStatement.isOnDemand()) { 29 | PsiClass psiClass = ClassUtils.findClass(fqClassName, context); 30 | if (psiClass != null && psiClass.equals(importStaticStatement.resolveTargetClass())) { 31 | return true; 32 | } 33 | continue; 34 | } 35 | final String name = importStaticStatement.getReferenceName(); 36 | if (!memberName.equals(name)) { 37 | continue; 38 | } 39 | final PsiJavaCodeReferenceElement importReference = importStaticStatement.getImportReference(); 40 | if (importReference == null) { 41 | continue; 42 | } 43 | final PsiElement qualifier = importReference.getQualifier(); 44 | if (qualifier == null) { 45 | continue; 46 | } 47 | final String qualifierText = qualifier.getText(); 48 | if (fqClassName.equals(qualifierText)) { 49 | return true; 50 | } 51 | } 52 | return false; 53 | } 54 | 55 | public static void importIfNot(final String fqClassName, final PsiElement context){ 56 | 57 | final PsiFile file = context.getContainingFile(); 58 | if (!(file instanceof PsiJavaFile)) { 59 | return; 60 | } 61 | final PsiJavaFile javaFile = (PsiJavaFile) file; 62 | final PsiImportList importList = javaFile.getImportList(); 63 | if (importList == null) { 64 | return; 65 | } 66 | 67 | boolean imported = false; 68 | PsiImportStatement[] importStatements = importList.getImportStatements(); 69 | for (PsiImportStatement importStatement : importStatements) { 70 | if (fqClassName.equals(importStatement.getQualifiedName())){ 71 | imported = true; 72 | break; 73 | } 74 | } 75 | 76 | if (!imported){ 77 | // UIUtil.invokeLaterIfNeeded(new Runnable() { 78 | // @Override 79 | // public void run() { 80 | // importList.add(JavaPsiFacade.getInstance(context.getProject()).getElementFactory().createTypeElement(PsiType.getTypeByName(fqClassName, context.getProject(), GlobalSearchScope.allScope(context.getProject())))); 81 | // } 82 | // }); 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/util/LayoutUtil.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.util; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.*; 6 | import com.intellij.psi.search.FilenameIndex; 7 | import com.intellij.psi.search.GlobalSearchScope; 8 | import com.intellij.psi.util.PsiTreeUtil; 9 | import com.intellij.psi.util.PsiUtilBase; 10 | import com.intellij.psi.xml.XmlAttribute; 11 | import com.intellij.psi.xml.XmlTag; 12 | import org.sssta.androidtools.model.ViewModel; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Created by cauchywei on 15/8/17. 19 | */ 20 | public class LayoutUtil { 21 | 22 | public static PsiFile findLayoutXmlFile(Project project, Editor editor){ 23 | 24 | PsiElement layoutResourceElement = findLayoutResourceElement(project, editor); 25 | 26 | if (layoutResourceElement == null || !isValidLayoutResource(layoutResourceElement)) { 27 | return null; 28 | } 29 | 30 | return findXmlByLayoutResourceElement(layoutResourceElement); 31 | } 32 | 33 | public static PsiElement findLayoutResourceElement(Project project, Editor editor){ 34 | return findLayoutResourceElementByMethodCallExpress(findLayoutSetMethod(project, editor)); 35 | } 36 | 37 | public static PsiElement findLayoutResourceElementByMethodCallExpress(PsiMethodCallExpression methodCallExpression) { 38 | 39 | if (methodCallExpression == null) { 40 | return null; 41 | } 42 | 43 | PsiExpression[] expressions = methodCallExpression.getArgumentList().getExpressions(); 44 | if (expressions.length == 0){ 45 | return null; 46 | } 47 | return expressions[0]; 48 | } 49 | 50 | public static PsiMethodCallExpression findLayoutSetMethod(Project project, Editor editor){ 51 | 52 | if (project == null || editor == null) { 53 | return null; 54 | } 55 | 56 | PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project); 57 | 58 | if (psiFile == null) { 59 | return null; 60 | } 61 | 62 | PsiElement elementAtCaret = PsiUtilBase.getElementAtCaret(editor); 63 | return PsiTreeUtil.getParentOfType(elementAtCaret, PsiMethodCallExpression.class); 64 | } 65 | 66 | public static boolean isValidLayoutResource(PsiElement psiElement) { 67 | 68 | if (psiElement == null) { 69 | return false; 70 | } 71 | 72 | String name; 73 | if (psiElement instanceof PsiReferenceExpression){ 74 | name = ((PsiReferenceExpression)psiElement).getReferenceName(); 75 | }else { 76 | name = psiElement.getText(); 77 | } 78 | 79 | 80 | return !"R.layout".startsWith(name); 81 | 82 | } 83 | 84 | public static PsiFile findXmlByLayoutResourceElement(PsiElement layoutResource){ 85 | 86 | Project project = layoutResource.getProject(); 87 | String xmlName = layoutResource.getLastChild().getText() + ".xml"; 88 | PsiFile[] xmlFiles = FilenameIndex.getFilesByName(project, xmlName, GlobalSearchScope.allScope(project)); 89 | 90 | if (xmlFiles == null || xmlFiles.length == 0){ 91 | return null; 92 | } 93 | 94 | return xmlFiles[0]; 95 | 96 | } 97 | 98 | 99 | 100 | public static List getContainingIdViewsInXml(PsiFile xmlFile){ 101 | 102 | return getContainingAttributeViewsInXml(xmlFile,"android:id"); 103 | } 104 | 105 | public static List getContainingAttributeViewsInXml(PsiFile xmlFile, final String attributeName) { 106 | 107 | final String name = xmlFile.getName(); 108 | final List views = new ArrayList<>(); 109 | xmlFile.accept(new XmlRecursiveElementVisitor() { 110 | @Override 111 | public void visitXmlTag(XmlTag tag) { 112 | super.visitXmlTag(tag); 113 | XmlAttribute idAttribute = tag.getAttribute(attributeName); 114 | 115 | if (idAttribute != null) { 116 | String id = idAttribute.getValue(); 117 | if (id != null && id.matches("^@\\+?id/.*")){ 118 | views.add(new ViewModel(name,id,tag.getName())); 119 | } 120 | } 121 | } 122 | }); 123 | 124 | return views; 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/org/sssta/androidtools/util/Levenshtein.java: -------------------------------------------------------------------------------- 1 | package org.sssta.androidtools.util; 2 | 3 | /** 4 | * Created by cauchywei on 15/8/17. 5 | */ 6 | public class Levenshtein { 7 | 8 | 9 | public static void levenshtein(String str1,String str2) { 10 | //计算两个字符串的长度。 11 | int len1 = str1.length(); 12 | int len2 = str2.length(); 13 | //建立上面说的数组,比字符长度大一个空间 14 | int[][] dif = new int[len1 + 1][len2 + 1]; 15 | //赋初值,步骤B。 16 | for (int a = 0; a <= len1; a++) { 17 | dif[a][0] = a; 18 | } 19 | for (int a = 0; a <= len2; a++) { 20 | dif[0][a] = a; 21 | } 22 | //计算两个字符是否一样,计算左上的值 23 | int temp; 24 | for (int i = 1; i <= len1; i++) { 25 | for (int j = 1; j <= len2; j++) { 26 | if (str1.charAt(i - 1) == str2.charAt(j - 1)) { 27 | temp = 0; 28 | } else { 29 | temp = 1; 30 | } 31 | //取三个值中最小的 32 | dif[i][j] = min(dif[i - 1][j - 1] + temp, dif[i][j - 1] + 1, 33 | dif[i - 1][j] + 1); 34 | } 35 | } 36 | System.out.println("字符串\""+str1+"\"与\""+str2+"\"的比较"); 37 | //取数组右下角的值,同样不同位置代表不同字符串的比较 38 | System.out.println("差异步骤:"+dif[len1][len2]); 39 | //计算相似度 40 | float similarity =1 - (float) dif[len1][len2] / Math.max(str1.length(), str2.length()); 41 | System.out.println("相似度:"+similarity); 42 | } 43 | 44 | private static int min(int... is) { 45 | int min = Integer.MAX_VALUE; 46 | for (int i : is) { 47 | if (min > i) { 48 | min = i; 49 | } 50 | } 51 | return min; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/postfixTemplates/ClickListenerPostfixTemplate/description.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | generate click listener according to context 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/postfixTemplates/ToastPostfixTemplate/description.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Show a Toast. 4 | 5 | Text after this comment will not be shown in tooltips. 6 | 7 | --------------------------------------------------------------------------------