Tasker
Home
Download
TaskerNet
Usage Examples
Pre-made Projects
FAQs
Guides
Reviews
Wiki
Plugin List
Forum
Forum/Support
Legacy Forum
Userguide (5.0+)
Index: en es fr zh
1 Page: en
More
Join
AutoApps
Developers
History
Privacy Policy
Release Notes
Next Version
Feature Requests
Report Issues
Patreon

Developers: Plugins Library

You can use an Android library to make it very easy to implement a new plugin for Tasker.

To start, simply add this to your gradle script:

   
       implementation 'com.joaomgcd:taskerpluginlibrary:0.4.1'
   

...and add this to your permission list in your manifest file

   
       <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
   

You can download a sample Tasker Plugin implementation, as well as check out the source code for this library here.

In the project you have the app module which is a collection of use cases examples for the library.

Check out all the examples along with their comments so you can see the various use cases that are possible with this.

Quick Start

Here are basic examples of an Action, Event and State that you can simply copy-paste into your code to quickly get started and then go from there.

These basic examples have will not allow users to input any configuration into your plugin and will not output any variables for them either, but allow you to get the gist of how stuff works.

On some apps these are really all you need, so you may even never go beyond these examples if your app doesn't require it. 😁

Actions

To create a Tasker action (to change a setting or an action in your app), add something like the following to the manifest:

   
        <activity
            android:name=".ActivityConfigBasicAction"
            android:exported="true"
            android:icon="@mipmap/ic_launcher"
            android:label="My Tasker Action">
            <intent-filter>
                <action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
            </intent-filter>
        </activity>
   

Then create a class that extends the TaskerPluginRunnerAction class

   
   	import android.app.Activity
	import android.content.Context
	import android.os.Bundle
	import android.os.Handler
	import android.os.Looper
	import android.widget.Toast
	import com.joaomgcd.taskerpluginlibrary.action.TaskerPluginRunnerActionNoOutputOrInput
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfig
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigHelperNoOutputOrInput
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigNoInput
	import com.joaomgcd.taskerpluginlibrary.input.TaskerInput
	import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResult
	import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultSucess

	class BasicActionHelper(config: TaskerPluginConfig<Unit>) : TaskerPluginConfigHelperNoOutputOrInput<BasicActionRunner>(config) {
	    override val runnerClass: Class<BasicActionRunner> get() = BasicActionRunner::class.java
	    override fun addToStringBlurb(input: TaskerInput<Unit>, blurbBuilder: StringBuilder) {
	        blurbBuilder.append("Will show a toast saying 'Basic'")
	    }
	}

	class ActivityConfigBasicAction : Activity(), TaskerPluginConfigNoInput {
	    override val context get() = applicationContext
	    private val taskerHelper by lazy { BasicActionHelper(this) }
	    override fun onCreate(savedInstanceState: Bundle?) {
	        super.onCreate(savedInstanceState)
	        taskerHelper.finishForTasker()
	    }
	}

	class BasicActionRunner : TaskerPluginRunnerActionNoOutputOrInput() {
	    override fun run(context: Context, input: TaskerInput<Unit>): TaskerPluginResult<Unit> {
	        Handler(Looper.getMainLooper()).post { Toast.makeText(context, "Basic", Toast.LENGTH_LONG).show() }
	        return TaskerPluginResultSucess()
	    }
	}
   

Event Conditions

To create a Tasker event condition (a condition that is instantly triggered and has no state, ie. user just pushed a button; new update for podcast is out;) add something like the following to the manifest:

   
        <activity android:name=".ActivityConfigBasicEvent"
            android:exported="true"
            android:icon="@mipmap/ic_launcher"
            android:label="My Event Condition">
            <intent-filter>
                <action android:name="net.dinglisch.android.tasker.ACTION_EDIT_EVENT" />
            </intent-filter>
        </activity>
   

Then create a class that extends the TaskerPluginRunnerConditionEvent class

   
   	import android.app.Activity
	import android.content.Context
	import android.os.Bundle
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfig
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigHelperEventNoOutputOrInputOrUpdate
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigNoInput
	import com.joaomgcd.taskerpluginlibrary.extensions.requestQuery
	import com.joaomgcd.taskerpluginlibrary.input.TaskerInput

	class BasicEventHelper(config: TaskerPluginConfig<Unit>) : TaskerPluginConfigHelperEventNoOutputOrInputOrUpdate(config) {
	    override fun addToStringBlurb(input: TaskerInput<Unit>, blurbBuilder: StringBuilder) {
	        blurbBuilder.append("Will trigger this app's Tasker event")
	    }
	}

	class ActivityConfigBasicEvent : Activity(), TaskerPluginConfigNoInput {
	    override val context get() = applicationContext
	    override fun onCreate(savedInstanceState: Bundle?) {
	        super.onCreate(savedInstanceState)
	        BasicEventHelper(this).finishForTasker()
	    }
	}

	fun Context.triggerBasicTaskerEvent() = ActivityConfigBasicEvent::class.java.requestQuery(this)
   

