diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 93a20c0..c22d68a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -134,21 +134,11 @@ android:exported="false" android:label="@string/error" android:theme="@style/AppTheme.NoActionBar.Dark"/> - - static const char *rewrite_executable(const char *filename, char *buffer, int buffer_len) { - strcpy(buffer, "/data/data/io.neoterm/files/usr/bin/"); char *bin_match = strstr(filename, "/bin/"); if (bin_match == filename || bin_match == (filename + 4)) { // We have either found "/bin/" at the start of the string or at // "/xxx/bin/". Take the path after that. - strncpy(buffer + 36, bin_match + 5, (size_t) (buffer_len - 37)); + strncpy(buffer, bin_match + 5, (size_t) (buffer_len - 1)); filename = buffer; } return filename; diff --git a/app/src/main/cpp/neoterm.cpp b/app/src/main/cpp/neoterm.cpp index fc00e85..9a63af8 100755 --- a/app/src/main/cpp/neoterm.cpp +++ b/app/src/main/cpp/neoterm.cpp @@ -108,7 +108,7 @@ static int create_subprocess(JNIEnv *env, // Show terminal output about failing exec() call: char *error_message; if (asprintf(&error_message, "exec(\"%s\")", cmd) == -1) - error_message = const_cast("exec()");; + error_message = const_cast("exec()"); perror(error_message); _exit(1); } diff --git a/app/src/main/java/io/neoterm/component/comp.kt b/app/src/main/java/io/neoterm/component/comp.kt index be1541a..1ec3ea6 100644 --- a/app/src/main/java/io/neoterm/component/comp.kt +++ b/app/src/main/java/io/neoterm/component/comp.kt @@ -7,7 +7,6 @@ import io.neoterm.component.completion.CompletionComponent import io.neoterm.component.config.ConfigureComponent import io.neoterm.component.extrakey.ExtraKeyComponent import io.neoterm.component.font.FontComponent -import io.neoterm.component.pm.PackageComponent import io.neoterm.component.profile.ProfileComponent import io.neoterm.component.session.SessionComponent import io.neoterm.component.session.ShellProfile @@ -80,7 +79,6 @@ object NeoInitializer { ComponentManager.registerComponent(UserScriptComponent::class.java) ComponentManager.registerComponent(ExtraKeyComponent::class.java) ComponentManager.registerComponent(CompletionComponent::class.java) - ComponentManager.registerComponent(PackageComponent::class.java) ComponentManager.registerComponent(SessionComponent::class.java) ComponentManager.registerComponent(ProfileComponent::class.java) diff --git a/app/src/main/java/io/neoterm/component/config/comp.kt b/app/src/main/java/io/neoterm/component/config/comp.kt index af7c1d3..e732aad 100644 --- a/app/src/main/java/io/neoterm/component/config/comp.kt +++ b/app/src/main/java/io/neoterm/component/config/comp.kt @@ -84,18 +84,6 @@ object NeoPreference { val dipInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, context.resources.displayMetrics) MIN_FONT_SIZE = (4f * dipInPixels).toInt() MAX_FONT_SIZE = 256 - - // load apt source - val sourceFile = File(NeoTermPath.SOURCE_FILE) - kotlin.runCatching { - Files.readAllBytes(sourceFile.toPath())?.let { - val source = String(it).trim().trimEnd() - val array = source.split(" ") - if (array.size >= 2 && array[0] == "deb") { - store(R.string.key_package_source, array[1]) - } - } - } } fun store(key: Int, value: Any) { @@ -156,7 +144,6 @@ object NeoPreference { val loginProgramPath = findLoginProgram(loginProgramName) ?: return false store(R.string.key_general_shell, loginProgramName) - symlinkLoginShell(loginProgramPath) return true } @@ -167,39 +154,17 @@ object NeoPreference { fun getLoginShellPath(): String { val loginProgramName = getLoginShellName() - // Some programs like ssh needs it - val shell = File(NeoTermPath.NEOTERM_LOGIN_SHELL_PATH) val loginProgramPath = findLoginProgram(loginProgramName) ?: { setLoginShellName(DefaultValues.loginShell) - "${NeoTermPath.USR_PATH}/bin/${DefaultValues.loginShell}" + loadString(R.string.key_general_shell,DefaultValues.loginShell) }() - if (!shell.exists()) { - symlinkLoginShell(loginProgramPath) - } - return loginProgramPath } fun validateFontSize(fontSize: Int): Int { return Math.max(MIN_FONT_SIZE, Math.min(fontSize, MAX_FONT_SIZE)) } - - private fun symlinkLoginShell(loginProgramPath: String) { - File(NeoTermPath.CUSTOM_PATH).mkdirs() - try { - val shellSymlink = File(NeoTermPath.NEOTERM_LOGIN_SHELL_PATH) - if (shellSymlink.exists()) { - shellSymlink.delete() - } - Os.symlink(loginProgramPath, NeoTermPath.NEOTERM_LOGIN_SHELL_PATH) - Os.chmod(NeoTermPath.NEOTERM_LOGIN_SHELL_PATH, 448 /* Decimal of 0700 */) - } catch (e: ErrnoException) { - NLog.e("Preference", "Failed to symlink login shell: ${e.localizedMessage}") - e.printStackTrace() - } - } - fun findLoginProgram(loginProgramName: String): String? { val file = File("${NeoTermPath.USR_PATH}/bin", loginProgramName) return if (file.canExecute()) file.absolutePath else null diff --git a/app/src/main/java/io/neoterm/component/config/defaults.kt b/app/src/main/java/io/neoterm/component/config/defaults.kt index eaf2d34..82e8085 100644 --- a/app/src/main/java/io/neoterm/component/config/defaults.kt +++ b/app/src/main/java/io/neoterm/component/config/defaults.kt @@ -18,7 +18,7 @@ object DefaultValues { const val enableSpecialVolumeKeys = false const val enableWordBasedIme = false - const val loginShell = "bash" + const val loginShell = "sh" const val initialCommand = "" const val defaultFont = "SourceCodePro" } @@ -28,7 +28,6 @@ object NeoTermPath { const val ROOT_PATH = "/data/data/io.neoterm/files" const val USR_PATH = "$ROOT_PATH/usr" const val HOME_PATH = "$ROOT_PATH/home" - const val APT_BIN_PATH = "$USR_PATH/bin/apt" const val LIB_PATH = "$USR_PATH/lib" const val CUSTOM_PATH = "$HOME_PATH/.neoterm" @@ -40,14 +39,4 @@ object NeoTermPath { const val USER_SCRIPT_PATH = "$CUSTOM_PATH/script" const val PROFILE_PATH = "$CUSTOM_PATH/profile" - const val SOURCE_FILE = "$USR_PATH/etc/apt/sources.list" - const val PACKAGE_LIST_DIR = "$USR_PATH/var/lib/apt/lists" - - private const val SOURCE = "https://raw.githubusercontent.com/NeoTerm/NeoTerm-repo/main" - - val DEFAULT_MAIN_PACKAGE_SOURCE: String - - init { - DEFAULT_MAIN_PACKAGE_SOURCE = SOURCE - } } diff --git a/app/src/main/java/io/neoterm/component/pm/NeoPackageParser.java b/app/src/main/java/io/neoterm/component/pm/NeoPackageParser.java deleted file mode 100644 index 2c5d816..0000000 --- a/app/src/main/java/io/neoterm/component/pm/NeoPackageParser.java +++ /dev/null @@ -1,170 +0,0 @@ -package io.neoterm.component.pm; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -/** - * @author kiva - */ - -public class NeoPackageParser { - public interface ParseStateListener { - void onStartState(); - - void onEndState(); - - NeoPackageInfo onCreatePackageInfo(); - - void onStartParsePackage(String name, NeoPackageInfo packageInfo); - - void onEndParsePackage(NeoPackageInfo packageInfo); - } - - private static final String - KEY_PACKAGE_NAME = "Package", - KEY_VERSION = "Version", - KEY_ESSENTIAL = "Essential", - KEY_ARCH = "Architecture", - KEY_MAINTAINER = "Maintainer", - KEY_INSTALLED_SIZE = "Installed-Size", - KEY_DEPENDS = "Depends", - KEY_FILENAME = "Filename", - KEY_SIZE = "Size", - KEY_MD5 = "MD5sum", - KEY_SHA1 = "SHA1", - KEY_SHA256 = "SHA256", - KEY_HOMEPAGE = "Homepage", - KEY_DESC = "Description"; - - private BufferedReader reader; - private ParseStateListener stateListener; - - NeoPackageParser(InputStream inputStream) { - reader = new BufferedReader(new InputStreamReader(inputStream)); - } - - void setStateListener(ParseStateListener stateListener) { - this.stateListener = stateListener; - } - - public void parse() throws IOException { - if (stateListener == null) { - return; - } - - String line; - String[] splits = new String[2]; - String key = null; - String value = null; - boolean appendMode = false; - - NeoPackageInfo packageInfo = null; - - stateListener.onStartState(); - while ((line = reader.readLine()) != null) { - if (line.isEmpty()) { - continue; - } - - if (splitKeyAndValue(line, splits)) { - key = splits[0]; - value = splits[1]; - appendMode = false; - } else { - if (key == null) { - // no key provided, we don't know where the value should be appended to - continue; - } - // the rest value to previous key - value = line.trim(); - appendMode = true; - } - - if (key.equals(KEY_PACKAGE_NAME)) { - if (packageInfo != null) { - stateListener.onEndParsePackage(packageInfo); - } - packageInfo = stateListener.onCreatePackageInfo(); - packageInfo.setPackageName(value); - stateListener.onStartParsePackage(value, packageInfo); - } - - if (packageInfo == null) { - continue; - } - - if (appendMode) { - value = appendToLastValue(packageInfo, key, value); - } - - switch (key) { - case KEY_ARCH: - packageInfo.setArchitecture(Architecture.Companion.parse(value)); - break; - case KEY_DEPENDS: - packageInfo.setDependenciesString(value); - break; - case KEY_DESC: - packageInfo.setDescription(value); - break; - case KEY_ESSENTIAL: - packageInfo.setEssential(value.equals("yes")); - break; - case KEY_FILENAME: - packageInfo.setFileName(value); - break; - case KEY_HOMEPAGE: - packageInfo.setHomePage(value); - break; - case KEY_INSTALLED_SIZE: - packageInfo.setInstalledSizeInBytes(Long.parseLong(value)); - break; - case KEY_MAINTAINER: - packageInfo.setMaintainer(value); - break; - case KEY_MD5: - packageInfo.setMd5(value); - break; - case KEY_SHA1: - packageInfo.setSha1(value); - break; - case KEY_SHA256: - packageInfo.setSha256(value); - break; - case KEY_SIZE: - packageInfo.setSizeInBytes(Long.parseLong(value)); - break; - case KEY_VERSION: - packageInfo.setVersion(value); - break; - } - } - if (packageInfo != null) { - stateListener.onEndParsePackage(packageInfo); - } - stateListener.onEndState(); - } - - private String appendToLastValue(NeoPackageInfo packageInfo, String key, String value) { - // Currently, only descriptions can be multiline - switch (key) { - case KEY_DESC: - return packageInfo.getDescription() + " " + value; - default: - return value; - } - } - - private boolean splitKeyAndValue(String line, String[] splits) { - int valueIndex = line.indexOf(':'); - if (valueIndex < 0) { - return false; - } - - splits[0] = line.substring(0, valueIndex).trim(); - splits[1] = line.substring(valueIndex == line.length() ? valueIndex : valueIndex + 1).trim(); - return true; - } -} diff --git a/app/src/main/java/io/neoterm/component/pm/PackageComponent.java b/app/src/main/java/io/neoterm/component/pm/PackageComponent.java deleted file mode 100644 index 56099bd..0000000 --- a/app/src/main/java/io/neoterm/component/pm/PackageComponent.java +++ /dev/null @@ -1,120 +0,0 @@ -package io.neoterm.component.pm; - -import io.neoterm.component.NeoComponent; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.HashMap; - -/** - * @author kiva - */ - -public class PackageComponent implements NeoComponent { - private final Object lock = new Object(); - private boolean isRefreshing = false; - private boolean queryEnabled = true; - private HashMap neoPackages; - - private NeoPackageInfo getPackageInfo(String packageName) { - return queryEnabled ? neoPackages.get(packageName) : null; - } - - public HashMap getPackages() { - return queryEnabled ? neoPackages : new HashMap<>(); - } - - public int getPackageCount() { - return queryEnabled ? neoPackages.size() : -1; - } - - public SourceManager getSourceManager() { - return new SourceManager(); - } - - public void reloadPackages(File packageListFile, boolean clearPrevious) throws IOException { - synchronized (lock) { - if (isRefreshing) { - return; - } - isRefreshing = true; - } - tryParsePackages(packageListFile, clearPrevious); - synchronized (lock) { - isRefreshing = false; - } - } - - public void clearPackages() { - if (isRefreshing) { - return; - } - neoPackages.clear(); - } - - private void tryParsePackages(File packageListFile, final boolean clearPrevious) throws IOException { - NeoPackageParser packageParser = new NeoPackageParser(new FileInputStream(packageListFile)); - packageParser.setStateListener(new NeoPackageParser.ParseStateListener() { - @Override - public void onStartState() { - queryEnabled = false; - if (clearPrevious) { - neoPackages.clear(); - } - } - - @Override - public void onEndState() { - queryEnabled = true; - for (NeoPackageInfo info : neoPackages.values()) { - resolveDepends(info); - } - } - - @Override - public NeoPackageInfo onCreatePackageInfo() { - return new NeoPackageInfo(); - } - - @Override - public void onStartParsePackage(String name, NeoPackageInfo packageInfo) { - } - - @Override - public void onEndParsePackage(NeoPackageInfo packageInfo) { - neoPackages.put(packageInfo.getPackageName(), packageInfo); - } - }); - packageParser.parse(); - } - - private void resolveDepends(NeoPackageInfo info) { - String dep = info.getDependenciesString(); - if (dep == null) { - return; - } - - String[] splits = dep.split(","); - NeoPackageInfo[] depends = new NeoPackageInfo[splits.length]; - info.setDependencies(depends); - - for (int i = 0; i < splits.length; ++i) { - String item = splits[i].trim(); - depends[i] = getPackageInfo(item); - } - } - - @Override - public void onServiceInit() { - neoPackages = new HashMap<>(); - } - - @Override - public void onServiceDestroy() { - } - - @Override - public void onServiceObtained() { - } -} diff --git a/app/src/main/java/io/neoterm/component/pm/Source.java b/app/src/main/java/io/neoterm/component/pm/Source.java deleted file mode 100644 index e88f960..0000000 --- a/app/src/main/java/io/neoterm/component/pm/Source.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.neoterm.component.pm; - -import io.neoterm.framework.database.annotation.ID; -import io.neoterm.framework.database.annotation.Table; - -/** - * @author kiva - */ -@Table -public class Source { - @ID(autoIncrement = true) - private int id; - - public String url; - - public String repo; - - public boolean enabled; - - public Source() { - // for Database - } - - public Source(String url, String repo, boolean enabled) { - this.url = url; - this.repo = repo; - this.enabled = enabled; - } -} diff --git a/app/src/main/java/io/neoterm/component/pm/data.kt b/app/src/main/java/io/neoterm/component/pm/data.kt deleted file mode 100644 index c7029c8..0000000 --- a/app/src/main/java/io/neoterm/component/pm/data.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.neoterm.component.pm - -enum class Architecture { - ALL, ARM, AARCH64, X86, X86_64; - - companion object { - fun parse(arch: String): Architecture { - return when (arch) { - "arm" -> ARM - "aarch64" -> AARCH64 - "x86" -> X86 - "x86_64" -> X86_64 - else -> ALL - } - } - } -} - -class NeoPackageInfo { - var packageName: String? = null - var isEssential: Boolean = false - var version: String? = null - var architecture: Architecture = Architecture.ALL - var maintainer: String? = null - var installedSizeInBytes: Long = 0L - var fileName: String? = null - var dependenciesString: String? = null - var dependencies: Array? = null - var sizeInBytes: Long = 0L - var md5: String? = null - var sha1: String? = null - var sha256: String? = null - var homePage: String? = null - var description: String? = null -} diff --git a/app/src/main/java/io/neoterm/component/pm/helper.kt b/app/src/main/java/io/neoterm/component/pm/helper.kt deleted file mode 100644 index 2b7ed9a..0000000 --- a/app/src/main/java/io/neoterm/component/pm/helper.kt +++ /dev/null @@ -1,120 +0,0 @@ -package io.neoterm.component.pm - -import io.neoterm.App -import io.neoterm.R -import io.neoterm.component.ComponentManager -import io.neoterm.component.config.NeoTermPath -import io.neoterm.framework.NeoTermDatabase -import io.neoterm.utils.NLog -import java.io.File -import java.net.URL -import java.nio.file.Files -import java.nio.file.Paths - -object SourceHelper { - fun syncSource() { - val sourceManager = ComponentManager.getComponent().sourceManager - syncSource(sourceManager) - } - - fun syncSource(sourceManager: SourceManager) { - val content = buildString { - this.append("# Generated by NeoTerm-Preference\n") - sourceManager.getEnabledSources() - .joinTo(this, "\n") { "deb [trusted=yes] ${it.url} ${it.repo}\n" } - } - kotlin.runCatching { - Files.write(Paths.get(NeoTermPath.SOURCE_FILE), content.toByteArray()) - } - } - - fun detectSourceFiles(): List { - val sourceManager = ComponentManager.getComponent().sourceManager - val sourceFiles = ArrayList() - try { - val prefixes = sourceManager.getEnabledSources() - .map { detectSourceFilePrefix(it) } - .filter { it.isNotEmpty() } - - File(NeoTermPath.PACKAGE_LIST_DIR) - .listFiles() - .filterTo(sourceFiles) { file -> - prefixes.filter { file.name.startsWith(it) } - .count() > 0 - } - } catch (e: Exception) { - sourceFiles.clear() - NLog.e("PM", "Failed to detect source files: ${e.localizedMessage}") - } - - return sourceFiles - } - - fun detectSourceFilePrefix(source: Source): String { - try { - val url = URL(source.url) - val builder = StringBuilder(url.host) - if (url.port != -1) { - builder.append(":${url.port}") - } - - val path = url.path - if (path != null && path.isNotEmpty()) { - builder.append("_") - val fixedPath = path.replace("/", "_").substring(1) // skip the last '/' - builder.append(fixedPath) - } - builder.append("_dists_${source.repo.replace(" ".toRegex(), "_")}_binary-") - return builder.toString() - } catch (e: Exception) { - NLog.e("PM", "Failed to detect source file prefix: ${e.localizedMessage}") - return "" - } - } -} - -class SourceManager internal constructor() { - private val database = NeoTermDatabase.instance("sources") - - init { - if (database.findAll(Source::class.java).isEmpty()) { - App.get().resources.getStringArray(R.array.pref_package_source_values) - .forEach { - database.saveBean(Source(it, "stable main", true)) - } - } - } - - fun addSource(sourceUrl: String, repo: String, enabled: Boolean) { - database.saveBean(Source(sourceUrl, repo, enabled)) - } - - fun removeSource(sourceUrl: String) { - database.deleteBeanByWhere(Source::class.java, "url == '$sourceUrl'") - } - - fun updateAll(sources: List) { - database.dropAllTable() - database.saveBeans(sources) - } - - fun getAllSources(): List { - return database.findAll(Source::class.java) - } - - fun getEnabledSources(): List { - return getAllSources().filter { it.enabled } - } - - fun getMainPackageSource(): String { - return getEnabledSources() - .map { it.repo } - .singleOrNull { it.trim() == "stable main" } - ?: NeoTermPath.DEFAULT_MAIN_PACKAGE_SOURCE - } - - fun applyChanges() { - database.vacuum() - } -} - diff --git a/app/src/main/java/io/neoterm/component/session/comp.kt b/app/src/main/java/io/neoterm/component/session/comp.kt index 9b08c38..6b25f8c 100644 --- a/app/src/main/java/io/neoterm/component/session/comp.kt +++ b/app/src/main/java/io/neoterm/component/session/comp.kt @@ -94,7 +94,6 @@ class SessionComponent : NeoComponent { .executablePath(parameter.executablePath) .currentWorkingDirectory(parameter.cwd) .callback(parameter.sessionCallback) - .systemShell(parameter.systemShell) .envArray(parameter.env) .argArray(parameter.arguments) .initialCommand(parameter.initialCommand) diff --git a/app/src/main/java/io/neoterm/component/session/shell.kt b/app/src/main/java/io/neoterm/component/session/shell.kt index 1ffd10c..798b56a 100644 --- a/app/src/main/java/io/neoterm/component/session/shell.kt +++ b/app/src/main/java/io/neoterm/component/session/shell.kt @@ -27,7 +27,6 @@ class ShellParameter { var initialCommand: String? = null var env: Array>? = null var sessionCallback: TerminalSession.SessionChangedCallback? = null - var systemShell: Boolean = false var shellProfile: ShellProfile? = null fun executablePath(executablePath: String?): ShellParameter { @@ -60,11 +59,6 @@ class ShellParameter { return this } - fun systemShell(systemShell: Boolean): ShellParameter { - this.systemShell = systemShell - return this - } - fun profile(shellProfile: ShellProfile): ShellParameter { this.shellProfile = shellProfile return this @@ -291,21 +285,14 @@ open class ShellTermSession private constructor( return this } - fun systemShell(systemShell: Boolean): Builder { - this.systemShell = systemShell - return this - } fun create(context: Context): ShellTermSession { val cwd = this.cwd ?: NeoTermPath.HOME_PATH - val shell = this.executablePath ?: if (systemShell) - "/system/bin/sh" - else - shellProfile.loginShell + val shell = shellProfile.loginShell val args = this.args ?: mutableListOf(shell) - val env = transformEnvironment(this.env) ?: buildEnvironment(cwd, systemShell) + val env = transformEnvironment(this.env) ?: buildEnvironment(cwd) val callback = changeCallback ?: TermSessionCallback() return ShellTermSession( shell, cwd, args.toTypedArray(), env, callback, @@ -324,7 +311,7 @@ open class ShellTermSession private constructor( } - private fun buildEnvironment(cwd: String?, systemShell: Boolean): Array { + private fun buildEnvironment(cwd: String?): Array { val selectedCwd = cwd ?: NeoTermPath.HOME_PATH File(NeoTermPath.HOME_PATH).mkdirs() @@ -336,62 +323,11 @@ open class ShellTermSession private constructor( val externalStorageEnv = "EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE") val colorterm = "COLORTERM=truecolor" - // PY Trade: Some programs support NeoTerm in a special way. - val neotermIdEnv = "__NEOTERM=1" - val originPathEnv = "__NEOTERM_ORIGIN_PATH=" + buildOriginPathEnv() - val originLdEnv = "__NEOTERM_ORIGIN_LD_LIBRARY_PATH=" + buildOriginLdLibEnv() - - return if (systemShell) { - val pathEnv = "PATH=" + System.getenv("PATH") - arrayOf( - termEnv, homeEnv, androidRootEnv, androidDataEnv, - externalStorageEnv, pathEnv, neotermIdEnv, prefixEnv, - originLdEnv, originPathEnv, colorterm - ) - - } else { - val ps1Env = "PS1=$ " - val langEnv = "LANG=en_US.UTF-8" - val pathEnv = "PATH=" + buildPathEnv() - val ldEnv = "LD_LIBRARY_PATH=" + buildLdLibraryEnv() - val pwdEnv = "PWD=$selectedCwd" - val tmpdirEnv = "TMPDIR=${NeoTermPath.USR_PATH}/tmp" - - - // execve(2) wrapper to avoid incorrect shebang - val ldPreloadEnv = if (shellProfile.enableExecveWrapper) { - "LD_PRELOAD=${App.get().applicationInfo.nativeLibraryDir}/libnexec.so" - } else { - "" - } - - arrayOf( - termEnv, homeEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, - androidRootEnv, androidDataEnv, externalStorageEnv, - tmpdirEnv, neotermIdEnv, originPathEnv, originLdEnv, - ldPreloadEnv, prefixEnv, colorterm - ) - } - .filter { it.isNotEmpty() } - .toTypedArray() - } - - private fun buildOriginPathEnv(): String { - val path = System.getenv("PATH") - return path ?: "" - } - - private fun buildOriginLdLibEnv(): String { - val path = System.getenv("LD_LIBRARY_PATH") - return path ?: "" - } - - private fun buildLdLibraryEnv(): String { - return "${NeoTermPath.USR_PATH}/lib" - } - - private fun buildPathEnv(): String { - return "${NeoTermPath.USR_PATH}/bin:${NeoTermPath.USR_PATH}/bin/applets" + val pathEnv = "PATH=" + System.getenv("PATH") + return arrayOf( + termEnv, homeEnv, androidRootEnv, androidDataEnv, + externalStorageEnv, pathEnv, prefixEnv, colorterm + ).filter { it.isNotEmpty() }.toTypedArray() } } } diff --git a/app/src/main/java/io/neoterm/frontend/floating/dialog.kt b/app/src/main/java/io/neoterm/frontend/floating/dialog.kt index 864ad30..c010ecb 100644 --- a/app/src/main/java/io/neoterm/frontend/floating/dialog.kt +++ b/app/src/main/java/io/neoterm/frontend/floating/dialog.kt @@ -53,7 +53,6 @@ class TerminalDialog(val context: Context) { .executablePath(executablePath) .arguments(arguments) .callback(terminalSessionCallback) - .systemShell(false) terminalSession = Terminals.createSession(context, parameter) if (terminalSession is ShellTermSession) { (terminalSession as ShellTermSession).exitPrompt = context.getString(R.string.process_exit_prompt_press_back) diff --git a/app/src/main/java/io/neoterm/setup/SetupThread.java b/app/src/main/java/io/neoterm/setup/SetupThread.java deleted file mode 100644 index 42e5103..0000000 --- a/app/src/main/java/io/neoterm/setup/SetupThread.java +++ /dev/null @@ -1,160 +0,0 @@ -package io.neoterm.setup; - -import android.app.ProgressDialog; -import android.system.Os; -import android.util.Pair; -import androidx.appcompat.app.AppCompatActivity; -import io.neoterm.backend.EmulatorDebug; -import io.neoterm.component.config.NeoTermPath; -import io.neoterm.utils.NLog; - -import java.io.*; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -/** - * @author kiva - */ - -final class SetupThread extends Thread { - private final SourceConnection sourceConnection; - private final File prefixPath; - private final AppCompatActivity activity; - private final ResultListener resultListener; - private final ProgressDialog progressDialog; - - public SetupThread(AppCompatActivity activity, SourceConnection sourceConnection, - File prefixPath, ResultListener resultListener, - ProgressDialog progressDialog) { - this.activity = activity; - this.sourceConnection = sourceConnection; - this.prefixPath = prefixPath; - this.resultListener = resultListener; - this.progressDialog = progressDialog; - } - - @Override - public void run() { - try { - final String stagingPrefixPath = NeoTermPath.ROOT_PATH + "/usr-staging"; - final File stagingPrefixFile = new File(stagingPrefixPath); - - if (stagingPrefixFile.exists()) { - deleteFolder(stagingPrefixFile); - } - - int totalReadBytes = 0; - final byte[] buffer = new byte[8096]; - final List> symlinks = new ArrayList<>(50); - - - try (ZipInputStream zipInput = new ZipInputStream(sourceConnection.getInputStream())) { - ZipEntry zipEntry; - - int totalBytes = sourceConnection.getSize(); - - while ((zipEntry = zipInput.getNextEntry()) != null) { - totalReadBytes += zipEntry.getCompressedSize(); - - final int totalReadBytesFinal = totalReadBytes; - final int totalBytesFinal = totalBytes; - - activity.runOnUiThread(() -> { - try { - double progressFloat = ((double) totalReadBytesFinal) / ((double) totalBytesFinal) * 100.0; - progressDialog.setProgress((int) progressFloat); - } catch (RuntimeException ignore) { - // activity dismissed - } - }); - - if (zipEntry.getName().contains("SYMLINKS.txt")) { - BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput)); - String line; - while ((line = symlinksReader.readLine()) != null) { - if (line.isEmpty()) { - continue; - } - String[] parts = line.split("←"); - if (parts.length != 2) - throw new RuntimeException("Malformed symlink line: " + line); - String oldPath = parts[0]; - String newPath = stagingPrefixPath + "/" + parts[1]; - symlinks.add(Pair.create(oldPath, newPath)); - } - } else { - String zipEntryName = zipEntry.getName(); - File targetFile = new File(stagingPrefixPath, zipEntryName); - if (zipEntry.isDirectory()) { - if (!targetFile.mkdirs()) - throw new RuntimeException("Failed to create directory: " + targetFile.getAbsolutePath()); - } else { - try (FileOutputStream outStream = new FileOutputStream(targetFile)) { - int readBytes; - while ((readBytes = zipInput.read(buffer)) != -1) { - outStream.write(buffer, 0, readBytes); - } - } - if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") || zipEntryName.startsWith("lib/apt/methods")) { - //noinspection OctalInteger - Os.chmod(targetFile.getAbsolutePath(), 0700); - } - } - } - } - } - - sourceConnection.close(); - - if (symlinks.isEmpty()) - throw new RuntimeException("No SYMLINKS.txt encountered"); - for (Pair symlink : symlinks) { - NLog.INSTANCE.e("Setup", "Linking " + symlink.first + " to " + symlink.second); - Os.symlink(symlink.first, symlink.second); - } - - if (!stagingPrefixFile.renameTo(prefixPath)) { - throw new RuntimeException("Unable to rename staging folder"); - } - - activity.runOnUiThread(() -> resultListener.onResult(null)); - } catch (final Exception e) { - NLog.INSTANCE.e(EmulatorDebug.LOG_TAG, "Bootstrap error", e); - activity.runOnUiThread(() -> { - try { - resultListener.onResult(e); - } catch (RuntimeException e1) { - // Activity already dismissed - ignore. - } - }); - } finally { - activity.runOnUiThread(() -> { - try { - progressDialog.dismiss(); - } catch (RuntimeException e) { - // Activity already dismissed - ignore. - } - }); - } - } - - private static void deleteFolder(File fileOrDirectory) throws IOException { - if (fileOrDirectory.getCanonicalPath().equals(fileOrDirectory.getAbsolutePath()) && fileOrDirectory.isDirectory()) { - File[] children = fileOrDirectory.listFiles(); - - if (children != null) { - for (File child : children) { - deleteFolder(child); - } - } - } - - if (!fileOrDirectory.delete()) { - throw new RuntimeException("Unable to delete " - + (fileOrDirectory.isDirectory() ? "directory " : "file ") - + fileOrDirectory.getAbsolutePath()); - } - } -} diff --git a/app/src/main/java/io/neoterm/setup/SourceConnection.java b/app/src/main/java/io/neoterm/setup/SourceConnection.java deleted file mode 100644 index 0fe8f89..0000000 --- a/app/src/main/java/io/neoterm/setup/SourceConnection.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.neoterm.setup; - -import java.io.IOException; -import java.io.InputStream; - -/** - * @author kiva - */ -public interface SourceConnection { - InputStream getInputStream() throws IOException; - int getSize(); - void close(); -} diff --git a/app/src/main/java/io/neoterm/setup/connections.kt b/app/src/main/java/io/neoterm/setup/connections.kt deleted file mode 100644 index 8ba120a..0000000 --- a/app/src/main/java/io/neoterm/setup/connections.kt +++ /dev/null @@ -1,113 +0,0 @@ -package io.neoterm.setup - -import android.content.Context -import android.net.Uri -import java.io.IOException -import java.io.InputStream -import java.net.HttpURLConnection -import java.net.URL - -/** - * @author kiva - */ - -class BackupFileConnection(context: Context, uri: Uri) : LocalFileConnection(context, uri) - -/** - * @author kiva - */ - -open class LocalFileConnection(context: Context, uri: Uri) : OfflineUriConnection(context, uri) - -/** - * @author kiva - */ - -class NetworkConnection(private val sourceUrl: String) : SourceConnection { - private var connection: HttpURLConnection? = null - - @Throws(IOException::class) - override fun getInputStream(): InputStream { - if (connection == null) { - connection = openHttpConnection() - connection!!.connectTimeout = 8000 - connection!!.readTimeout = 8000 - } - return connection!!.inputStream - } - - override fun getSize(): Int { - return if (connection != null) { - connection!!.contentLength - } else 0 - - } - - override fun close() { - if (connection != null) { - connection!!.disconnect() - } - } - - @Throws(IOException::class) - private fun openHttpConnection(): HttpURLConnection { - val arch = SetupHelper.determineArchName() - - return URL("$sourceUrl/boot/$arch.zip").openConnection() as HttpURLConnection - } -} - -/** - * @author kiva - */ - -abstract class OfflineConnection : SourceConnection { - private var inputStream: InputStream? = null - - @Throws(IOException::class) - protected abstract fun openInputStream(): InputStream - - @Throws(IOException::class) - override fun getInputStream(): InputStream { - if (inputStream == null) { - inputStream = openInputStream() - } - return inputStream!! - } - - override fun getSize(): Int { - if (inputStream != null) { - return try { - inputStream!!.available() - } catch (e: IOException) { - e.printStackTrace() - 0 - } - - } - return 0 - } - - override fun close() { - if (inputStream != null) { - try { - inputStream!!.close() - } catch (ignore: IOException) { - ignore.printStackTrace() - } - - } - } -} - -/** - * @author kiva - */ - -open class OfflineUriConnection(private val context: Context, private val uri: Uri) : OfflineConnection() { - - @Throws(IOException::class) - override fun openInputStream(): InputStream { - return context.contentResolver.openInputStream(uri) - } -} diff --git a/app/src/main/java/io/neoterm/setup/setup.kt b/app/src/main/java/io/neoterm/setup/setup.kt deleted file mode 100644 index 1bfca1a..0000000 --- a/app/src/main/java/io/neoterm/setup/setup.kt +++ /dev/null @@ -1,87 +0,0 @@ -package io.neoterm.setup - -import android.app.ProgressDialog -import android.content.Context -import android.os.Build -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import io.neoterm.App -import io.neoterm.R -import io.neoterm.component.config.NeoTermPath -import java.io.File -import java.util.* - -/** - * @author kiva - */ -interface ResultListener { - fun onResult(error: Exception?) -} - -/** - * @author kiva - */ -object SetupHelper { - fun needSetup(): Boolean { - val PREFIX_FILE = File(NeoTermPath.USR_PATH) - return !PREFIX_FILE.isDirectory - } - - fun setup( - activity: AppCompatActivity, connection: SourceConnection, - resultListener: ResultListener - ) { - if (!needSetup()) { - resultListener.onResult(null) - return - } - - val prefixFile = File(NeoTermPath.USR_PATH) - - val progress = makeProgressDialog(activity) - progress.max = 100 - progress.show() - - SetupThread(activity, connection, prefixFile, resultListener, progress).start() - } - - private fun makeProgressDialog(context: Context): ProgressDialog { - return makeProgressDialog(context, context.getString(R.string.installer_message)) - } - - fun makeProgressDialog(context: Context, message: String): ProgressDialog { - val dialog = ProgressDialog(context) - dialog.setMessage(message) - dialog.isIndeterminate = false - dialog.setCancelable(false) - dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL) - return dialog - } - - fun makeErrorDialog(context: Context, messageId: Int): AlertDialog { - return makeErrorDialog(context, context.getString(messageId)) - } - - fun makeErrorDialog(context: Context, message: String): AlertDialog { - return AlertDialog.Builder(context) - .setTitle(R.string.error) - .setMessage(message) - .setPositiveButton(android.R.string.yes, null) - .setNeutralButton(R.string.show_help) { _, _ -> App.get().openHelpLink() } - .create() - } - - fun determineArchName(): String { - for (androidArch in Build.SUPPORTED_ABIS) { - when (androidArch) { - "arm64-v8a" -> return "aarch64" - "armeabi-v7a" -> return "arm" - "x86_64" -> return "x86_64" - } - } - throw RuntimeException( - "Unable to determine arch from Build.SUPPORTED_ABIS = " - + Arrays.toString(Build.SUPPORTED_ABIS) - ) - } -} diff --git a/app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt b/app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt index ef60883..eb017cc 100644 --- a/app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt +++ b/app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt @@ -44,7 +44,6 @@ open class BaseCustomizeActivity : AppCompatActivity() { .executablePath("${NeoTermPath.USR_PATH}/bin/echo") .arguments(arrayOf("echo", "-e", *script)) .callback(sessionCallback) - .systemShell(false) session = Terminals.createSession(this, parameter) terminalView.attachSession(session) diff --git a/app/src/main/java/io/neoterm/ui/other/AboutActivity.kt b/app/src/main/java/io/neoterm/ui/other/AboutActivity.kt index fd6db93..97b6548 100644 --- a/app/src/main/java/io/neoterm/ui/other/AboutActivity.kt +++ b/app/src/main/java/io/neoterm/ui/other/AboutActivity.kt @@ -131,20 +131,6 @@ class AboutActivity : AppCompatActivity() { findViewById(R.id.about_source_code_view).setOnClickListener { openUrl("https://github.com/NeoTerm/NeoTerm") } - - findViewById(R.id.about_reset_app_view).setOnClickListener { - AlertDialog.Builder(this) - .setMessage(R.string.reset_app_warning) - .setPositiveButton(R.string.yes) { _, _ -> - resetApp() - } - .setNegativeButton(android.R.string.no, null) - .show() - } - } - - private fun resetApp() { - startActivity(Intent(this, SetupActivity::class.java)) } private fun openUrl(url: String) { diff --git a/app/src/main/java/io/neoterm/ui/other/SetupActivity.kt b/app/src/main/java/io/neoterm/ui/other/SetupActivity.kt deleted file mode 100644 index 7981aa0..0000000 --- a/app/src/main/java/io/neoterm/ui/other/SetupActivity.kt +++ /dev/null @@ -1,238 +0,0 @@ -package io.neoterm.ui.other - -import android.content.ActivityNotFoundException -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.widget.* -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import io.neoterm.App -import io.neoterm.R -import io.neoterm.component.config.NeoTermPath -import io.neoterm.component.pm.SourceHelper -import io.neoterm.setup.* -import io.neoterm.utils.getPathOfMediaUri -import io.neoterm.utils.runApt -import java.io.File - - -/** - * @author kiva - */ -class SetupActivity : AppCompatActivity(), View.OnClickListener, ResultListener { - companion object { - private const val REQUEST_SELECT_PARAMETER = 520; - } - - private var setupParameter = "" - private var setupParameterUri: Uri? = null - - private val hintMapping = arrayOf( - R.id.setup_method_online, R.string.setup_hint_online, - R.id.setup_method_local, R.string.setup_hint_local, - R.id.setup_method_backup, R.string.setup_hint_backup - ) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.ui_setup) - - val parameterEditor = findViewById(R.id.setup_source_parameter) - - val tipText = findViewById(R.id.setup_url_tip_text) - - val onCheckedChangeListener = CompoundButton.OnCheckedChangeListener { button, checked -> - if (checked) { - val id = button.id - val index = hintMapping.indexOf(id) - if (index < 0 || index % 2 != 0) { - parameterEditor.setHint(R.string.setup_input_source_parameter) - return@OnCheckedChangeListener - } - parameterEditor.setHint(hintMapping[index + 1]) - tipText.setText(hintMapping[index + 1]) - setDefaultValue(parameterEditor, id) - } - } - - findViewById(R.id.setup_method_online).setOnCheckedChangeListener(onCheckedChangeListener) - findViewById(R.id.setup_method_local).setOnCheckedChangeListener(onCheckedChangeListener) - findViewById(R.id.setup_method_backup).setOnCheckedChangeListener(onCheckedChangeListener) - - findViewById