Skip to content

Commit

Permalink
Added: Add support for waiting for foreground session commands
Browse files Browse the repository at this point in the history
Previously configured actions will behave the same, i.e wait for only background commands. For new or edited actions, the `Wait for result for commands` toggle value will be used to decide whether to wait for result of commands. It will apply to both foreground session and background commands.

Note that for foreground commands, only the session transcript is returned which will contain both `stdout` and `stderr` combined in `%stdout` variable, basically anything sent to the the pseudo terminal `/dev/pts`, including `PS1` prefixes for interactive sessions.

For foreground commands that exited with failure will require `termux-app` version `>=0.118` for sessions to automatically close without waiting for user to press enter as per termux/termux-app@162a430b.

Closes #39
  • Loading branch information
agnostic-apollo committed Sep 9, 2021
1 parent 1c1567f commit fecba50
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 75 deletions.
58 changes: 37 additions & 21 deletions app/src/main/java/com/termux/tasker/EditConfigurationActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* This is the "Edit" activity for a Locale Plug-in.
Expand All @@ -45,6 +46,7 @@ public final class EditConfigurationActivity extends AbstractPluginActivity {
private TextInputEditText mArgumentsText;
private AutoCompleteTextView mWorkingDirectoryPathText;
private CheckBox mInTerminalCheckbox;
private CheckBox mWaitForResult;
private TextView mExecutableAbsolutePathText;
private TextView mWorkingDirectoryAbsolutePathText;
private TextView mTermuxAppFilesPathInaccessibleWarning;
Expand Down Expand Up @@ -82,6 +84,7 @@ protected void onCreate(final Bundle savedInstanceState) {
mArgumentsText = findViewById(R.id.arguments);
mWorkingDirectoryPathText = findViewById(R.id.working_directory_path);
mInTerminalCheckbox = findViewById(R.id.in_terminal);
mWaitForResult = findViewById(R.id.wait_for_result);
mExecutableAbsolutePathText = findViewById(R.id.executable_absolute_path);
mWorkingDirectoryAbsolutePathText = findViewById(R.id.working_directory_absolute_path);
mTermuxAppFilesPathInaccessibleWarning = findViewById(R.id.termux_app_files_path_inaccessible_warning);
Expand Down Expand Up @@ -127,7 +130,7 @@ public void afterTextChanged(Editable editable) {
if (localeBundle != null) {
String errmsg;
// If bundle is valid, then load values from bundle
errmsg = PluginBundleManager.isBundleValid(this, localeBundle);
errmsg = PluginBundleManager.parseBundle(this, localeBundle);
if (errmsg == null) {
final String selectedExecutable = localeBundle.getString(PluginBundleManager.EXTRA_EXECUTABLE);
mExecutablePathText.setText(selectedExecutable);
Expand All @@ -137,6 +140,8 @@ public void afterTextChanged(Editable editable) {
mWorkingDirectoryPathText.setText(selectedWorkingDirectory);
final boolean inTerminal = localeBundle.getBoolean(PluginBundleManager.EXTRA_TERMINAL);
mInTerminalCheckbox.setChecked(inTerminal);
final boolean waitForResult = localeBundle.getBoolean(PluginBundleManager.EXTRA_WAIT_FOR_RESULT);
mWaitForResult.setChecked(waitForResult);
} else {
Logger.logError(LOG_TAG, errmsg);
}
Expand Down Expand Up @@ -170,6 +175,7 @@ public void finish() {
final String arguments = mArgumentsText.getText() == null ? null : mArgumentsText.getText().toString();
final String workingDirectory = mWorkingDirectoryPathText.getText() == null ? null : mWorkingDirectoryPathText.getText().toString();
final boolean inTerminal = mInTerminalCheckbox.isChecked();
final boolean waitForResult = mWaitForResult.isChecked();

if (executable != null && executable.length() > 0) {
final Intent resultIntent = new Intent();
Expand All @@ -182,10 +188,11 @@ public void finish() {
* Android platform objects (A Serializable class private to this plug-in's APK cannot be
* stored in the Bundle, as Locale's classloader will not recognize it).
*/
final Bundle resultBundle = PluginBundleManager.generateBundle(getApplicationContext(), executable, arguments, workingDirectory, inTerminal);
final Bundle resultBundle = PluginBundleManager.generateBundle(getApplicationContext(),
executable, arguments, workingDirectory, inTerminal, waitForResult);

// The blurb is a concise status text to be displayed in the host's UI.
final String blurb = generateBlurb(executable, arguments, inTerminal);
final String blurb = generateBlurb(executable, arguments, inTerminal, waitForResult);

// If host supports variable replacement when running plugin action, then
// request it to replace variables in following fields
Expand All @@ -201,20 +208,24 @@ public void finish() {
resultIntent.putExtra(com.twofortyfouram.locale.Intent.EXTRA_STRING_BLURB, blurb);

// Configuration information for Tasker variables returned from the executed task
//
// Do not run if we are opening a terminal, because the user might not care about this
// if they are running something that will literally pop up in front of them (Plus
// getting that information requires additional work for now)
if(!inTerminal) {
if(waitForResult) {
List<String> relevantVariableList = new ArrayList<>();
relevantVariableList.add(PluginUtils.PLUGIN_VARIABLE_STDOUT + "\nStandard Output\nThe <B>stdout</B> of the command.");
relevantVariableList.add(PluginUtils.PLUGIN_VARIABLE_STDOUT_ORIGINAL_LENGTH + "\nStandard Output Original Length\nThe original length of <B>stdout</B>.");

// For foreground commands, the session transcript is returned which will contain
// both stdout and stderr combined, basically anything sent to the the pseudo
// terminal /dev/pts, including PS1 prefixes for interactive sessions.
if (!inTerminal) {
relevantVariableList.add(PluginUtils.PLUGIN_VARIABLE_STDERR + "\nStandard Error\nThe <B>stderr</B> of the command.");
relevantVariableList.add(PluginUtils.PLUGIN_VARIABLE_STDERR_ORIGINAL_LENGTH + "\nStandard Error Original Length\nThe original length of <B>stderr</B>.");
}

relevantVariableList.add(PluginUtils.PLUGIN_VARIABLE_EXIT_CODE + "\nExit Code\nThe <B>exit code</B> of the command." +
"0 often means success and anything else is usually a failure of some sort.");

if (TaskerPlugin.hostSupportsRelevantVariables(getIntent().getExtras())) {
TaskerPlugin.addRelevantVariableList(resultIntent, new String[]{
PluginUtils.PLUGIN_VARIABLE_STDOUT + "\nStandard Output\nThe <B>stdout</B> of the command.",
PluginUtils.PLUGIN_VARIABLE_STDOUT_ORIGINAL_LENGTH + "\nStandard Output Original Length\nThe original length of <B>stdout</B>.",
PluginUtils.PLUGIN_VARIABLE_STDERR + "\nStandard Error\nThe <B>stderr</B> of the command.",
PluginUtils.PLUGIN_VARIABLE_STDERR_ORIGINAL_LENGTH + "\nStandard Error Original Length\nThe original length of <B>stderr</B>.",
PluginUtils.PLUGIN_VARIABLE_EXIT_CODE + "\nExit Code\nThe <B>exit code</B> of the command. " +
"0 often means success and anything else is usually a failure of some sort."
});
TaskerPlugin.addRelevantVariableList(resultIntent, relevantVariableList.toArray(new String[0]));
}
}

Expand All @@ -239,13 +250,18 @@ public void finish() {
* @param executable value set for the action.
* @param arguments value set for the action.
* @param inTerminal value set for the action.
* @param waitForResult value set for the action.
* @return A blurb for the plug-in.
*/
String generateBlurb(final String executable, final String arguments, boolean inTerminal) {
final int stringResource = inTerminal ? R.string.blurb_in_terminal : R.string.blurb_in_background;
final String message = getString(stringResource, executable, arguments);
final int maxBlurbLength = 60; // R.integer.twofortyfouram_locale_maximum_blurb_length.
return (message.length() > maxBlurbLength) ? message.substring(0, maxBlurbLength) : message;
String generateBlurb(final String executable, final String arguments, boolean inTerminal, boolean waitForResult) {
StringBuilder builder = new StringBuilder();
builder.append(getString(R.string.blurb_executable_and_arguments, executable, arguments));
builder.append("\n\n").append(getString(inTerminal ? R.string.blurb_in_terminal : R.string.blurb_not_in_terminal));
builder.append("\n").append(getString(waitForResult ? R.string.blurb_wait_for_result : R.string.blurb_no_wait_for_result));

String blurb = builder.toString();
final int maxBlurbLength = 60; // R.integer.twofortyfouram_locale_maximum_blurb_length
return (blurb.length() > maxBlurbLength) ? blurb.substring(0, maxBlurbLength) : blurb;
}

@Override
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/com/termux/tasker/FireReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void onReceive(final Context context, final Intent intent) {
BundleScrubber.scrub(bundle);

// If bundle is not valid, then return RESULT_CODE_FAILED to plugin host app
errmsg = PluginBundleManager.isBundleValid(context, bundle);
errmsg = PluginBundleManager.parseBundle(context, bundle);
if (errmsg != null) {
Logger.logError(LOG_TAG, errmsg);
PluginUtils.sendImmediateResultToPluginHostApp(this, intent, TaskerPlugin.Setting.RESULT_CODE_FAILED, errmsg);
Expand All @@ -71,6 +71,8 @@ public void onReceive(final Context context, final Intent intent) {
final String arguments_string = bundle.getString(PluginBundleManager.EXTRA_ARGUMENTS);
executionCommand.workingDirectory = IntentUtils.getStringExtraIfSet(intent, PluginBundleManager.EXTRA_WORKDIR, null);
executionCommand.inBackground = !(intent.getBooleanExtra(PluginBundleManager.EXTRA_TERMINAL, false));
final boolean waitForResult = bundle.getBoolean(PluginBundleManager.EXTRA_WAIT_FOR_RESULT, true);



// If Termux app is not installed, enabled or accessible with current context or if
Expand Down Expand Up @@ -166,6 +168,7 @@ public void onReceive(final Context context, final Intent intent) {


Logger.logVerboseExtended(LOG_TAG, executionCommand.toString());
Logger.logVerbose(LOG_TAG, "Wait For Result: `" + waitForResult + "`");

// Create execution intent with the action TERMUX_SERVICE#ACTION_SERVICE_EXECUTE to be sentto the TERMUX_SERVICE
Intent executionIntent = new Intent(TERMUX_SERVICE.ACTION_SERVICE_EXECUTE, executionCommand.executableUri);
Expand All @@ -177,7 +180,7 @@ public void onReceive(final Context context, final Intent intent) {
executionIntent.putExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, executionCommand.inBackground);

// Send execution intent to TERMUX_SERVICE
PluginUtils.sendExecuteIntentToExecuteService(context, this, intent, executionIntent, executionCommand.inBackground);
PluginUtils.sendExecuteIntentToExecuteService(context, this, intent, executionIntent, waitForResult);
}

}
58 changes: 29 additions & 29 deletions app/src/main/java/com/termux/tasker/PluginBundleManager.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.termux.tasker;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
Expand All @@ -10,40 +11,25 @@
/**
* Class for managing the {@link com.twofortyfouram.locale.Intent#EXTRA_BUNDLE} for this plug-in.
*/
final class PluginBundleManager {
public class PluginBundleManager {

/**
* Type: {@code String}.
*
* The path to the executable to execute.
*/
/** The {@code String} extra for the path to the executable to execute. */
public static final String EXTRA_EXECUTABLE = TermuxConstants.TERMUX_TASKER_PACKAGE_NAME + ".extra.EXECUTABLE"; // Default: "com.termux.tasker.extra.EXECUTABLE"

/**
* Type: {@code sting}.
*
* The arguments to pass to the script.
*/
/** The {@code String} extra for the arguments to pass to the executable. */
public static final String EXTRA_ARGUMENTS = TermuxConstants.TERMUX_PACKAGE_NAME + ".execute.arguments"; // Default: "com.termux.execute.arguments"

/**
* Type: {@code String}.
*
* The path to current working directory for execution.
*/
/** The {@code String} extra for path to current working directory for execution. */
public static final String EXTRA_WORKDIR = TermuxConstants.TERMUX_TASKER_PACKAGE_NAME + ".extra.WORKDIR"; // Default: "com.termux.tasker.extra.WORKDIR"

/**
* Type: {@code boolean}.
*
* If the executable should be run inside a terminal.
*/
/** The {@code boolean} extra for whether the executable should be run inside a terminal. */
public static final String EXTRA_TERMINAL = TermuxConstants.TERMUX_TASKER_PACKAGE_NAME + ".extra.TERMINAL"; // Default: "com.termux.tasker.extra.TERMINAL"

/** The {@code boolean} extra for whether plugin action should wait for result of commands or not. */
public static final String EXTRA_WAIT_FOR_RESULT = TermuxConstants.TERMUX_TASKER_PACKAGE_NAME + ".extra.WAIT_FOR_RESULT"; // Default: "com.termux.tasker.extra.WAIT_FOR_RESULT"

/**
* Type: {@code int}.
* <p>
* versionCode of the plug-in that saved the Bundle.
* The {@code int} extra for the versionCode of the plugin app that saved the Bundle.
*
* This extra is not strictly required, however it makes backward and forward compatibility significantly
* easier. For example, suppose a bug is found in how some version of the plug-in stored its Bundle. By
Expand All @@ -60,7 +46,8 @@ final class PluginBundleManager {
* @param bundle The {@link Bundle} to verify. May be {@code null}, which will always return {@code false}.
* @return Returns the {@code errmsg} if Bundle is not valid, otherwise {@code null}.
*/
public static String isBundleValid(final Context context, final Bundle bundle) {
@SuppressLint("DefaultLocale")
public static String parseBundle(final Context context, final Bundle bundle) {
if (bundle == null) return context.getString(R.string.error_null_or_empty_executable);

/*
Expand All @@ -72,6 +59,7 @@ public static String isBundleValid(final Context context, final Bundle bundle) {
* The bundle may optionally contain:
* - EXTRA_WORKDIR
* - EXTRA_TERMINAL
* - EXTRA_WAIT_FOR_RESULT
* - VARIABLE_REPLACE_KEYS
*/

Expand All @@ -88,13 +76,13 @@ public static String isBundleValid(final Context context, final Bundle bundle) {
}

/*
* Check if bundle contains at least 3 keys but no more than 6.
* Check if bundle contains at least 3 keys but no more than 7.
* Run this test after checking for required Bundle extras above so that the error message
* is more useful. (E.g. the caller will see what extras are missing, rather than just a
* message that there is the wrong number).
*/
if (bundle.keySet().size() < 3 || bundle.keySet().size() > 6) {
return String.format("The bundle must contain 3-6 keys, but currently contains %d keys.", bundle.keySet().size());
if (bundle.keySet().size() < 3 || bundle.keySet().size() > 7) {
return String.format("The bundle must contain 3-7 keys, but currently contains %d keys.", bundle.keySet().size());
}

if (TextUtils.isEmpty(bundle.getString(EXTRA_EXECUTABLE))) {
Expand All @@ -105,15 +93,27 @@ public static String isBundleValid(final Context context, final Bundle bundle) {
return String.format("The bundle extra %s appears to be the wrong type. It must be an int.", BUNDLE_EXTRA_INT_VERSION_CODE);
}

if (!bundle.containsKey(EXTRA_WAIT_FOR_RESULT)) {
// Termux:Tasker <= v0.5 did not have the EXTRA_WAIT_FOR_RESULT key so we only wait
// for results for background commands
if (bundle.containsKey(EXTRA_TERMINAL))
bundle.putBoolean(EXTRA_WAIT_FOR_RESULT, !bundle.getBoolean(EXTRA_TERMINAL));
else
bundle.putBoolean(EXTRA_WAIT_FOR_RESULT, true);
}

return null;
}

public static Bundle generateBundle(final Context context, final String executable, final String arguments, final String workingDirectory, final boolean inTerminal) {
public static Bundle generateBundle(final Context context, final String executable,
final String arguments, final String workingDirectory,
final boolean inTerminal, final boolean waitForResult) {
final Bundle result = new Bundle();
result.putString(EXTRA_EXECUTABLE, executable);
result.putString(EXTRA_ARGUMENTS,arguments);
result.putString(EXTRA_WORKDIR, workingDirectory);
result.putBoolean(EXTRA_TERMINAL, inTerminal);
result.putBoolean(EXTRA_WAIT_FOR_RESULT, waitForResult);
result.putInt(BUNDLE_EXTRA_INT_VERSION_CODE, PackageUtils.getVersionCodeForPackage(context));
return result;
}
Expand Down
Loading

0 comments on commit fecba50

Please sign in to comment.