In this example you would trigger the event in Tasker by simply calling the included triggerBasicTaskerEvent() function on the context object from anywhere in your code.

State Conditions

To create a Tasker state condition (a condition that can match or not match a situation at any given time, ie. a light being on or off; music is playing or not;) add something like the following to the manifest:

   
        <activity
            android:name=".ActivityConfigBasicState"
            android:exported="true"
            android:icon="@mipmap/ic_launcher"
            android:label="My State Condition">
            <intent-filter>
                <action android:name="com.twofortyfouram.locale.intent.action.EDIT_CONDITION" />
            </intent-filter>
        </activity>
   

Then create a class that extends the TaskerPluginRunnerConditionState class

   
   	import android.app.Activity
	import android.content.Context
	import android.os.Bundle
	import com.joaomgcd.taskerpluginlibrary.condition.TaskerPluginRunnerConditionNoOutputOrInputOrUpdateState
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfig
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigHelperStateNoOutputOrInputOrUpdate
	import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigNoInput
	import com.joaomgcd.taskerpluginlibrary.extensions.requestQuery
	import com.joaomgcd.taskerpluginlibrary.input.TaskerInput
	import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultCondition
	import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultConditionSatisfied
	import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultConditionUnsatisfied

	class BasicStateHelper(config: TaskerPluginConfig<Unit>) : TaskerPluginConfigHelperStateNoOutputOrInputOrUpdate<BasicStateRunner>(config) {
	    override val runnerClass get() = BasicStateRunner::class.java
	    override fun addToStringBlurb(input: TaskerInput<Unit>, blurbBuilder: StringBuilder) {
	        blurbBuilder.append("Will toggle between being activated and deactivated")
	    }
	}

	class ActivityConfigBasicState : Activity(), TaskerPluginConfigNoInput {
	    override val context: Context get() = applicationContext
	    override fun onCreate(savedInstanceState: Bundle?) {
	        super.onCreate(savedInstanceState)
	        BasicStateHelper(this).finishForTasker()
	    }
	}

	class BasicStateRunner : TaskerPluginRunnerConditionNoOutputOrInputOrUpdateState() {
	    override fun getSatisfiedCondition(context: Context, input: TaskerInput<Unit>, update: Unit?): TaskerPluginResultCondition<Unit> {
	        return if (isActive) TaskerPluginResultConditionSatisfied(context) else TaskerPluginResultConditionUnsatisfied()
	    }
	}

	private var isActive = false
	fun Context.toggleBasicState() {
	    isActive = !isActive
	    ActivityConfigBasicState::class.java.requestQuery(this)
	}
   

In this example you would toggle the state in Tasker being active/inactive by simply calling the included toggleBasicState() function on the context object from anywhere in your code.

For each of these actions/conditions you need to:

  • Make your Config Activity extend the TaskerPluginConfig class
  • create a class that extends the TaskerPluginConfigHelper class

Reference Use Cases:

Actions

Use Case File in Plugin Sample
Basic example of an action with easy input and output \getip\
Return static data (bound to a class) to the current task \getartists\GetArtists.kt
Dynamic Input (not bound to the usually used input classes) \dynamicinput\DynamicInput.kt or \gettime\ActivityConfigGetTime.kt
Return dyamic data (not bound to a class) to the current task \getip\ActivityConfigGetIP.kt
Do background work and return result in the same action \backgroundwork\BackgroundWork.kt

Events

Use Case File in Plugin Sample
Basic event triggering with some data \gottime\GotTime.kt
Trigger event with some data but only if condition is met \playstatechanged\PlayStateChanged.kt

States

Use Case File in Plugin Sample
Basic state based on a global state variable \togglingcondition\TogglingCondition.kt

Main Concepts: Runner, Config Helper and Config

I'll intruduce the Runner, Config Helper and Config concepts using the "Hello World" example from the Plugin Sample Project.

