diff --git a/app/build.gradle b/app/build.gradle index 24b5882580..3d519ffa27 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,6 +21,15 @@ android { versionCode 108 versionName "0.108" + manifestPlaceholders.TERMUX_PACKAGE_NAME = "com.termux" + manifestPlaceholders.TERMUX_APP_NAME = "Termux" + manifestPlaceholders.TERMUX_API_APP_NAME = "Termux:API" + manifestPlaceholders.TERMUX_BOOT_APP_NAME = "Termux:Boot" + manifestPlaceholders.TERMUX_FLOAT_APP_NAME = "Termux:Float" + manifestPlaceholders.TERMUX_STYLING_APP_NAME = "Termux:Styling" + manifestPlaceholders.TERMUX_TASKER_APP_NAME = "Termux:Tasker" + manifestPlaceholders.TERMUX_WIDGET_APP_NAME = "Termux:Widget" + externalNativeBuild { ndkBuild { cFlags "-std=c11", "-Wall", "-Wextra", "-Werror", "-Os", "-fno-stack-protector", "-Wl,--gc-sections" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 59324ec799..0a2e70a616 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,13 +2,13 @@ xmlns:tools="http://schemas.android.com/tools" package="com.termux" android:installLocation="internalOnly" - android:sharedUserId="com.termux" + android:sharedUserId="${TERMUX_PACKAGE_NAME}" android:sharedUserLabel="@string/shared_user_label" > - @@ -112,7 +113,7 @@ @@ -122,25 +123,25 @@ + android:permission="${TERMUX_PACKAGE_NAME}.permission.RUN_COMMAND" > - + - + android:name=".app.TermuxOpenReceiver$ContentProvider" /> diff --git a/app/src/main/java/com/termux/app/BackgroundJob.java b/app/src/main/java/com/termux/app/BackgroundJob.java index faf0089bcc..bb4e19a828 100644 --- a/app/src/main/java/com/termux/app/BackgroundJob.java +++ b/app/src/main/java/com/termux/app/BackgroundJob.java @@ -36,7 +36,7 @@ public BackgroundJob(String cwd, String fileToExecute, final String[] args, fina public BackgroundJob(String cwd, String fileToExecute, final String[] args, final TermuxService service, PendingIntent pendingIntent) { String[] env = buildEnvironment(false, cwd); - if (cwd == null || cwd.isEmpty()) cwd = TermuxService.HOME_PATH; + if (cwd == null || cwd.isEmpty()) cwd = TermuxConstants.HOME_PATH; final String[] progArray = setupProcessArgs(fileToExecute, args); final String processDescription = Arrays.toString(progArray); @@ -134,17 +134,17 @@ private static void addToEnvIfPresent(List environment, String name) { } static String[] buildEnvironment(boolean failSafe, String cwd) { - new File(TermuxService.HOME_PATH).mkdirs(); + new File(TermuxConstants.HOME_PATH).mkdirs(); - if (cwd == null || cwd.isEmpty()) cwd = TermuxService.HOME_PATH; + if (cwd == null || cwd.isEmpty()) cwd = TermuxConstants.HOME_PATH; List environment = new ArrayList<>(); environment.add("TERMUX_VERSION=" + BuildConfig.VERSION_NAME); environment.add("TERM=xterm-256color"); environment.add("COLORTERM=truecolor"); - environment.add("HOME=" + TermuxService.HOME_PATH); - environment.add("PREFIX=" + TermuxService.PREFIX_PATH); + environment.add("HOME=" + TermuxConstants.HOME_PATH); + environment.add("PREFIX=" + TermuxConstants.PREFIX_PATH); environment.add("BOOTCLASSPATH=" + System.getenv("BOOTCLASSPATH")); environment.add("ANDROID_ROOT=" + System.getenv("ANDROID_ROOT")); environment.add("ANDROID_DATA=" + System.getenv("ANDROID_DATA")); @@ -164,9 +164,9 @@ static String[] buildEnvironment(boolean failSafe, String cwd) { environment.add("PATH= " + System.getenv("PATH")); } else { environment.add("LANG=en_US.UTF-8"); - environment.add("PATH=" + TermuxService.PREFIX_PATH + "/bin"); + environment.add("PATH=" + TermuxConstants.PREFIX_PATH + "/bin"); environment.add("PWD=" + cwd); - environment.add("TMPDIR=" + TermuxService.PREFIX_PATH + "/tmp"); + environment.add("TMPDIR=" + TermuxConstants.PREFIX_PATH + "/tmp"); } return environment.toArray(new String[0]); @@ -215,7 +215,7 @@ static String[] setupProcessArgs(String fileToExecute, String[] args) { if (executable.startsWith("/usr") || executable.startsWith("/bin")) { String[] parts = executable.split("/"); String binary = parts[parts.length - 1]; - interpreter = TermuxService.PREFIX_PATH + "/bin/" + binary; + interpreter = TermuxConstants.PREFIX_PATH + "/bin/" + binary; } break; } @@ -225,7 +225,7 @@ static String[] setupProcessArgs(String fileToExecute, String[] args) { } } else { // No shebang and no ELF, use standard shell. - interpreter = TermuxService.PREFIX_PATH + "/bin/sh"; + interpreter = TermuxConstants.PREFIX_PATH + "/bin/sh"; } } } diff --git a/app/src/main/java/com/termux/app/RunCommandService.java b/app/src/main/java/com/termux/app/RunCommandService.java index 93b43cf606..eb6efc216a 100644 --- a/app/src/main/java/com/termux/app/RunCommandService.java +++ b/app/src/main/java/com/termux/app/RunCommandService.java @@ -13,6 +13,8 @@ import android.util.Log; import com.termux.R; +import com.termux.app.TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE; +import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE; import java.io.File; import java.io.FileInputStream; @@ -77,12 +79,6 @@ */ public class RunCommandService extends Service { - public static final String RUN_COMMAND_ACTION = "com.termux.RUN_COMMAND"; - public static final String RUN_COMMAND_PATH = "com.termux.RUN_COMMAND_PATH"; - public static final String RUN_COMMAND_ARGUMENTS = "com.termux.RUN_COMMAND_ARGUMENTS"; - public static final String RUN_COMMAND_WORKDIR = "com.termux.RUN_COMMAND_WORKDIR"; - public static final String RUN_COMMAND_BACKGROUND = "com.termux.RUN_COMMAND_BACKGROUND"; - private static final String NOTIFICATION_CHANNEL_ID = "termux_run_command_notification_channel"; private static final int NOTIFICATION_ID = 1338; @@ -108,7 +104,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { runStartForeground(); // If wrong action passed, then just return - if (!RUN_COMMAND_ACTION.equals(intent.getAction())) { + if (!RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND.equals(intent.getAction())) { Log.e("termux", "Unexpected intent action to RunCommandService: " + intent.getAction()); return Service.START_NOT_STICKY; } @@ -119,16 +115,16 @@ public int onStartCommand(Intent intent, int flags, int startId) { return Service.START_NOT_STICKY; } - Uri programUri = new Uri.Builder().scheme("com.termux.file").path(getExpandedTermuxPath(intent.getStringExtra(RUN_COMMAND_PATH))).build(); + Uri programUri = new Uri.Builder().scheme(TERMUX_SERVICE.URI_SCHEME_SERVICE_EXECUTE).path(getExpandedTermuxPath(intent.getStringExtra(RUN_COMMAND_SERVICE.EXTRA_COMMAND_PATH))).build(); - Intent execIntent = new Intent(TermuxService.ACTION_EXECUTE, programUri); + Intent execIntent = new Intent(TERMUX_SERVICE.ACTION_SERVICE_EXECUTE, programUri); execIntent.setClass(this, TermuxService.class); - execIntent.putExtra(TermuxService.EXTRA_ARGUMENTS, intent.getStringArrayExtra(RUN_COMMAND_ARGUMENTS)); - execIntent.putExtra(TermuxService.EXTRA_EXECUTE_IN_BACKGROUND, intent.getBooleanExtra(RUN_COMMAND_BACKGROUND, false)); + execIntent.putExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS, intent.getStringArrayExtra(RUN_COMMAND_SERVICE.EXTRA_ARGUMENTS)); + execIntent.putExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, intent.getBooleanExtra(RUN_COMMAND_SERVICE.EXTRA_BACKGROUND, false)); - String workingDirectory = intent.getStringExtra(RUN_COMMAND_WORKDIR); + String workingDirectory = intent.getStringExtra(RUN_COMMAND_SERVICE.EXTRA_WORKDIR); if (workingDirectory != null && !workingDirectory.isEmpty()) { - execIntent.putExtra(TermuxService.EXTRA_CURRENT_WORKING_DIRECTORY, getExpandedTermuxPath(workingDirectory)); + execIntent.putExtra(TERMUX_SERVICE.EXTRA_WORKDIR, getExpandedTermuxPath(workingDirectory)); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -188,9 +184,9 @@ private void setupNotificationChannel() { } private boolean allowExternalApps() { - File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties"); + File propsFile = new File(TermuxConstants.TERMUX_PROPERTIES_PRIMARY_PATH); if (!propsFile.exists()) - propsFile = new File(TermuxService.HOME_PATH + "/.config/termux/termux.properties"); + propsFile = new File(TermuxConstants.TERMUX_PROPERTIES_SECONDARY_PATH); Properties props = new Properties(); try { @@ -209,10 +205,10 @@ private boolean allowExternalApps() { /** Replace "$PREFIX/" or "~/" prefix with termux absolute paths */ public static String getExpandedTermuxPath(String path) { if(path != null && !path.isEmpty()) { - path = path.replaceAll("^\\$PREFIX$", TermuxService.PREFIX_PATH); - path = path.replaceAll("^\\$PREFIX/", TermuxService.PREFIX_PATH + "/"); - path = path.replaceAll("^~/$", TermuxService.HOME_PATH); - path = path.replaceAll("^~/", TermuxService.HOME_PATH + "/"); + path = path.replaceAll("^\\$PREFIX$", TermuxConstants.PREFIX_PATH); + path = path.replaceAll("^\\$PREFIX/", TermuxConstants.PREFIX_PATH + "/"); + path = path.replaceAll("^~/$", TermuxConstants.HOME_PATH); + path = path.replaceAll("^~/", TermuxConstants.HOME_PATH + "/"); } return path; diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java index b5d876815e..a6e1d985ea 100644 --- a/app/src/main/java/com/termux/app/TermuxActivity.java +++ b/app/src/main/java/com/termux/app/TermuxActivity.java @@ -2,7 +2,6 @@ import android.Manifest; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; @@ -48,6 +47,7 @@ import android.widget.Toast; import com.termux.R; +import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY; import com.termux.terminal.EmulatorDebug; import com.termux.terminal.TerminalColors; import com.termux.terminal.TerminalSession; @@ -84,8 +84,6 @@ */ public final class TermuxActivity extends Activity implements ServiceConnection { - public static final String TERMUX_FAILSAFE_SESSION_ACTION = "com.termux.app.failsafe_session"; - private static final int CONTEXTMENU_SELECT_URL_ID = 0; private static final int CONTEXTMENU_SHARE_TRANSCRIPT_ID = 1; private static final int CONTEXTMENU_PASTE_ID = 3; @@ -100,9 +98,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection private static final int REQUESTCODE_PERMISSION_STORAGE = 1234; - private static final String RELOAD_STYLE_ACTION = "com.termux.app.reload_style"; - private static final String BROADCAST_TERMUX_OPENED = "com.termux.app.OPENED"; + private static final String BROADCAST_TERMUX_OPENED = TermuxConstants.TERMUX_PACKAGE_NAME + ".app.OPENED"; /** The main view of the activity showing the terminal. Initialized in onCreate(). */ @SuppressWarnings("NullableProblems") @@ -145,7 +142,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection @Override public void onReceive(Context context, Intent intent) { if (mIsVisible) { - String whatToReload = intent.getStringExtra(RELOAD_STYLE_ACTION); + String whatToReload = intent.getStringExtra(TERMUX_ACTIVITY.EXTRA_RELOAD_STYLE); if ("storage".equals(whatToReload)) { if (ensureStoragePermissionGranted()) TermuxInstaller.setupStorageSymlinks(TermuxActivity.this); @@ -163,8 +160,8 @@ public void onReceive(Context context, Intent intent) { void checkForFontAndColors() { try { - @SuppressLint("SdCardPath") File fontFile = new File("/data/data/com.termux/files/home/.termux/font.ttf"); - @SuppressLint("SdCardPath") File colorsFile = new File("/data/data/com.termux/files/home/.termux/colors.properties"); + File colorsFile = new File(TermuxConstants.COLOR_PROPERTIES_PATH); + File fontFile = new File(TermuxConstants.FONT_PATH); final Properties props = new Properties(); if (colorsFile.isFile()) { @@ -541,7 +538,7 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { Bundle bundle = getIntent().getExtras(); boolean launchFailsafe = false; if (bundle != null) { - launchFailsafe = bundle.getBoolean(TERMUX_FAILSAFE_SESSION_ACTION, false); + launchFailsafe = bundle.getBoolean(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); } addNewSession(launchFailsafe, null); } catch (WindowManager.BadTokenException e) { @@ -556,7 +553,7 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { Intent i = getIntent(); if (i != null && Intent.ACTION_RUN.equals(i.getAction())) { // Android 7.1 app shortcut from res/xml/shortcuts.xml. - boolean failSafe = i.getBooleanExtra(TERMUX_FAILSAFE_SESSION_ACTION, false); + boolean failSafe = i.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); addNewSession(failSafe, null); } else { switchToSession(getStoredCurrentSessionOrLast()); @@ -612,7 +609,7 @@ public void onStart() { mListViewAdapter.notifyDataSetChanged(); } - registerReceiver(mBroadcastReceiever, new IntentFilter(RELOAD_STYLE_ACTION)); + registerReceiver(mBroadcastReceiever, new IntentFilter(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE)); // The current terminal session may have changed while being away, force // a refresh of the displayed terminal: @@ -914,14 +911,14 @@ public boolean onContextItemSelected(MenuItem item) { } case CONTEXTMENU_STYLING_ID: { Intent stylingIntent = new Intent(); - stylingIntent.setClassName("com.termux.styling", "com.termux.styling.TermuxStyleActivity"); + stylingIntent.setClassName(TermuxConstants.TERMUX_STYLING_PACKAGE_NAME, TermuxConstants.TERMUX_STYLING.TERMUX_STYLING_ACTIVITY_NAME); try { startActivity(stylingIntent); } catch (ActivityNotFoundException | IllegalArgumentException e) { // The startActivity() call is not documented to throw IllegalArgumentException. // However, crash reporting shows that it sometimes does, so catch it here. - new AlertDialog.Builder(this).setMessage(R.string.styling_not_installed) - .setPositiveButton(R.string.styling_install, (dialog, which) -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://f-droid.org/en/packages/com.termux.styling/")))).setNegativeButton(android.R.string.cancel, null).show(); + new AlertDialog.Builder(this).setMessage(getString(R.string.styling_not_installed)) + .setPositiveButton(R.string.styling_install, (dialog, which) -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://f-droid.org/en/packages/" + TermuxConstants.TERMUX_STYLING_PACKAGE_NAME + " /")))).setNegativeButton(android.R.string.cancel, null).show(); } return true; } diff --git a/app/src/main/java/com/termux/app/TermuxConstants.java b/app/src/main/java/com/termux/app/TermuxConstants.java new file mode 100644 index 0000000000..cf4da908d1 --- /dev/null +++ b/app/src/main/java/com/termux/app/TermuxConstants.java @@ -0,0 +1,232 @@ +package com.termux.app; + +import android.annotation.SuppressLint; + +import java.io.File; + +// Version: v0.1.0 + +/** + * A class that defines shared constants of the Termux app and its plugins. + * This class will be hosted by termux-app and should be imported by other termux plugin apps as is + * instead of copying constants to random classes. The 3rd party apps can also import it for + * interacting with termux apps. + * + * Termux app default package name is "com.termux" and is used in PREFIX_PATH. + * The binaries compiled for termux have PREFIX_PATH hardcoded in them but it can be changed during + * compilation. + * + * The TERMUX_PACKAGE_NAME must be the same as the applicationId of termux-app build.gradle since + * its also used by FILES_PATH. + * If TERMUX_PACKAGE_NAME is changed, then binaries, specially used in bootstrap need to be compiled + * appropriately. Check https://github.com/termux/termux-packages/wiki/Building-packages for more info. + * + * Ideally the only places where changes should be required if changing package name are the following: + * - The TERMUX_PACKAGE_NAME in TermuxConstants. + * - The "applicationId" in "build.gradle". This is package name that android and app stores will + * use and is also the final package name stored in "AndroidManifest.xml". + * - The "manifestPlaceholders" values for TERMUX_PACKAGE_NAME and *_APP_NAME in "build.gradle". + * - The "ENTITY" values for TERMUX_PACKAGE_NAME and *_APP_NAME in "strings.xml". + * - The "shortcut.xml" files like in termux-app since dynamic variables don't work in it. + * - Optionally the "package" in "AndroidManifest.xml" if modifying project structure. This is + * package name for java classes project structure and is prefixed if activity and service + * names use dot (.) notation. + * - Optionally the *_PATH variables in TermuxConstants containing the string "termux". + * + * Check https://developer.android.com/studio/build/application-id for info on "package" in + * "AndroidManifest.xml" and "applicationId" in "build.gradle". + * + * TERMUX_PACKAGE_NAME must be used in source code of Termux app and its plugins instead of hardcoded + * "com.termux" paths. + */ + +public final class TermuxConstants { + + /** + * Termux app and plugin app and package names. + */ + + public static final String TERMUX_APP_NAME = "Termux"; // Default: "Termux" + public static final String TERMUX_PACKAGE_NAME = "com.termux"; // Default: "com.termux" + + public static final String TERMUX_API_APP_NAME = "Termux:API"; // Default: "Termux:API" + public static final String TERMUX_API_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".api"; // Default: "com.termux.api" + + public static final String TERMUX_BOOT_APP_NAME = "Termux:Boot"; // Default: "Termux:Boot" + public static final String TERMUX_BOOT_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".boot"; // Default: "com.termux.boot" + + public static final String TERMUX_FLOAT_APP_NAME = "Termux:Float"; // Default: "Termux:Float" + public static final String TERMUX_FLOAT_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".window"; // Default: "com.termux.window" + + public static final String TERMUX_STYLING_APP_NAME = "Termux:Styling"; // Default: "Termux:Styling" + public static final String TERMUX_STYLING_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".styling"; // Default: "com.termux.styling" + + public static final String TERMUX_TASKER_APP_NAME = "Termux:Tasker"; // Default: "Termux:Tasker" + public static final String TERMUX_TASKER_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".tasker"; // Default: "com.termux.tasker" + + public static final String TERMUX_WIDGET_APP_NAME = "Termux:Widget"; // Default: "Termux:Widget" + public static final String TERMUX_WIDGET_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".widget"; // Default: "com.termux.widget" + + + + /** + * Termux app core paths. + */ + + @SuppressLint("SdCardPath") + public static final String FILES_PATH = "/data/data/" + TERMUX_PACKAGE_NAME + "/files"; // Default: "/data/data/com.termux/files" + public static final String PREFIX_PATH = FILES_PATH + "/usr"; // Termux $PREFIX path. Default: "/data/data/com.termux/files/usr" + public static final String HOME_PATH = FILES_PATH + "/home"; // Termux $HOME path. Default: "/data/data/com.termux/files/home" + public static final String DATA_HOME_PATH = HOME_PATH + "/.termux"; // Default: "/data/data/com.termux/files/home/.termux" + public static final String CONFIG_HOME_PATH = HOME_PATH + "/.config/termux"; // Default: "/data/data/com.termux/files/home/.config/termux" + + + + /** + * Termux app plugin specific paths. + */ + + // Path to store scripts to be run at boot by Termux:Boot + public static final String BOOT_SCRIPTS_PATH = DATA_HOME_PATH + "/boot"; // Default: "/data/data/com.termux/files/home/.termux/boot" + + // Path to store foreground scripts that can be run by the termux launcher widget provided by Termux:Widget + public static final String SHORTCUT_SCRIPTS_PATH = DATA_HOME_PATH + "/shortcuts"; // Default: "/data/data/com.termux/files/home/.termux/shortcuts" + + // Path to store background scripts that can be run by the termux launcher widget provided by Termux:Widget + public static final String SHORTCUT_TASKS_SCRIPTS_PATH = DATA_HOME_PATH + "/shortcuts/tasks"; // Default: "/data/data/com.termux/files/home/.termux/shortcuts/tasks" + + // Path to store scripts to be run by 3rd party twofortyfouram locale plugin host apps like Tasker app via the Termux:Tasker plugin client + public static final String TASKER_SCRIPTS_PATH = DATA_HOME_PATH + "/tasker"; // Default: "/data/data/com.termux/files/home/.termux/tasker" + + // Termux app termux.properties primary path + public static final String TERMUX_PROPERTIES_PRIMARY_PATH = DATA_HOME_PATH + "/termux.properties"; // Default: "/data/data/com.termux/files/home/.termux/termux.properties" + + // Termux app termux.properties secondary path + public static final String TERMUX_PROPERTIES_SECONDARY_PATH = CONFIG_HOME_PATH + "/termux.properties"; // Default: "/data/data/com.termux/files/home/.config/termux/termux.properties" + + // Termux app and Termux:Styling colors.properties path + public static final String COLOR_PROPERTIES_PATH = DATA_HOME_PATH + "/colors.properties"; // Default: "/data/data/com.termux/files/home/.termux/colors.properties" + + // Termux app and Termux:Styling font.ttf path + public static final String FONT_PATH = DATA_HOME_PATH + "/font.ttf"; // Default: "/data/data/com.termux/files/home/.termux/font.ttf" + + + + /** + * Termux app plugin specific path File objects. + */ + + public static final File FILES_DIR = new File(FILES_PATH); + public static final File PREFIX_DIR = new File(PREFIX_PATH); + public static final File HOME_DIR = new File(HOME_PATH); + public static final File DATA_HOME_DIR = new File(DATA_HOME_PATH); + public static final File CONFIG_HOME_DIR = new File(CONFIG_HOME_PATH); + public static final File BOOT_SCRIPTS_DIR = new File(BOOT_SCRIPTS_PATH); + public static final File SHORTCUT_SCRIPTS_DIR = new File(SHORTCUT_SCRIPTS_PATH); + public static final File SHORTCUT_TASKS_SCRIPTS_DIR = new File(SHORTCUT_TASKS_SCRIPTS_PATH); + public static final File TASKER_SCRIPTS_DIR = new File(TASKER_SCRIPTS_PATH); + + + + // Android OS permission declared by Termux app in AndroidManifest.xml which can be requested by 3rd party apps to run various commands in Termux app context + public static final String PERMISSION_RUN_COMMAND = TERMUX_PACKAGE_NAME + ".permission.RUN_COMMAND"; // Default: "com.termux.permission.RUN_COMMAND" + + // Termux property defined in termux.properties file as a secondary check to PERMISSION_RUN_COMMAND to allow 3rd party apps to run various commands in Termux app context + public static final String PROP_ALLOW_EXTERNAL_APPS = "allow-external-apps"; // Default: "allow-external-apps" + public static final String PROP_DEFAULT_VALUE_ALLOW_EXTERNAL_APPS = "false"; // Default: "false" + + + /** + * Termux app constants. + */ + + public static final class TERMUX_APP { + + /** + * Termux app core activity. + */ + + public static final String TERMUX_ACTIVITY_NAME = TERMUX_PACKAGE_NAME + ".app.TermuxActivity"; // Default: "com.termux.app.TermuxActivity" + public static final class TERMUX_ACTIVITY { + + // Intent action to start termux failsafe session + public static final String ACTION_FAILSAFE_SESSION = TermuxConstants.TERMUX_PACKAGE_NAME + ".app.failsafe_session"; // Default: "com.termux.app.failsafe_session" + + // Intent action to make termux reload its termux session styling + public static final String ACTION_RELOAD_STYLE = TermuxConstants.TERMUX_PACKAGE_NAME + ".app.reload_style"; // Default: "com.termux.app.reload_style" + // Intent extra for what to reload for the TERMUX_ACTIVITY.ACTION_RELOAD_STYLE intent + public static final String EXTRA_RELOAD_STYLE = TermuxConstants.TERMUX_PACKAGE_NAME + ".app.reload_style"; // Default: "com.termux.app.reload_style" + + } + + + + /** + * Termux app core service. + */ + + public static final String TERMUX_SERVICE_NAME = TERMUX_PACKAGE_NAME + ".app.TermuxService"; // Default: "com.termux.app.TermuxService" + public static final class TERMUX_SERVICE { + + // Intent action to stop TERMUX_SERVICE + public static final String ACTION_STOP_SERVICE = TERMUX_PACKAGE_NAME + ".service_stop"; // Default: "com.termux.service_stop" + + // Intent action to make TERMUX_SERVICE acquire a wakelock + public static final String ACTION_WAKE_LOCK = TERMUX_PACKAGE_NAME + ".service_wake_lock"; // Default: "com.termux.service_wake_lock" + + // Intent action to make TERMUX_SERVICE release wakelock + public static final String ACTION_WAKE_UNLOCK = TERMUX_PACKAGE_NAME + ".service_wake_unlock"; // Default: "com.termux.service_wake_unlock" + + // Intent action to execute command with TERMUX_SERVICE + public static final String ACTION_SERVICE_EXECUTE = TERMUX_PACKAGE_NAME + ".service_execute"; // Default: "com.termux.service_execute" + // Uri scheme for paths sent via intent to TERMUX_SERVICE + public static final String URI_SCHEME_SERVICE_EXECUTE = TERMUX_PACKAGE_NAME + ".file"; // Default: "com.termux.file" + // Intent extra for command arguments for the TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent + public static final String EXTRA_ARGUMENTS = TERMUX_PACKAGE_NAME + ".execute.arguments"; // Default: "com.termux.execute.arguments" + // Intent extra for command current working directory for the TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent + public static final String EXTRA_WORKDIR = TERMUX_PACKAGE_NAME + ".execute.cwd"; // Default: "com.termux.execute.cwd" + // Intent extra for command background mode for the TERMUX_SERVICE.ACTION_SERVICE_EXECUTE intent + public static final String EXTRA_BACKGROUND = TERMUX_PACKAGE_NAME + ".execute.background"; // Default: "com.termux.execute.background" + + } + + + + /** + * Termux app service to receive commands sent by 3rd party apps. + */ + + public static final String RUN_COMMAND_SERVICE_NAME = TERMUX_PACKAGE_NAME + ".app.RunCommandService"; // Termux app service to receive commands from 3rd party apps "com.termux.app.RunCommandService" + public static final class RUN_COMMAND_SERVICE { + + // Intent action to execute command with RUN_COMMAND_SERVICE + public static final String ACTION_RUN_COMMAND = TERMUX_PACKAGE_NAME + ".RUN_COMMAND"; // Default: "com.termux.RUN_COMMAND" + // Intent extra for command path for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent + public static final String EXTRA_COMMAND_PATH = TERMUX_PACKAGE_NAME + ".RUN_COMMAND_PATH"; // Default: "com.termux.RUN_COMMAND_PATH" + // Intent extra for command arguments for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent + public static final String EXTRA_ARGUMENTS = TERMUX_PACKAGE_NAME + ".RUN_COMMAND_ARGUMENTS"; // Default: "com.termux.RUN_COMMAND_ARGUMENTS" + // Intent extra for command current working directory for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent + public static final String EXTRA_WORKDIR = TERMUX_PACKAGE_NAME + ".RUN_COMMAND_WORKDIR"; // Default: "com.termux.RUN_COMMAND_WORKDIR" + // Intent extra for command background mode for the RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND intent + public static final String EXTRA_BACKGROUND = TERMUX_PACKAGE_NAME + ".RUN_COMMAND_BACKGROUND"; // Default: "com.termux.RUN_COMMAND_BACKGROUND" + + } + } + + + + /** + * Termux:Styling app constants. + */ + + public static final class TERMUX_STYLING { + + /** + * Termux:Styling app core activity constants. + */ + + public static final String TERMUX_STYLING_ACTIVITY_NAME = TERMUX_STYLING_PACKAGE_NAME + ".TermuxStyleActivity"; // Default: "com.termux.styling.TermuxStyleActivity" + + } + +} diff --git a/app/src/main/java/com/termux/app/TermuxInstaller.java b/app/src/main/java/com/termux/app/TermuxInstaller.java index 6e50b22dd6..390eff1208 100644 --- a/app/src/main/java/com/termux/app/TermuxInstaller.java +++ b/app/src/main/java/com/termux/app/TermuxInstaller.java @@ -58,7 +58,7 @@ static void setupIfNeeded(final Activity activity, final Runnable whenDone) { return; } - final File PREFIX_FILE = new File(TermuxService.PREFIX_PATH); + final File PREFIX_FILE = new File(TermuxConstants.PREFIX_PATH); if (PREFIX_FILE.isDirectory()) { whenDone.run(); return; @@ -69,7 +69,7 @@ static void setupIfNeeded(final Activity activity, final Runnable whenDone) { @Override public void run() { try { - final String STAGING_PREFIX_PATH = TermuxService.FILES_PATH + "/usr-staging"; + final String STAGING_PREFIX_PATH = TermuxConstants.FILES_PATH + "/usr-staging"; final File STAGING_PREFIX_FILE = new File(STAGING_PREFIX_PATH); if (STAGING_PREFIX_FILE.exists()) { @@ -194,7 +194,7 @@ static void setupStorageSymlinks(final Context context) { new Thread() { public void run() { try { - File storageDir = new File(TermuxService.HOME_PATH, "storage"); + File storageDir = new File(TermuxConstants.HOME_PATH, "storage"); if (storageDir.exists()) { try { diff --git a/app/src/main/java/com/termux/app/TermuxOpenReceiver.java b/app/src/main/java/com/termux/app/TermuxOpenReceiver.java index 6b8bf227b6..26f5fec98b 100644 --- a/app/src/main/java/com/termux/app/TermuxOpenReceiver.java +++ b/app/src/main/java/com/termux/app/TermuxOpenReceiver.java @@ -178,7 +178,7 @@ public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) thr String path = file.getCanonicalPath(); String storagePath = Environment.getExternalStorageDirectory().getCanonicalPath(); // See https://support.google.com/faqs/answer/7496913: - if (!(path.startsWith(TermuxService.FILES_PATH) || path.startsWith(storagePath))) { + if (!(path.startsWith(TermuxConstants.FILES_PATH) || path.startsWith(storagePath))) { throw new IllegalArgumentException("Invalid path: " + path); } } catch (IOException e) { diff --git a/app/src/main/java/com/termux/app/TermuxPreferences.java b/app/src/main/java/com/termux/app/TermuxPreferences.java index d3df7fc335..f4187eef79 100644 --- a/app/src/main/java/com/termux/app/TermuxPreferences.java +++ b/app/src/main/java/com/termux/app/TermuxPreferences.java @@ -170,9 +170,9 @@ static TerminalSession getCurrentSession(TermuxActivity context) { } void reloadFromProperties(Context context) { - File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties"); + File propsFile = new File(TermuxConstants.TERMUX_PROPERTIES_PRIMARY_PATH); if (!propsFile.exists()) - propsFile = new File(TermuxService.HOME_PATH + "/.config/termux/termux.properties"); + propsFile = new File(TermuxConstants.TERMUX_PROPERTIES_SECONDARY_PATH); Properties props = new Properties(); try { @@ -213,12 +213,12 @@ void reloadFromProperties(Context context) { mUseFullScreen = "true".equals(props.getProperty("fullscreen", "false").toLowerCase()); mUseFullScreenWorkAround = "true".equals(props.getProperty("use-fullscreen-workaround", "false").toLowerCase()); - mDefaultWorkingDir = props.getProperty("default-working-directory", TermuxService.HOME_PATH); + mDefaultWorkingDir = props.getProperty("default-working-directory", TermuxConstants.HOME_PATH); File workDir = new File(mDefaultWorkingDir); if (!workDir.exists() || !workDir.isDirectory()) { // Fallback to home directory if user configured working directory is not exist // or is a regular file. - mDefaultWorkingDir = TermuxService.HOME_PATH; + mDefaultWorkingDir = TermuxConstants.HOME_PATH; } String defaultExtraKeys = "[[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]"; diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java index 3cb714fa18..854659dca4 100644 --- a/app/src/main/java/com/termux/app/TermuxService.java +++ b/app/src/main/java/com/termux/app/TermuxService.java @@ -22,6 +22,8 @@ import android.widget.ArrayAdapter; import com.termux.R; +import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY; +import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE; import com.termux.terminal.EmulatorDebug; import com.termux.terminal.TerminalSession; import com.termux.terminal.TerminalSession.SessionChangedCallback; @@ -45,26 +47,8 @@ public final class TermuxService extends Service implements SessionChangedCallback { private static final String NOTIFICATION_CHANNEL_ID = "termux_notification_channel"; - - /** Note that this is a symlink on the Android M preview. */ - @SuppressLint("SdCardPath") - public static final String FILES_PATH = "/data/data/com.termux/files"; - public static final String PREFIX_PATH = FILES_PATH + "/usr"; - public static final String HOME_PATH = FILES_PATH + "/home"; - private static final int NOTIFICATION_ID = 1337; - private static final String ACTION_STOP_SERVICE = "com.termux.service_stop"; - private static final String ACTION_LOCK_WAKE = "com.termux.service_wake_lock"; - private static final String ACTION_UNLOCK_WAKE = "com.termux.service_wake_unlock"; - /** Intent action to launch a new terminal session. Executed from TermuxWidgetProvider. */ - public static final String ACTION_EXECUTE = "com.termux.service_execute"; - - public static final String EXTRA_ARGUMENTS = "com.termux.execute.arguments"; - - public static final String EXTRA_CURRENT_WORKING_DIRECTORY = "com.termux.execute.cwd"; - public static final String EXTRA_EXECUTE_IN_BACKGROUND = "com.termux.execute.background"; - /** This service is only bound from inside the same process and never uses IPC. */ class LocalBinder extends Binder { public final TermuxService service = TermuxService.this; @@ -91,19 +75,19 @@ class LocalBinder extends Binder { private PowerManager.WakeLock mWakeLock; private WifiManager.WifiLock mWifiLock; - /** If the user has executed the {@link #ACTION_STOP_SERVICE} intent. */ + /** If the user has executed the {@link TermuxConstants.TERMUX_APP.TERMUX_SERVICE#ACTION_STOP_SERVICE} intent. */ boolean mWantsToStop = false; @SuppressLint("Wakelock") @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent.getAction(); - if (ACTION_STOP_SERVICE.equals(action)) { + if (TERMUX_SERVICE.ACTION_STOP_SERVICE.equals(action)) { mWantsToStop = true; for (int i = 0; i < mTerminalSessions.size(); i++) mTerminalSessions.get(i).finishIfRunning(); stopSelf(); - } else if (ACTION_LOCK_WAKE.equals(action)) { + } else if (TERMUX_SERVICE.ACTION_WAKE_LOCK.equals(action)) { if (mWakeLock == null) { PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, EmulatorDebug.LOG_TAG + ":service-wakelock"); @@ -130,7 +114,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { updateNotification(); } - } else if (ACTION_UNLOCK_WAKE.equals(action)) { + } else if (TERMUX_SERVICE.ACTION_WAKE_UNLOCK.equals(action)) { if (mWakeLock != null) { mWakeLock.release(); mWakeLock = null; @@ -140,19 +124,19 @@ public int onStartCommand(Intent intent, int flags, int startId) { updateNotification(); } - } else if (ACTION_EXECUTE.equals(action)) { + } else if (TERMUX_SERVICE.ACTION_SERVICE_EXECUTE.equals(action)) { Uri executableUri = intent.getData(); String executablePath = (executableUri == null ? null : executableUri.getPath()); - String[] arguments = (executableUri == null ? null : intent.getStringArrayExtra(EXTRA_ARGUMENTS)); - String cwd = intent.getStringExtra(EXTRA_CURRENT_WORKING_DIRECTORY); + String[] arguments = (executableUri == null ? null : intent.getStringArrayExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS)); + String cwd = intent.getStringExtra(TERMUX_SERVICE.EXTRA_WORKDIR); - if (intent.getBooleanExtra(EXTRA_EXECUTE_IN_BACKGROUND, false)) { + if (intent.getBooleanExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, false)) { BackgroundJob task = new BackgroundJob(cwd, executablePath, arguments, this, intent.getParcelableExtra("pendingIntent")); mBackgroundTasks.add(task); updateNotification(); } else { - boolean failsafe = intent.getBooleanExtra(TermuxActivity.TERMUX_FAILSAFE_SESSION_ACTION, false); + boolean failsafe = intent.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); TerminalSession newSession = createTermSession(executablePath, arguments, cwd, failsafe); // Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh". @@ -238,10 +222,10 @@ private Notification buildNotification() { } Resources res = getResources(); - Intent exitIntent = new Intent(this, TermuxService.class).setAction(ACTION_STOP_SERVICE); + Intent exitIntent = new Intent(this, TermuxService.class).setAction(TERMUX_SERVICE.ACTION_STOP_SERVICE); builder.addAction(android.R.drawable.ic_delete, res.getString(R.string.notification_action_exit), PendingIntent.getService(this, 0, exitIntent, 0)); - String newWakeAction = wakeLockHeld ? ACTION_UNLOCK_WAKE : ACTION_LOCK_WAKE; + String newWakeAction = wakeLockHeld ? TERMUX_SERVICE.ACTION_WAKE_UNLOCK : TERMUX_SERVICE.ACTION_WAKE_LOCK; Intent toggleWakeLockIntent = new Intent(this, TermuxService.class).setAction(newWakeAction); String actionTitle = res.getString(wakeLockHeld ? R.string.notification_action_wake_unlock : @@ -254,7 +238,7 @@ private Notification buildNotification() { @Override public void onDestroy() { - File termuxTmpDir = new File(TermuxService.PREFIX_PATH + "/tmp"); + File termuxTmpDir = new File(TermuxConstants.PREFIX_PATH + "/tmp"); if (termuxTmpDir.exists()) { try { @@ -280,9 +264,9 @@ public List getSessions() { } TerminalSession createTermSession(String executablePath, String[] arguments, String cwd, boolean failSafe) { - new File(HOME_PATH).mkdirs(); + new File(TermuxConstants.HOME_PATH).mkdirs(); - if (cwd == null || cwd.isEmpty()) cwd = HOME_PATH; + if (cwd == null || cwd.isEmpty()) cwd = TermuxConstants.HOME_PATH; String[] env = BackgroundJob.buildEnvironment(failSafe, cwd); boolean isLoginShell = false; @@ -290,7 +274,7 @@ TerminalSession createTermSession(String executablePath, String[] arguments, Str if (executablePath == null) { if (!failSafe) { for (String shellBinary : new String[]{"login", "bash", "zsh"}) { - File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary); + File shellFile = new File(TermuxConstants.PREFIX_PATH + "/bin/" + shellBinary); if (shellFile.canExecute()) { executablePath = shellFile.getAbsolutePath(); break; @@ -320,8 +304,8 @@ TerminalSession createTermSession(String executablePath, String[] arguments, Str updateNotification(); // Make sure that terminal styling is always applied. - Intent stylingIntent = new Intent("com.termux.app.reload_style"); - stylingIntent.putExtra("com.termux.app.reload_style", "styling"); + Intent stylingIntent = new Intent(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE); + stylingIntent.putExtra(TERMUX_ACTIVITY.EXTRA_RELOAD_STYLE, "styling"); sendBroadcast(stylingIntent); return session; diff --git a/app/src/main/java/com/termux/filepicker/TermuxDocumentsProvider.java b/app/src/main/java/com/termux/filepicker/TermuxDocumentsProvider.java index b72442ac27..1a314e8ced 100644 --- a/app/src/main/java/com/termux/filepicker/TermuxDocumentsProvider.java +++ b/app/src/main/java/com/termux/filepicker/TermuxDocumentsProvider.java @@ -12,7 +12,7 @@ import android.webkit.MimeTypeMap; import com.termux.R; -import com.termux.app.TermuxService; +import com.termux.app.TermuxConstants; import java.io.File; import java.io.FileNotFoundException; @@ -35,7 +35,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider { private static final String ALL_MIME_TYPES = "*/*"; - private static final File BASE_DIR = new File(TermuxService.HOME_PATH); + private static final File BASE_DIR = new File(TermuxConstants.HOME_PATH); // The default columns to return information about a root if no specific @@ -171,7 +171,7 @@ public Cursor querySearchDocuments(String rootId, String query, String[] project // through the whole SD card). boolean isInsideHome; try { - isInsideHome = file.getCanonicalPath().startsWith(TermuxService.HOME_PATH); + isInsideHome = file.getCanonicalPath().startsWith(TermuxConstants.HOME_PATH); } catch (IOException e) { isInsideHome = true; } diff --git a/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java b/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java index e1ef5d423f..c5493ae75f 100644 --- a/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java +++ b/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java @@ -11,6 +11,8 @@ import com.termux.R; import com.termux.app.DialogUtils; +import com.termux.app.TermuxConstants; +import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE; import com.termux.app.TermuxService; import java.io.ByteArrayInputStream; @@ -25,9 +27,9 @@ public class TermuxFileReceiverActivity extends Activity { - static final String TERMUX_RECEIVEDIR = TermuxService.FILES_PATH + "/home/downloads"; - static final String EDITOR_PROGRAM = TermuxService.HOME_PATH + "/bin/termux-file-editor"; - static final String URL_OPENER_PROGRAM = TermuxService.HOME_PATH + "/bin/termux-url-opener"; + static final String TERMUX_RECEIVEDIR = TermuxConstants.FILES_PATH + "/home/downloads"; + static final String EDITOR_PROGRAM = TermuxConstants.HOME_PATH + "/bin/termux-file-editor"; + static final String URL_OPENER_PROGRAM = TermuxConstants.HOME_PATH + "/bin/termux-url-opener"; /** * If the activity should be finished when the name input dialog is dismissed. This is disabled @@ -131,17 +133,17 @@ void promptNameAndSave(final InputStream in, final String attachmentFileName) { final Uri scriptUri = new Uri.Builder().scheme("file").path(EDITOR_PROGRAM).build(); - Intent executeIntent = new Intent(TermuxService.ACTION_EXECUTE, scriptUri); + Intent executeIntent = new Intent(TERMUX_SERVICE.ACTION_SERVICE_EXECUTE, scriptUri); executeIntent.setClass(TermuxFileReceiverActivity.this, TermuxService.class); - executeIntent.putExtra(TermuxService.EXTRA_ARGUMENTS, new String[]{outFile.getAbsolutePath()}); + executeIntent.putExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS, new String[]{outFile.getAbsolutePath()}); startService(executeIntent); finish(); }, R.string.file_received_open_folder_button, text -> { if (saveStreamWithName(in, text) == null) return; - Intent executeIntent = new Intent(TermuxService.ACTION_EXECUTE); - executeIntent.putExtra(TermuxService.EXTRA_CURRENT_WORKING_DIRECTORY, TERMUX_RECEIVEDIR); + Intent executeIntent = new Intent(TERMUX_SERVICE.ACTION_SERVICE_EXECUTE); + executeIntent.putExtra(TERMUX_SERVICE.EXTRA_WORKDIR, TERMUX_RECEIVEDIR); executeIntent.setClass(TermuxFileReceiverActivity.this, TermuxService.class); startService(executeIntent); finish(); @@ -188,9 +190,9 @@ void handleUrlAndFinish(final String url) { final Uri urlOpenerProgramUri = new Uri.Builder().scheme("file").path(URL_OPENER_PROGRAM).build(); - Intent executeIntent = new Intent(TermuxService.ACTION_EXECUTE, urlOpenerProgramUri); + Intent executeIntent = new Intent(TERMUX_SERVICE.ACTION_SERVICE_EXECUTE, urlOpenerProgramUri); executeIntent.setClass(TermuxFileReceiverActivity.this, TermuxService.class); - executeIntent.putExtra(TermuxService.EXTRA_ARGUMENTS, new String[]{url}); + executeIntent.putExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS, new String[]{url}); startService(executeIntent); finish(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 50e0283efe..a34c5d110d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,9 +1,21 @@ + + + + + + + + + + ]> + - Termux - Termux user - Run commands in Termux environment - execute arbitrary commands within Termux environment + &TERMUX_APP_NAME; + &TERMUX_APP_NAME; user + Run commands in &TERMUX_APP_NAME; environment + execute arbitrary commands within &TERMUX_APP_NAME; environment New session Failsafe Keyboard @@ -16,10 +28,10 @@ Installing… Unable to install - Termux was unable to install the bootstrap packages. + &TERMUX_APP_NAME; was unable to install the bootstrap packages. Abort Try again - Termux can only be installed on the primary user account. + &TERMUX_APP_NAME; can only be installed on the primary user account. Max terminals reached Close down existing ones before creating new. @@ -41,7 +53,7 @@ New named session Create - The Termux:Style add-on is not installed. + The &TERMUX_STYLING_APP_NAME; Plugin App is not installed. Install Exit diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml/shortcuts.xml index ae2e42f100..dfdab62a91 100644 --- a/app/src/main/res/xml/shortcuts.xml +++ b/app/src/main/res/xml/shortcuts.xml @@ -1,6 +1,14 @@ +