diff --git a/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java b/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java
index 62fa7864f7..df45a5e9a7 100644
--- a/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java
+++ b/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java
@@ -3,11 +3,14 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import androidx.annotation.NonNull;
import com.termux.shared.R;
+import com.termux.shared.file.FileUtils;
import com.termux.shared.file.TermuxFileUtils;
import com.termux.shared.logger.Logger;
import com.termux.shared.markdown.MarkdownUtils;
@@ -101,6 +104,90 @@ public static Context getTermuxWidgetPackageContext(@NonNull Context context) {
+ /**
+ * Check if Termux app is installed and enabled. This can be used by external apps that don't
+ * share `sharedUserId` with the Termux app.
+ *
+ * If your third-party app is targeting sdk `30` (android `11`), then it needs to add `com.termux`
+ * package to the `queries` element or request `QUERY_ALL_PACKAGES` permission in its
+ * `AndroidManifest.xml`. Otherwise it will get `PackageSetting{...... com.termux/......} BLOCKED`
+ * errors in `logcat` and `RUN_COMMAND` won't work.
+ * Check [package-visibility](https://developer.android.com/training/basics/intents/package-visibility#package-name),
+ * `QUERY_ALL_PACKAGES` [googleplay policy](https://support.google.com/googleplay/android-developer/answer/10158779
+ * and this [article](https://medium.com/androiddevelopers/working-with-package-visibility-dc252829de2d) for more info.
+ *
+ * {@code
+ *
+ *
+ *
+ *
+ * }
+ *
+ * @param currentPackageContext The context of current package.
+ * @return Returns {@code errmsg} if termux package is not installed or disabled, otherwise {@code null}.
+ */
+ public static String isTermuxAppInstalled(@NonNull final Context currentPackageContext) {
+ String errmsg = null;
+
+ PackageManager packageManager = currentPackageContext.getPackageManager();
+
+ ApplicationInfo applicationInfo;
+ try {
+ applicationInfo = packageManager.getApplicationInfo(TermuxConstants.TERMUX_PACKAGE_NAME, 0);
+ } catch (final PackageManager.NameNotFoundException e) {
+ applicationInfo = null;
+ }
+ boolean termuxAppEnabled = (applicationInfo != null && applicationInfo.enabled);
+
+ // If Termux app is not installed or is disabled
+ if (!termuxAppEnabled)
+ errmsg = currentPackageContext.getString(R.string.error_termux_app_not_installed_or_disabled_warning);
+
+ return errmsg;
+ }
+
+ /**
+ * Check if Termux app is installed and accessible. This can only be used by apps that share
+ * `sharedUserId` with the Termux app.
+ *
+ * This is done by checking if first checking if app is installed and enabled and then if
+ * {@code currentPackageContext} can be used to get the {@link Context} of the app with
+ * {@link TermuxConstants#TERMUX_PACKAGE_NAME} and then if
+ * {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} exists and has
+ * {@link FileUtils#APP_WORKING_DIRECTORY_PERMISSIONS} permissions. The directory will not
+ * be automatically created and neither the missing permissions automatically set.
+ *
+ * @param currentPackageContext The context of current package.
+ * @return Returns {@code errmsg} if failed to get termux package {@link Context} or
+ * {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} is accessible, otherwise {@code null}.
+ */
+ public static String isTermuxAppAccessible(@NonNull final Context currentPackageContext) {
+ String errmsg = isTermuxAppInstalled(currentPackageContext);
+ if (errmsg == null) {
+ Context termuxPackageContext = TermuxUtils.getTermuxPackageContext(currentPackageContext);
+ // If failed to get Termux app package context
+ if (termuxPackageContext == null)
+ errmsg = currentPackageContext.getString(R.string.error_termux_app_package_context_not_accessible);
+
+ if (errmsg == null) {
+ // If TermuxConstants.TERMUX_PREFIX_DIR_PATH is not a directory or does not have required permissions
+ Error error = TermuxFileUtils.isTermuxPrefixDirectoryAccessible(false, false);
+ if (error != null)
+ errmsg = currentPackageContext.getString(R.string.error_termux_prefix_dir_path_not_accessible,
+ PackageUtils.getAppNameForPackage(currentPackageContext));
+ }
+ }
+
+ if (errmsg != null)
+ return errmsg + " " + currentPackageContext.getString(R.string.msg_termux_app_required_by_app,
+ PackageUtils.getAppNameForPackage(currentPackageContext));
+ else
+ return null;
+ }
+
+
+
/**
* Send the {@link TermuxConstants#BROADCAST_TERMUX_OPENED} broadcast to notify apps that Termux
* app has been opened.
diff --git a/termux-shared/src/main/res/values/strings.xml b/termux-shared/src/main/res/values/strings.xml
index 65e3667c86..c79d04c19a 100644
--- a/termux-shared/src/main/res/values/strings.xml
+++ b/termux-shared/src/main/res/values/strings.xml
@@ -9,6 +9,7 @@
+
]>
@@ -71,6 +72,15 @@
on github or other official termux community forums **will likely be automatically closed/deleted** and may
even result in **temporary or permanent** ban. Check %1$s/wiki/Hacking for details.
+ The &TERMUX_APP_NAME; is required by the %1$s app to run termux commands."
+ The &TERMUX_APP_NAME; app is not installed or is disabled."
+ The &TERMUX_APP_NAME; app (package context) is not accessible."
+ The &TERMUX_APP_NAME; app $PREFIX directory is not accessible by the %1$s app.
+ This may be because you have not installed or setup &TERMUX_APP_NAME; app or
+ &TERMUX_APP_NAME; app and %1$s app both have different APK signatures because you have managed to install both apps from different sources.
+ It may also be because &TERMUX_APP_NAME; $PREFIX directory \"&TERMUX_PREFIX_DIR_PATH;\" does not exist or does not have read,
+ write and execute permissions."
+