Runner

   
      class HelloWorldRunner : TaskerPluginRunnerActionNoOutputOrInput() {
          override fun run(context: Context, input: TaskerInput<Unit>): TaskerPluginResult<Unit> {
              "Hello Tasker World".toToast(context)
              return TaskerPluginResultSucess()
          }
      }
   

A Runner is the class that will perform the actual action you want. When the runner is done it returns a TaskerPluginResult instance depending on what the result is: TaskerPluginResultSucess for success, TaskerPluginResultError when there's an error

Here the runner will simply create a toast with the text "Hello Tasker World" and return success

Runners have to extend one of these classes

  • TaskerPluginRunnerAction
  • TaskerPluginRunnerConditionState
  • TaskerPluginRunnerConditionEvent

In this case the Runner is extending TaskerPluginRunnerActionNoOutputOrInput because it's a Tasker action and it requires no input or output.

Config Helper

   
      class HelloWorldHelper(config: TaskerPluginConfig<Unit>) : TaskerPluginConfigHelperNoOutputOrInput<HelloWorldRunner>(config) {
          override val runnerClass = HelloWorldRunner::class.java
          override fun addToStringBlurb(input: TaskerInput<Unit>, blurbBuilder: StringBuilder) {
              blurbBuilder.append("This will show a toast saying 'Hello Tasker World'")
          }
      }
   

This will help you configure the plugin, which you will usually do in an Activity. It allows you to manipulate input and output variables, the resulting string blurb, amongst other things.

You can call the relevantVariables property on a Config Helper to get a list of local variables relevant to the task that's being edited.

In this case it's only setting the String Blurb to "This will show a toast saying 'Hello Tasker World'" and indicating the runner class

ConfigHelpers have to extend the TaskerPluginConfigHelper class. In this case it extends the TaskerPluginConfigHelperNoOutputOrInput class because it doesn't need inputs or outputs.

Config

   
      class HelloWorldActivity : ActivityConfigTaskerNoOutputOrInput<HelloWorldRunner, HelloWorldHelper>() {
          override fun getNewHelper(config: TaskerPluginConfig<Unit>) = HelloWorldHelper(config)

      }
   

Usually an activity. It's a class that implements the various methods in TaskerPluginConfig and allows you to send inputs from the Activity to Tasker and vice-versa.

In this case the Config does nothing but intantiating the appropriate Config Helper.

Configs have to implement the TaskerPluginConfig interface. Because it's an interface they have to implement, Configs can have any other subclass they want. In this case this action is configured in an activity so it extends the ActivityConfigTaskerNoOutputOrInput activity which is a base activity for all of the plugin sample Configs.

Input and Output

Both actions and conditions can use Inputs and Outputs.

Inputs

This is the input for the GetIP example.

   
      @TaskerInputRoot
      class GetIPInput @JvmOverloads constructor(
              @field:TaskerInputField("separator", R.string.separator) var separator: String? = null,
              @field:TaskerInputObject("optionsip") var options: GetIPInputOptions = GetIPInputOptions()
      )

      @TaskerInputObject("options", R.string.options)
      class GetIPInputOptions @JvmOverloads constructor(
              @field:TaskerInputField("split", R.string.split) var split: Boolean = false
      )
   

Inputs are what is used by Configs to configure the Tasker action/condition. Think of them as input fields that a user can configure in your app

They will be sent to your config via the assignFromInput method, and you have to send them back to Tasker via the inputForTasker val:

   
    override fun assignFromInput(input: TaskerInput<GetIPInput>) = input.regular.run {
        editTextSeparator.setText(separator)
        checkboxSplit.isChecked = options.split
    }

    override val inputForTasker get() = TaskerInput(GetIPInput(editTextSeparator.text?.toString(), GetIPInputOptions(checkboxSplit.isChecked)))
   

In this example an EditText is assigned the separator input and a checkbox state is based on the split input.

You can also add dynamic input in the inputForTasker val if needed.

They will also be sent to your Runner when running the action/condition so you can have different behaviour based on your input:

   
    override fun run(context: Context, input: TaskerInput<GetIPInput>): TaskerPluginResult<GetIPOutput> {
      .....
    }
   

Here are a few rules for Inputs

  • An Input class has to have either the @TaskerInputRoot or @TaskerInputObject annotations
  • Each Input field has to be either an int, long, float, double, boolean, String, String[], ArrayList<String> or another Input Object
  • Each Input field has to have the @TaskerInputField annotation. These annotations are applied to the class fields themselves, and not on the getters or setters
  • Inputs can be nested. You can optionally apply the @TaskerInputField annotation on the nested object to give it a specific key.
  • Inputs are based on keys. If you change the keys between app versions those inputs will be lost for users
  • In Kotlin Input fields need to be var so that they can be written to by the library.

