In the previous two posts, we looked at creating custom live templates and file templates. I received a question on twitter once I posted the file template tutorial:

@riggaroo Any idea how to create File Templates that create multiple files? Like "Activity" and "fragment" do? — Justin Angel (@JustinAngel) May 2, 2016

The following slides were shared with me from Tomás Ruiz-López. I thought I would share my findings thanks to the slides. Android Studio Templates use Freemarker for template definitions. In this post, we will look at creating a set of file templates that can achieve similar results to the default ones in Android Studio.

How to create a group of file templates

In this example, we are going to look at how to create a custom group of file templates for creating MVP functionality. We need to create a Presenter, an Activity and a Contract that defines the interfaces between the two.

Navigate to the location of the templates folder : {ANDROID_STUDIO_LOCATION}/plugins/android/lib/templates/other/ On MacOS, this folder was the following: /Applications/Android Studio.app/Contents/plugins/android/lib/templates/other/ Create a folder in the location of your android studio installation with the name of your template – for example MVP Template Activity . Define the following files & folder in the folder that you have just created: template.xml – This will contain information about the template (Name, minSdkVersion, etc)

– This will contain information about the template (Name, minSdkVersion, etc) recipe.xml.ftl – This will contain instructions explaining how to create the template, including what variables to ask the user for and what should be done with those variables.

– This will contain instructions explaining how to create the template, including what variables to ask the user for and what should be done with those variables. globals.xml.ftl – This defines global variables

– This defines global variables root/ folder – this will contain the template code. In the template.xml place the following xml, substituted with your information about your app. <template format="4" revision="1" name="MVP Template Activity" description="Creates a new MVP classes - Presenter, View and Contract between the two"> <category value="Other"/> <parameter id="className" name="Functionality Name" type="string" constraints="class|unique|nonempty" default="MvpFunction" help="The name of the functionality that requires MVP views"/> <globals file="globals.xml.ftl" /> <execute file="recipe.xml.ftl" /> </template> This file contains all the parameters that should be asked for from the user. In this case, I am asking the user for a name for the MVP functionality that we will be creating. In the recipe.xml.ftl you define exactly how the recipe should run, what parameters should be requested from the user (the types of parameters too) and the order in which to instantiate them. <?xml version="1.0"?> <recipe> <instantiate from="src/app_package/Contract.java.ftl" to="${escapeXmlAttribute(srcOut)}/${className}Contract.java" /> <instantiate from="src/app_package/MvpViewActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${className}Activity.java" /> <instantiate from="src/app_package/Presenter.java.ftl" to="${escapeXmlAttribute(srcOut)}/${className}Presenter.java" /> <open file="${srcOut}/${className}Presenter.java"/> </recipe> We are now specifying the different files that are created, and where to substitute the name of the output file. We also specify which file should be opened at the end. In the globals.xml.ftl place the following: <?xml version="1.0"?> <globals> <global id="resOut" value="${resDir}" /> <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" /> </globals> Then in the root folder, create the following folder structure: root/src/app_package/ In this app_package folder, you can then create the template files that need to be created when using this template. For my example, I have the following files in the app_package folder: Contract.java.ftl MvpViewActivity.java.ftl Presenter.java.ftl



package ${packageName}; import com.example.android.presentation.base.MvpPresenter; import com.example.presentation.base.MvpView; public interface ${className}Contract{ interface View extends MvpView { } interface Presenter extends MvpPresenter<View> { } }

package ${packageName}; import com.example.android.presentation.base.*; public class ${className}Activity extends BaseActivity implements ${className}Contract.View { ${classname}Contract.Presenter presenter; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); presenter = new ${className}Presenter(); presenter.attachView(this); } @Override protected void onDestroy() { super.onDestroy(); presenter.detachView(); } }

package ${packageName}; import com.example.android.presentation.base.*; public class ${className}Presenter extends BasePresenter<${className}Contract.View> implements ${className}Contract.Presenter{ }

To use it, you might need to restart Android Studio. Once restarted, right click on a package that you want to create the template in and select it from the “New” menu, as below:

You will then be prompted to fill in the variables that you asked for in the template.xml

It will then generate all the classes required for new MVP functionality. I found that I was often creating the same kind of classes and having to look up how to link the presenter and activity to one another. By implementing it as a template, the whole team can use it, which will ensure standardisation and an easy way of creating new screens or classes.

What other use cases can you think of for groups of file templates?

If you are looking for a sample usage of File Templates, you can see the github repo here which contains the folder for the template that I use.

Known Issue:

When upgrading Android Studio, and you have custom groups of file templates in the installation area specified above, Studio will say you have conflicts in the area that need to be deleted. Unfortunately you will have to remove them, and then place them in the same folder after the upgrade. Check out this feature request here.

Links: