From 4e474d0b26170f642ab615578f4e7bff1474ccf7 Mon Sep 17 00:00:00 2001 From: David Kramer Date: Mon, 18 Jun 2018 11:28:09 -0600 Subject: [PATCH 1/8] Add TermuxIntentHelper class for executing background commands via termux app --- .../termux/api/util/TermuxIntentHelper.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 app/src/main/java/com/termux/api/util/TermuxIntentHelper.java diff --git a/app/src/main/java/com/termux/api/util/TermuxIntentHelper.java b/app/src/main/java/com/termux/api/util/TermuxIntentHelper.java new file mode 100644 index 000000000..f5014aa68 --- /dev/null +++ b/app/src/main/java/com/termux/api/util/TermuxIntentHelper.java @@ -0,0 +1,30 @@ +package com.termux.api.util; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import java.util.Arrays; + +public final class TermuxIntentHelper { + private static final String TERMUX_SERVICE = "com.termux.app.TermuxService"; + private static final String ACTION_EXECUTE = "com.termux.service_execute"; + private static final String EXTRA_ARGUMENTS = "com.termux.execute.arguments"; + private static final String BIN_SH = "/data/data/com.termux/files/usr/bin/sh"; + private static final String EXTRA_EXECUTE_IN_BACKGROUND = "com.termux.execute.background"; + + + public static PendingIntent createPendingIntent(Context context, String action) { + String[] arguments = new String[]{"-c", action}; + Uri executeUri = new Uri.Builder().scheme("com.termux.file") + .path(BIN_SH) + .appendQueryParameter("arguments", Arrays.toString(arguments)) + .build(); + Intent executeIntent = new Intent(ACTION_EXECUTE, executeUri); + executeIntent.setClassName("com.termux", TERMUX_SERVICE); + executeIntent.putExtra(EXTRA_EXECUTE_IN_BACKGROUND, true); + executeIntent.putExtra(EXTRA_ARGUMENTS, arguments); + return PendingIntent.getService(context, 0, executeIntent, 0); + } +} From e23510abe45ac6ee23398acdedb01c8a3f6cc716 Mon Sep 17 00:00:00 2001 From: David Kramer Date: Mon, 18 Jun 2018 11:47:36 -0600 Subject: [PATCH 2/8] Adds link color resource --- app/src/main/res/values/colors.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 327c0604c..2e5641ddc 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,5 +1,6 @@ #66000000 + #269588 From eb5f090ee716ff2402bcdf97eff8be4f0e633152 Mon Sep 17 00:00:00 2001 From: David Kramer Date: Mon, 18 Jun 2018 11:48:27 -0600 Subject: [PATCH 3/8] Add new string resources for help dialog --- app/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bd13a9b20..bb49bf48a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,4 +5,7 @@ Grant permission Termux:API needs the following permission(s):\n + Learn more + Termux:API - Version: %1$s + The required \'termux-api\' package is missing! Please install it to use Termux:API functionality From 16e764d40acb926e617966ff9ad2a2f339c80242 Mon Sep 17 00:00:00 2001 From: David Kramer Date: Mon, 18 Jun 2018 11:49:42 -0600 Subject: [PATCH 4/8] Update TermuxAPIReceiver to listen to Termux opened broadcast / Add TermuxApiVerifyActivity and dialog layout --- app/src/main/AndroidManifest.xml | 18 ++- .../com/termux/api/TermuxApiReceiver.java | 11 +- .../api/util/TermuxApiVerifyActivity.java | 153 ++++++++++++++++++ .../main/res/layout/dialog_missing_tools.xml | 44 +++++ 4 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/termux/api/util/TermuxApiVerifyActivity.java create mode 100644 app/src/main/res/layout/dialog_missing_tools.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0d58de2fb..aee68eed9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,6 +26,7 @@ + @@ -43,14 +44,21 @@ - + + + + + + + diff --git a/app/src/main/java/com/termux/api/TermuxApiReceiver.java b/app/src/main/java/com/termux/api/TermuxApiReceiver.java index 9f3469f4f..f5a418f58 100644 --- a/app/src/main/java/com/termux/api/TermuxApiReceiver.java +++ b/app/src/main/java/com/termux/api/TermuxApiReceiver.java @@ -10,11 +10,19 @@ import com.termux.api.util.TermuxApiLogger; import com.termux.api.util.TermuxApiPermissionActivity; +import com.termux.api.util.TermuxApiVerifyActivity; public class TermuxApiReceiver extends BroadcastReceiver { + private static final String TERMUX_OPENED = "com.termux.app.OPENED"; @Override - public void onReceive(Context context, Intent intent) { + public void onReceive(final Context context, Intent intent) { + // validate user has installed termux-api package when Termux first opened + if (TERMUX_OPENED.equals(intent.getAction())) { + TermuxApiVerifyActivity.checkAndNotifyToolStatus(context); + return; + } + String apiMethod = intent.getStringExtra("api_method"); if (apiMethod == null) { TermuxApiLogger.error("Missing 'api_method' extra"); @@ -176,5 +184,4 @@ public void onReceive(Context context, Intent intent) { TermuxApiLogger.error("Unrecognized 'api_method' extra: '" + apiMethod + "'"); } } - } diff --git a/app/src/main/java/com/termux/api/util/TermuxApiVerifyActivity.java b/app/src/main/java/com/termux/api/util/TermuxApiVerifyActivity.java new file mode 100644 index 000000000..7c3e36f52 --- /dev/null +++ b/app/src/main/java/com/termux/api/util/TermuxApiVerifyActivity.java @@ -0,0 +1,153 @@ +package com.termux.api.util; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.PendingIntent; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.View; +import android.view.Window; +import android.widget.Toast; + +import com.termux.api.R; + +import java.io.File; + +/** + * Verifies installation of termux-api package for user and provides notification + * dialog if it isn't as well as help link to the Termux Wiki + */ +public class TermuxApiVerifyActivity extends Activity { + private static final String TERMUX_API_WIKI_URL = "https://wiki.termux.com/wiki/Termux:API"; + private static final int DIALOG_DELAY = 2500; + + + protected Dialog dialog; + + + public static void checkAndNotifyToolStatus(final Context context) { + if (!hasApiToolsInstalled()) { + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + Intent installIntent = new Intent(context, TermuxApiVerifyActivity.class); + installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(installIntent); + } + }, DIALOG_DELAY); + } + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setFinishOnTouchOutside(false); + showDialog(); + } + + protected void showDialog() { + View dialogView = View.inflate(this, R.layout.dialog_missing_tools, null); + requestWindowFeature(Window.FEATURE_NO_TITLE); + + dialog = getDialog(dialogView); + dialog.show(); + } + + /** + * Installs termux-api package + * @param view + */ + public void installToolsButtonClicked(View view) { + dismiss(); + + Toast.makeText(this, "Installing termux-api", Toast.LENGTH_SHORT).show(); + + // install termux-api and use our notification api to display success + String command = "pkg install termux-api && termux-notification --title 'Termux:API!' --content 'termux-api package installed successfully!'"; + execTermuxCommand(this, command); + } + + /** + * Launches TermuxAPI Wiki page + * @param view + */ + public void learnMoreButtonClicked(View view) { + dismiss(); + + Intent urlIntent = new Intent(Intent.ACTION_VIEW); + urlIntent.setData(Uri.parse(TERMUX_API_WIKI_URL)); + startActivity(urlIntent); + } + + protected Dialog getDialog(View dialogView) { + Dialog dialog = new AlertDialog.Builder(this) + .setTitle(getString(R.string.version, getInstalledTermuxAPIVersion(this))) + .setView(dialogView) + .setPositiveButton("Okay", null) + .setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialogInterface) { + finish(); + } + }) + .create(); + dialog.setCanceledOnTouchOutside(false); + return dialog; + } + + protected void dismiss() { + if (dialog != null) { + dialog.dismiss(); + } + } + + /** + * Returns version number of our TermuxAPI + * @param context + * @return + */ + protected static String getInstalledTermuxAPIVersion(Context context) { + String version = "Unknown"; + try { + PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + version = packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + TermuxApiLogger.error("Failed to obtain TermuxAPIVersion", e); + } + return version; + } + + + /** + * Executes specified command through Termux using a PendingIntent + * @param context + * @param command + */ + protected static void execTermuxCommand(Context context, String command) { + PendingIntent pi = TermuxIntentHelper.createPendingIntent(context, command); + try { + pi.send(); + } catch (PendingIntent.CanceledException e) { + TermuxApiLogger.error("execTermuxCommand error", e); + } + } + + /** + * Checks to see if 'termux-api' pkg is installed + * @return + */ + @SuppressLint("SdCardPath") + protected static boolean hasApiToolsInstalled() { + return new File("/data/data/com.termux/files/usr/libexec/termux-api").exists(); + } +} diff --git a/app/src/main/res/layout/dialog_missing_tools.xml b/app/src/main/res/layout/dialog_missing_tools.xml new file mode 100644 index 000000000..7f2dc59ae --- /dev/null +++ b/app/src/main/res/layout/dialog_missing_tools.xml @@ -0,0 +1,44 @@ + + + + + +