Outputs

This is the output for the GetArtists example.

   
      @TaskerOutputObject()
      class MusicArtist(
              @get:TaskerOutputVariable(R.string.artist, R.string.artist_label, R.string.artist_html_label) val name: String,
              val album: MusicAlbum,
              val songs: Array<MusicSong>

      )

      @TaskerOutputObject()
      class MusicSong(
              @get:TaskerOutputVariable(R.string.song, R.string.song_label, R.string.song_html_label) val name: String,
              @get:TaskerOutputVariable(R.string.duration, R.string.duration_label, R.string.duration_html_label) val duration: Int
      )
      @TaskerOutputObject()
      class MusicAlbum(
              @get:TaskerOutputVariable(R.string.album, R.string.album_label, R.string.album_html_label) val name: String
      )
   

Outputs are what are used by Runners to create Tasker variables from the action/condition.

In the Configs you need to specify the output class:

   
    override val outputClass = Array<MusicArtist>::class.java
   

In this example you can see that it's outputting an array of Music Artists

In the Runners you have to return them from the run method:

   
    override fun run(context: Context, input: TaskerInput<Unit>): TaskerPluginResult<Array<MusicArtist>> {
        val result = arrayOf(
                MusicArtist("Pearl Jam", MusicAlbum("Backspacer"), arrayOf(MusicSong("Even Flow", 200), MusicSong("Alive", 300))),
                MusicArtist("Rui Veloso", MusicAlbum("Mingos E Os Samurais"), arrayOf(MusicSong("Sayago Blues", 360), MusicSong("Dia De Passeio", 240)))
        )
        return TaskerPluginResultSucess(result)
    }
   

In this example it's outputting a list of artists where each has a name, an album name and a list of songs.

Here are a few rules for Outputs

  • An Output class has to have the @TaskerOutputObject annotation
  • Each Output value is converted to a string using its toString method
  • Each Output method has to have the @TaskerOutputVariable annotation. These annotations are applied to methods so you can have methods that output the values you want and not have backing fields for them
  • Outputs can be nested as long as each nested object has the @TaskerOutputObject annotation in its class declaration.
  • Array Outputs will automatically create Tasker variable arrays. Nested arrays (like in the example) will be joined in the output.

Update

Only Tasker event conditions have access to Updates. These are bits of data that are related to whichever event that happened.

This is the Update for the GotTime example

   
      @TaskerInputRoot
      class GotTimeUpdate @JvmOverloads constructor(@field:TaskerInputField("time") var time: Long? = null)
   

As you can see these behave just like Inputs so all the same rules apply.

Triggering Events/States

To trigger an event or state condition use the requestQuery method on the Activity you configured in the manifest (or the TaskerPluginRunnerCondition.requestQuery method if you're not using Kotlin).

This is an example that triggers the GotTime event:

   
        ActivityConfigGotTime::class.java.requestQuery(context, GotTimeUpdate(Date().time))
   

As you can see in this example you're sending a GotTimeUpdate object as an Update to the event.

Returning Result in Event/States Conditions

This is for the PlayStateChanged event example:

   
    override fun getSatisfiedCondition(context: Context, input: TaskerInput<PlayStateFilter>, update: PlayState?): TaskerPluginResultCondition<PlayState> {
        if (update?.playing == null) return TaskerPluginResultConditionUnsatisfied()
        val matchesPlaying = input.regular.isPlaying && update.playing
        val matchesStopped = input.regular.isStopped && !update.playing
        if (!matchesPlaying && !matchesStopped) {
            return TaskerPluginResultConditionUnsatisfied()
        }
        return TaskerPluginResultConditionSatisfied(context, update)
    }
   

A condition has to return either TaskerPluginResultConditionSatisfied, TaskerPluginResultConditionUnsatisfied or TaskerPluginResultConditionUnknown

In the example you can see that the condition is only satisfied if the playing state from the update matches the playing state that the user has configured.

This is how Tasker conditions will react to each state:

  • TaskerPluginResultConditionSatisfied:
    • Events: will trigger
    • States: will be enabled (or stay enabled if already in that state)
  • TaskerPluginResultConditionUnsatisfied:
    • Events: will not trigger
    • States: will be disabled (or stay disabled if already in that state)
  • TaskerPluginResultConditionUnknown:
    • Events: will not trigger
    • States: will not change state