Allows you to write and execute arbitrary Java code. The code is interpreted by the BeanShell interpreter, which uses a syntax similar to older versions of Java (pre-Java 5).
Click the magnifying glass icon next to the code field to get help writing your code. You can use the built-in AI to generate the code for you directly in the editor, or you can copy the system instructions to use with your preferred external AI (like ChatGPT, Gemini, etc.).
Two special variables are always available for you to use in your code: context
and tasker
.
The context
variable is an Android Context object from a Service. Because it is not from an Activity, you must add the flag FLAG_ACTIVITY_NEW_TASK
when starting new activities to avoid errors.
The tasker
variable is a helper object that provides methods to interact with Tasker and the system. The available methods are:
String getVariable(String name)
null
if the variable is not set.
String data = tasker.getVariable("http_data");
void setVariable(String name, Object value)
tasker.setVariable("LastLocation", "Home");
void setJavaVariable(String name, Object value)
tasker.setJavaVariable("myList", new ArrayList());
void clearGlobalJavaVariables()
tasker.clearGlobalJavaVariables();
void log(String message)
System.out.println()
.
tasker.log("Processing item number " + i);
IBinder getShizukuService(String name)
IBinder cs = tasker.getShizukuService("clipboard");
NotificationListenerService getNotificationListener()
null
if the service is not available, so your code must handle this.
if (tasker.getNotificationListener() != null) { ... }
AccessibilityService getAccessibilityService()
null
otherwise.
if (tasker.getAccessibilityService() != null) { ... }
Object implementClass(Class clzz, ClassImplementation handler)
Intent.class
) and an implementation of the ClassImplementation
interface. Your implementation's run
method will be called for every method invoked on the created object.com.joaomgcd.taskerm.action.java.ClassImplementation
. Remember to import this class at the top of your script.run
method parameters:
Callable superCaller
: This is the most important parameter. It is a handle to the original method that was called. To execute the original method, you must call superCaller.call()
. The value returned by .call()
is the original method's return value. This allows you to run code before or after the original logic.String methodName
: The name of the method being called (e.g., "putExtra"
).Object[] args
: An array of arguments passed to that method.run
method signature without a return type or access modifier. The line must be written literally as run(Callable superCaller, String methodName, Object[] args){ ... }
.This example creates a wrapper around the Intent
class that logs every method call before and after it executes.
/* Import the necessary classes. */ import com.joaomgcd.taskerm.action.java.ClassImplementation; import android.content.Intent; import java.util.concurrent.Callable; /* Create a proxied Intent object. */ intent = tasker.implementClass(Intent.class, new ClassImplementation(){ run(Callable superCaller, String methodName, Object[] args){ tasker.log("Method Logger: about to call " + methodName); /* Execute the original method and capture its result. */ Object result = superCaller.call(); tasker.log("Method Logger: finished " + methodName +". Got result: " + result); /* Return the original result to preserve functionality. */ return result; } }); /* * Now, when you use this 'intent' object, every method call will be logged. */ intent.putExtra("aaa","coool"); intent.getStringExtra("aaa");
BroadcastReceiver
This example shows how to correctly implement an abstract class to wait for a specific event (screen turning off) using RxJava.
import com.joaomgcd.taskerm.action.java.ClassImplementation; import android.content.BroadcastReceiver; import android.content.IntentFilter; import android.content.Intent; import java.util.concurrent.Callable; import io.reactivex.subjects.CompletableSubject; screenOffSignal = CompletableSubject.create(); filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); screenOffReceiver = tasker.implementClass(BroadcastReceiver.class, new ClassImplementation(){ run(Callable superCaller, String methodName, Object[] args){ /* * Check for the specific abstract method we want to implement. * For any other method, we MUST call superCaller to preserve * the original behavior (e.g., for toString(), equals(), etc.). */ if (!methodName.equals("onReceive")) { return superCaller.call(); } /* This is our custom implementation for the 'onReceive' method. */ tasker.log("Screen just went OFF!"); screenOffSignal.onComplete(); return null; /* onReceive is a void method. */ } }); context.registerReceiver(screenOffReceiver, filter); try { screenOffSignal.blockingAwait(); tasker.log("...Signal received!"); } finally { context.unregisterReceiver(screenOffReceiver); }
TaskForHelper getTask()
TaskForHelper
object has the following methods:
String getName()
: Gets the name of the current task.int getTaskId()
: Gets the ID of the running task.List getActions()
: Gets a list of all actions (as ActionForHelper
objects) in the task.int getActionCount()
: Gets the number of actions in this task.int getCurrentActionIndex()
: Gets the 0-based index of this Java Code action.ActionForHelper getNextAction()
: Gets the next action in the task.ActionForHelper getPreviousAction()
: Gets the previous action in the task.ActionForHelper
object has the following methods:
int getCode()
: Gets the action's internal code.String getName()
: Gets the action's display name.Bundle getTaskVariables()
Bundle vars = tasker.getTaskVariables();
String toJson(Object object, boolean pretty)
pretty
is true
, the JSON will be formatted for readability.
String prettyJson = tasker.toJson(myObject, true);
void doWithActivity(Consumer consumer)
This is the solution for running APIs that require an `Activity` and will not work with the default `Service` context. A prime example is showing an `AlertDialog` or any other UI element, which will crash if attempted from a service.
How it works:activity.finish()
inside your code when you are done. If you don't, the invisible activity will linger in the background. For asynchronous UI like a Dialog, this means calling finish()
inside the dialog's button listeners.This example correctly demonstrates all the rules: it shows a dialog, pauses the script until the user clicks a button, and safely closes the temporary activity.
import java.util.function.Consumer; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import io.reactivex.subjects.SingleSubject; /* Use a SingleSubject to wait for the dialog's result. */ resultSignal = SingleSubject.create(); /* Create a Consumer to build and show the dialog. */ myActivityConsumer = new Consumer() { public void accept(Object activity) { final Activity currentActivity = (Activity) activity; onClickListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { String result = "cancel"; if (which == DialogInterface.BUTTON_POSITIVE) { result = "ok"; } resultSignal.onSuccess(result); currentActivity.finish(); /* CRITICAL: Finish activity here! */ } }; AlertDialog.Builder builder = new AlertDialog.Builder(currentActivity); builder.setTitle("Confirmation"); builder.setMessage("Do you want to proceed?"); builder.setPositiveButton("OK", onClickListener); builder.setNegativeButton("Cancel", onClickListener); builder.setCancelable(false); builder.create().show(); } }; /* Execute the consumer to show the dialog. */ tasker.doWithActivity(myActivityConsumer); /* Block the script and wait for the signal from the button listener. */ userChoice = resultSignal.blockingGet(); return userChoice;
Single<Intent> getWithActivityForResult(Intent intent)
This is the standard way to get data from other apps that require user interaction, like picking a file, selecting a contact, or choosing an account.
How it works:You must subscribe to the `Single` to get the result. The most common way to do this is by using .blockingGet()
, which pauses the script until the result is available.
import android.content.Intent; /* Create an intent to pick any file. */ intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); /* Start the activity and get the Single placeholder. */ resultSingle = tasker.getWithActivityForResult(intent); /* Block the script and wait for the user to pick a file. */ resultIntent = resultSingle.blockingGet(); /* Extract the data (e.g., the file's URI) from the result. */ fileUri = resultIntent.getData(); return fileUri.toString();Example: Advanced Usage (Adding a Timeout)
The flexibility of returning a `Single` allows you to add operators like `timeout`.
import android.content.Intent; import java.util.concurrent.TimeUnit; intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); resultSingle = tasker.getWithActivityForResult(intent); try { /* Wait for the result, but with a 30-second timeout. */ resultIntent = resultSingle.timeout(30, TimeUnit.SECONDS).blockingGet(); return resultIntent.getData().toString(); } catch (Exception e) { tasker.log("User did not pick a file within 30 seconds."); return "timeout"; }
String convertToRealFilePath(Uri uri)
Use Case: Android's modern security model often provides `content://` URIs instead of direct file paths (e.g., when you pick a file). This function acts as a bridge for tools or code that require a traditional, absolute path like `/storage/emulated/0/Download/MyFile.pdf`.
IMPORTANT: This conversion is not guaranteed to succeed and may return `null`, especially if the URI points to a cloud file (like from Google Drive) or other virtual content. You MUST always check if the result is `null`.
Example: Picking a File and Getting Its Real Pathimport android.content.Intent; import android.net.Uri; /* 1. Get the result Intent from the file picker. */ intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); resultIntent = tasker.getWithActivityForResult(intent).blockingGet(); if (resultIntent == null) return "User cancelled."; /* 2. Get the content URI from the result. */ contentUri = resultIntent.getData(); if (contentUri == null) return "Could not get file URI."; tasker.log("Got content URI: " + contentUri); /* 3. Attempt to convert the content URI to a real file path. */ realPath = tasker.convertToRealFilePath(contentUri); /* 4. CRITICAL: Check if the conversion succeeded. */ if (realPath != null) { tasker.log("Successfully converted to real path: " + realPath); return realPath; } else { tasker.log("Failed to convert URI to a real path."); return "Could not get real file path."; }
You can also access Java objects that were created and returned by other Tasker Java actions (like Java Function) within your code.
The RxJava2 library is available for handling asynchronous operations like waiting for events, performing background work, or running code after a delay. The action will automatically wait for your reactive stream to complete before proceeding.
You must end your RxJava chain with a blocking operator (like blockingAwait()
, blockingGet()
, or blockingFirst()
). This is what tells the action to wait for your asynchronous operation to finish.
Waiting for Events with Subjects (Recommended)
To wait for an event from a callback (like in a `BroadcastReceiver` or another listener), the cleanest approach is to use a Subject. You create a subject, use it within your callback implementation to signal that an event has occurred, and then block the script until that signal is received. This is much simpler than using complex operators like Completable.create()
.
Example: Using a Subject to wait for a signal
import io.reactivex.subjects.CompletableSubject; /* Create a subject that will act as our signal. */ mySignal = CompletableSubject.create(); /* * In another part of your code (e.g., inside the 'run' method of an * implemented BroadcastReceiver), you would signal completion like this: * * mySignal.onComplete(); * * See the `implementClass` BroadcastReceiver example for a practical use case. */ /* This line will block and wait until onComplete() is called. */ mySignal.blockingAwait(); tasker.log("Signal received, script can now continue.");
Simple Delays and Timers
For basic delays, using a simple timer is still appropriate.
Example: Wait for 3 seconds
import java.util.concurrent.TimeUnit; import io.reactivex.Completable; /* Wait for 3 seconds on a background thread. */ Completable.timer(3, TimeUnit.SECONDS).blockingAwait(); /* This code will run after the 3-second delay. */ tasker.log("3 seconds have passed.");
Example: Get a value after a delay
import java.util.concurrent.TimeUnit; import io.reactivex.Single; import io.reactivex.functions.Function; /* Get the string "Hello" after a 1 second delay. */ result = Single.timer(1, TimeUnit.SECONDS) .map(new Function() { public Object apply(Object aLong) { return "Hello"; } }) .blockingGet(); return result; /* Returns "Hello" */
The value from your code's return statement will be stored in the output variable specified in the "Return" field. Any variables you declare inside your code are temporary and will be discarded when the action finishes.
The scope (local or global) of the returned value is determined by the capitalization of the variable name you choose:
This is ideal for passing complex data, like a List
or custom object, to another Java-based action. The actual Java object is stored in memory.
my_object
, resultslist
): The object is stored locally and is only available within the current task.myGlobalObject
, persistentList
): The object is stored globally and persists across different tasks.
myGlobalObject
is valid, but MyGlobalObject
is not).Important: Global Java objects persist in memory. If you no longer need one, you should manually clear it using the tasker.clearGlobalJavaVariables()
method or the Java Function action to prevent memory leaks.
The object's .toString()
method is called, and the resulting text is stored in a Tasker variable.
%result
, %http_data
): The variable is local to the current task.%WIFI
). These are typically reserved for Tasker's built-in read-only variables.You can use the following code to set the %wifi variable to the name of currently connected Wifi Network.
import android.content.Context; import android.net.wifi.WifiManager; import android.net.wifi.WifiInfo; /* Get the WifiManager service. */ WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); /* If WiFi manager is unavailable or WiFi is disabled, exit. */ if (wifiManager == null || !wifiManager.isWifiEnabled()) return null; /* Get information about the currently connected WiFi network. */ WifiInfo wifiInfo = wifiManager.getConnectionInfo(); if (wifiInfo == null) return null; /* Get the raw SSID string. */ String ssid = wifiInfo.getSSID(); if (ssid == null) return null; /* Remove surrounding quotes from the SSID if they exist. */ if (ssid.startsWith("\"") && ssid.endsWith("\"")) ssid = ssid.substring(1, ssid.length() - 1); /* Return the cleaned SSID. */ return ssid;
%wifi