首页 > 解决方案 > 通过 RUN_COMMAND_SERVICE 意图从 Termux 接收结果

问题描述

我开发了一个 android 应用程序来向 Termux 发送一个意图并让它运行一个特定的任务。我已经设法做到了,使用以下代码:

public void downloadWithReponse() {
    Intent intent = new Intent();
    intent.setClassName("com.termux", "com.termux.app.RunCommandService");
    intent.setAction("com.termux.RUN_COMMAND");
    intent.putExtra("com.termux.RUN_COMMAND_PATH", "/data/data/com.termux/files/usr/bin/touch");
    intent.putExtra("com.termux.RUN_COMMAND_ARGUMENTS", new String[]{"/sdcard/test.txt"});
    intent.putExtra("com.termux.RUN_COMMAND_BACKGROUND", true);

    intent.putExtra(TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE.EXTRA_SESSION_ACTION, "0");
    intent.putExtra(TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE.EXTRA_COMMAND_LABEL, "touch command");
    intent.putExtra(TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE.EXTRA_COMMAND_DESCRIPTION, "Runs the touch command to show processes using the most resources.");

    // Create the intent for the IntentService class that should be sent the result by TermuxService
    Intent pluginResultsServiceIntent = new Intent(MainActivity.this, PluginResultsService.class);

    // Generate a unique execution id for this execution command
    int executionId = PluginResultsService.getNextExecutionId();

    // Optional put an extra that uniquely identifies the command internally for your app.
    // This can be an Intent extra as well with more extras instead of just an int.
    pluginResultsServiceIntent.putExtra(PluginResultsService.EXTRA_EXECUTION_ID, executionId);

    // Create the PendingIntent that will be used by TermuxService to send result of
    // commands back to the IntentService
    // Note that the requestCode (currently executionId) must be unique for each pending
    // intent, even if extras are different, otherwise only the result of only the first
    // execution will be returned since pending intent will be cancelled by android
    // after the first result has been sent back via the pending intent and termux
    // will not be able to send more.
    PendingIntent pendingIntent = PendingIntent.getService(MainActivity.this, executionId, pluginResultsServiceIntent, PendingIntent.FLAG_ONE_SHOT);
    intent.putExtra(TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE.EXTRA_PENDING_INTENT, pendingIntent);

    try {
        // Send command intent for execution
        Log.d("Termux", "Sending execution command with id " + executionId);
        //startService(intent);
        startForegroundService(intent);
    } catch (Exception e) {
        Log.e("Termux", "Failed to start execution command with id " + executionId + ": " + e.getMessage());
    }
}

使用类 PluginResultsService:

public class PluginResultsService extends IntentService {

public static final String EXTRA_EXECUTION_ID = "execution_id";

private static int EXECUTION_ID = 1000;

public static final String PLUGIN_SERVICE_LABEL = "PluginResultsService";

private static final String LOG_TAG = "PluginResultsService";

public PluginResultsService(){
    super(PLUGIN_SERVICE_LABEL);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
    if (intent == null) return;

    Log.d(LOG_TAG, PLUGIN_SERVICE_LABEL + " received execution result");

    final Bundle resultBundle = intent.getBundleExtra(TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE);
    if (resultBundle == null) {
        Log.e(LOG_TAG, "The intent does not contain the result bundle at the \"" + TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE + "\" key.");
        return;
    }

    final int executionId = intent.getIntExtra(EXTRA_EXECUTION_ID, 0);

    Log.d(LOG_TAG, "Execution id " + executionId + " result:\n" +
            "stdout:\n```\n" + resultBundle.getString(TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_STDOUT, "") + "\n```\n" +
            "stdout_original_length: `" + resultBundle.getString(TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_STDOUT_ORIGINAL_LENGTH) + "`\n" +
            "stderr:\n```\n" + resultBundle.getString(TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_STDERR, "") + "\n```\n" +
            "stderr_original_length: `" + resultBundle.getString(TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_STDERR_ORIGINAL_LENGTH) + "`\n" +
            "exitCode: `" + resultBundle.getInt(TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_EXIT_CODE) + "`\n" +
            "errCode: `" + resultBundle.getInt(TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_ERR) + "`\n" +
            "errmsg: `" + resultBundle.getString(TermuxConstants.TERMUX_APP.TERMUX_SERVICE.EXTRA_PLUGIN_RESULT_BUNDLE_ERRMSG, "") + "`");
}

public static synchronized int getNextExecutionId() {
    return EXECUTION_ID++;
}
}

上面的代码运行良好。可以看到sdcard目录下创建的文件,用logcat可以看到adb中的日志。

我的问题是我似乎无法在我的 Android 应用程序中找到从 Termux 获得响应的方法,以查看命令是否已执行,或者是否失败等。我尝试从我的应用程序中读取 logcat,但它需要系统授权,所以这对我来说是不行的。

你知道我如何使用意图来做到这一点吗?

编辑:

我已经解决了这个问题:我的 Termux 版本太旧,不支持意图结果......

但是,我现在有另一个问题,即只有在命令完全完成后才能获得命令的结果。例如,如果我的命令是下载命令,我将在 100% 下载后获取所有 ETA 和 % 下载的消息。

有任何想法吗?

标签: android-pendingintenttermux

解决方案


推荐阅读