Adding Tasker/Locale Support
A feature that was requested a couple times of my app Media Button Overlay was an action plugin for Tasker, an automation tool for Android. While working though this, I found that there weren't too many resources online, other than sample code. Now that I've figured it out, I've decided to write up how I did it for others in the future.
1. Build locale-api and include it as a dependency
Tasker uses Locale's API for interactions, allowing you to kill two birds with one stone and create a plugin for both. To do so, you either need to build the API or drop in a binary, depending on what you're doing.
- In Eclipse, you can include this project and add it as a library to your app.
- In Android Studio you can build from the version I created. I created this project so that I wouldn't have to stray outside of Android Studio. There is also a precompiled AAR version available, if you don't want to build yourself. To include it, you can follow this guide, which is pretty straightforward.
2. Add related files to your project
These files are taken directly from Tasker's example plugin. The main ones we're concerned with involve keeping extras that we don't need out of Bundles, and working with extra data sent to Tasker.
The files you'll need are in the "src" directory:
- Constants.java
- PluginApplication.java
- bundles/BundleScrubber.java
- bundles/PluginBundleManager.java
Note that I prefer to keep these in their own package (which I name net.dinglisch.tasker), but you're free to do this how you please.
3. Add the Receiver
Apps can communicate with each other through the use of Intents. Tasker and Locale have their own specific Intents that they send to apps, which can respond to them. Once the Intent is received, it will execute whatever you assign to it.
// Make sure you import these!
import net.dinglisch.tasker.bundle.BundleScrubber;
import net.dinglisch.tasker.bundle.PluginBundleManager;
public class TaskerReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent){
// Make sure we're getting the right Intent
if (!com.twofortyfouram.locale.Intent.ACTION_FIRE_SETTING.equals(intent.getAction())) return;
// Clear it of any extras
BundleScrubber.scrub(intent);
Bundle bundle = intent.getBundleExtra(com.twofortyfouram.locale.Intent.EXTRA_BUNDLE);
BundleScrubber.scrub(intent);
// If the Bundle is valid, execute
if(PluginBundleManager.isBundleValid(bundle)) {
/*
...
*/
}
}
}
This is based on the sample code, which just checks that the Intent is valid before executing your plugin.
4. Add the Edit Activity
A requirement for Tasker plugins is an Activity that allows the user to edit settings. This activity would normally extend a class they created with their sample, however it had some code that depended on severely out of date Android version. This has the required bits of it, but does not subclass that functionality.
// Make sure to import these!
import net.dinglisch.tasker.Constants;
import net.dinglisch.tasker.bundle.BundleScrubber;
import net.dinglisch.tasker.bundle.PluginBundleManager;
public class TaskerActivity extends MainActivity {
boolean mIsCancelled = false;
// Your standard onCreate, do as you would a normal activity!
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
...
*/
}
@Override
public void finish() {
if(this.mIsCancelled) return;
// This string is what shows under "Configuration" in the Tasker interface. If you don't
// have this set, you might not be able to get the plugin working properly
String message = "Configured!";
Intent resultI = new Intent();
Bundle resultB = PluginBundleManager.generateBundle(getApplicationContext(), message);
resultI.putExtra(com.twofortyfouram.locale.Intent.EXTRA_BUNDLE, resultB);
resultI.putExtra(com.twofortyfouram.locale.Intent.EXTRA_STRING_BLURB, message);
setResult(RESULT_OK, resultI);
super.finish();
}
// Adds the Tasker menus at the top
@Override
public boolean onCreateOptionsMenu(final Menu menu){
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(com.twofortyfouram.locale.api.R.menu.twofortyfouram_locale_help_save_dontsave, menu);
getActionBar().setDisplayHomeAsUpEnabled(true);
try {
getActionBar().setIcon(getPackageManager().getApplicationIcon(getCallingPackage()));
}
catch (final PackageManager.NameNotFoundException e) {
if (Constants.IS_LOGGABLE)
Log.w(Constants.LOG_TAG, "An error occurred loading the host's icon", e); //$NON-NLS-1$
}
return true;
}
// Handles menu item selection
@Override
public boolean onMenuItemSelected(final int featureId, final MenuItem item) {
final int id = item.getItemId();
if (android.R.id.home == id) {
this.finish();
return true;
}
else if (R.id.twofortyfouram_locale_menu_dontsave == id) {
mIsCancelled = true;
this.finish();
return true;
}
else if (R.id.twofortyfouram_locale_menu_save == id) {
this.finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
5. Update Manifest
Finally, in your AndroidManifest.xml, you need to add the proper Intent filters to the receiver and the activity, so that you actually respond when Tasker says to do something.
<activity
android:name=".tasker.TaskerActivity"
android:exported="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
</intent-filter>
</activity>
<receiver
android:name=".tasker.TaskerReceiver"
android:exported="true"
android:process=":background">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
</intent-filter>
</receiver>
6. Test
Plug it in, create a profile and test your new plugin. You should be able to get an example working easily with Toasts to ensure it is working; after that, just go to town on your code and create whatever plugin you can imagine!