diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4dd1422..0355ee4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,6 +4,8 @@ stages: - build before_script: +- apt update && apt -y install openjdk-17-jdk +- JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 - export GRADLE_USER_HOME=$(pwd)/.gradle - export PACKAGE=`egrep '^[[:blank:]]+package' ./nhterm/src/main/AndroidManifest.xml | awk -F'[=>]' '{print $2}' | sed s/\"//g` - export VERSION_NAME=`egrep '^[[:blank:]]+versionName=' ./build.gradle | awk -F '"' '{print $2}'` diff --git a/NeoLang/build.gradle b/NeoLang/build.gradle index 574f81b..c459fc7 100644 --- a/NeoLang/build.gradle +++ b/NeoLang/build.gradle @@ -32,6 +32,6 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } diff --git a/NeoTermBridge/build.gradle b/NeoTermBridge/build.gradle index d09a6f3..e8ca3cc 100644 --- a/NeoTermBridge/build.gradle +++ b/NeoTermBridge/build.gradle @@ -30,6 +30,6 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } diff --git a/Xorg/build.gradle b/Xorg/build.gradle index ab1f764..7278189 100644 --- a/Xorg/build.gradle +++ b/Xorg/build.gradle @@ -35,6 +35,6 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } diff --git a/build.gradle b/build.gradle index f58a7c1..100eb37 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.5.30' + ext.kotlin_version = '1.6.10' ext.android = [ - KOTLIN_VERSION : '1.5.30', + KOTLIN_VERSION : '1.6.10', MIN_SDK_VERSION : 21, COMPILE_SDK_VERSION: 31, - TARGET_SDK_VERSION : 32, + TARGET_SDK_VERSION : 28, JUNIT_VERSION : "4.12" ] @@ -14,8 +14,8 @@ buildscript { ext { //version=YYYYMMVVRR (Either "VV" for stable version OR "RR" for pre-release candidate (e.g. 0001 for rc1)) //noinspection HighAppVersionCode - versionCode=2023030200 - versionName="2023.3" + versionCode=2023040100 + versionName="2023.4" } ext.deps = [ @@ -35,7 +35,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.3.1' + classpath 'com.android.tools.build:gradle:8.0.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath rootProject.ext.deps["kotlin-gradle-plugin"] diff --git a/chrome-tabs/build.gradle b/chrome-tabs/build.gradle index 4846b5a..4a6ec4a 100644 --- a/chrome-tabs/build.gradle +++ b/chrome-tabs/build.gradle @@ -25,6 +25,6 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } diff --git a/chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/view/TabSwitcherButton.java b/chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/view/TabSwitcherButton.java index 2eb3239..7a9a539 100644 --- a/chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/view/TabSwitcherButton.java +++ b/chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/view/TabSwitcherButton.java @@ -41,13 +41,13 @@ public class TabSwitcherButton extends AppCompatImageButton implements TabSwitch private TabSwitcherDrawable drawable; /** - * Initializes the view. + * Initializes the view. // selectableItemBackgroundBorderless */ private void initialize() { drawable = new TabSwitcherDrawable(getContext()); setImageDrawable(drawable); ViewUtil.setBackground(this, - ThemeUtil.getDrawable(getContext(), R.attr.selectableItemBackgroundBorderless)); + ThemeUtil.getDrawable(getContext(), androidx.appcompat.R.attr.selectableItemBackgroundBorderless)); setContentDescription(null); setClickable(true); setFocusable(true); @@ -144,4 +144,4 @@ public class TabSwitcherButton extends AppCompatImageButton implements TabSwitch drawable.onAllTabsRemoved(tabSwitcher, tabs, animation); } -} \ No newline at end of file +} diff --git a/chrome-tabs/src/main/res/values/attrs.xml b/chrome-tabs/src/main/res/values/attrs.xml index 220eb8e..c13a503 100644 --- a/chrome-tabs/src/main/res/values/attrs.xml +++ b/chrome-tabs/src/main/res/values/attrs.xml @@ -15,20 +15,63 @@ License. --> - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle.properties b/gradle.properties index 1eb1d68..a8d1527 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,3 +14,5 @@ org.gradle.jvmargs=-Xmx1536m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true + +ndkVersion=25.1.8937393 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ec7c7ff..9ec9d1f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 diff --git a/nhterm/build.gradle b/nhterm/build.gradle index d841496..8963a4b 100644 --- a/nhterm/build.gradle +++ b/nhterm/build.gradle @@ -15,7 +15,7 @@ android { externalNativeBuild { cmake { cppFlags "-std=c++11" - abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' + abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64' } } signingConfigs { @@ -63,18 +63,26 @@ dependencies { implementation rootProject.ext.deps["kotlin-stdlib"] - implementation 'org.greenrobot:eventbus:3.0.0' - implementation 'com.github.wrdlbrnft:modular-adapter:0.2.0.6' - implementation 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.19' + implementation 'org.greenrobot:eventbus:3.3.1' + implementation 'com.github.wrdlbrnft:modular-adapter:0.3.0.22' + implementation 'com.github.wrdlbrnft:sorted-list-adapter:0.3.0.27' implementation 'com.simplecityapps:recyclerview-fastscroll:1.0.16' - implementation 'de.psdev.licensesdialog:licensesdialog:1.8.3' + implementation 'de.psdev.licensesdialog:licensesdialog:1.9.0' implementation 'com.github.GrenderG:Color-O-Matic:1.1.5' + implementation 'com.github.topjohnwu.libsu:core:5.2.1' - implementation 'androidx.annotation:annotation:1.2.0' + implementation 'androidx.annotation:annotation:1.3.0' + implementation "androidx.core:core:1.6.0" implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.appcompat:appcompat-resources:1.2.0' - implementation 'androidx.preference:preference:1.0.0' + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'androidx.appcompat:appcompat-resources:1.3.0' + implementation 'com.google.android.material:material:1.5.0' + implementation 'androidx.preference:preference:1.1.0' + implementation "androidx.compose.material:material:1.0.0" + + // Backports for lower api levels + implementation 'com.llamalab.safs:safs-core:0.2.0' + implementation 'me.zhanghai.android.retrofile:library:1.1.1' implementation project(':chrome-tabs') implementation project(':NeoLang') @@ -83,6 +91,6 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } diff --git a/nhterm/src/main/AndroidManifest.xml b/nhterm/src/main/AndroidManifest.xml index a09d398..85f2b64 100644 --- a/nhterm/src/main/AndroidManifest.xml +++ b/nhterm/src/main/AndroidManifest.xml @@ -38,6 +38,8 @@ android:label="@string/app_name" android:usesCleartextTraffic="true" android:resizeableActivity="true" + android:hardwareAccelerated="true" + android:largeHeap="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="false" tools:replace="android:supportsRtl" @@ -46,7 +48,7 @@ android:name=".ui.term.NeoTermActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:launchMode="singleTask" - android:theme="@style/AppTheme.NoActionBar.Dark" + android:theme="@style/AppTheme.NoActionBar" android:windowSoftInputMode="adjustResize|stateHidden" android:exported="true"> @@ -79,7 +81,7 @@ android:name=".ui.term.NeoTermRemoteInterface" android:configChanges="orientation|keyboardHidden" android:exported="true" - android:theme="@style/AppTheme.Dark" + android:theme="@style/AppTheme" android:windowSoftInputMode="adjustResize|stateHidden"> @@ -141,12 +143,12 @@ android:name=".ui.other.AboutActivity" android:exported="false" android:label="@string/about" - android:theme="@style/AppTheme.NoActionBar.Dark"/> + android:theme="@style/AppTheme.NoActionBar"/> + android:theme="@style/AppTheme.NoActionBar"/> + android:theme="@style/AppTheme.NoActionBar"/> + android:theme="@style/AppTheme.NoActionBar"/> + android:theme="@style/AppTheme.NoActionBar"/> + android:theme="@style/AppTheme.NoActionBar"/> + android:theme="@style/AppTheme"/> + android:theme="@style/AppTheme"/> + android:theme="@style/AppTheme"/> Unit)?) { - AlertDialog.Builder(context) + MaterialAlertDialogBuilder(context, R.style.DialogStyle) .setTitle(R.string.error) .setMessage(message) .setNegativeButton(android.R.string.no, null) diff --git a/nhterm/src/main/java/com/offsec/nhterm/backend/KeyHandler.java b/nhterm/src/main/java/com/offsec/nhterm/backend/KeyHandler.java index 112c58d..e74e64c 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/backend/KeyHandler.java +++ b/nhterm/src/main/java/com/offsec/nhterm/backend/KeyHandler.java @@ -10,6 +10,7 @@ public final class KeyHandler { public static final int KEYMOD_ALT = 0x80000000; public static final int KEYMOD_CTRL = 0x40000000; public static final int KEYMOD_SHIFT = 0x20000000; + public static final int KEYMOD_NUM_LOCK = 0x10000000; private static final Map TERMCAP_TO_KEYCODE = new HashMap<>(); @@ -96,10 +97,16 @@ public final class KeyHandler { keyMod |= KEYMOD_ALT; keyCode &= ~KEYMOD_ALT; } + if ((keyCode & KEYMOD_NUM_LOCK) != 0) { + keyMod |= KEYMOD_NUM_LOCK; + keyCode &= ~KEYMOD_NUM_LOCK; + } return getCode(keyCode, keyMod, cursorKeysApplication, keypadApplication); } public static String getCode(int keyCode, int keyMode, boolean cursorApp, boolean keypadApplication) { + boolean numLockOn = (keyMode & KEYMOD_NUM_LOCK) != 0; + keyMode &= ~KEYMOD_NUM_LOCK; switch (keyCode) { case KEYCODE_DPAD_CENTER: return "\015"; @@ -179,7 +186,11 @@ public final class KeyHandler { // Just do what xterm and gnome-terminal does: return prefix + (((keyMode & KEYMOD_CTRL) == 0) ? "\u007F" : "\u0008"); case KEYCODE_NUM_LOCK: - return "\033OP"; + if (keypadApplication) { + return "\033OP"; + } else { + return null; + } case KEYCODE_SPACE: // If ctrl is not down, return null so that it goes through normal input processing (which may e.g. cause a @@ -200,31 +211,81 @@ public final class KeyHandler { case KEYCODE_NUMPAD_COMMA: return ","; case KEYCODE_NUMPAD_DOT: - return keypadApplication ? "\033On" : "."; + if (numLockOn) { + return keypadApplication ? "\033On" : "."; + } else { + // DELETE + return transformForModifiers("\033[3", keyMode, '~'); + } case KEYCODE_NUMPAD_SUBTRACT: return keypadApplication ? transformForModifiers("\033O", keyMode, 'm') : "-"; case KEYCODE_NUMPAD_DIVIDE: return keypadApplication ? transformForModifiers("\033O", keyMode, 'o') : "/"; case KEYCODE_NUMPAD_0: - return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "0"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "0"; + } else { + // INSERT + return transformForModifiers("\033[2", keyMode, '~'); + } case KEYCODE_NUMPAD_1: - return keypadApplication ? transformForModifiers("\033O", keyMode, 'q') : "1"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 'q') : "1"; + } else { + // END + return (keyMode == 0) ? (cursorApp ? "\033OF" : "\033[F") : transformForModifiers("\033[1", keyMode, 'F'); + } case KEYCODE_NUMPAD_2: - return keypadApplication ? transformForModifiers("\033O", keyMode, 'r') : "2"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 'r') : "2"; + } else { + // DOWN + return (keyMode == 0) ? (cursorApp ? "\033OB" : "\033[B") : transformForModifiers("\033[1", keyMode, 'B'); + } case KEYCODE_NUMPAD_3: - return keypadApplication ? transformForModifiers("\033O", keyMode, 's') : "3"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 's') : "3"; + } else { + // PGDN + return "\033[6~"; + } case KEYCODE_NUMPAD_4: - return keypadApplication ? transformForModifiers("\033O", keyMode, 't') : "4"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 't') : "4"; + } else { + // LEFT + return (keyMode == 0) ? (cursorApp ? "\033OD" : "\033[D") : transformForModifiers("\033[1", keyMode, 'D'); + } case KEYCODE_NUMPAD_5: return keypadApplication ? transformForModifiers("\033O", keyMode, 'u') : "5"; case KEYCODE_NUMPAD_6: - return keypadApplication ? transformForModifiers("\033O", keyMode, 'v') : "6"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 'v') : "6"; + } else { + // RIGHT + return (keyMode == 0) ? (cursorApp ? "\033OC" : "\033[C") : transformForModifiers("\033[1", keyMode, 'C'); + } case KEYCODE_NUMPAD_7: - return keypadApplication ? transformForModifiers("\033O", keyMode, 'w') : "7"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 'w') : "7"; + } else { + // HOME + return (keyMode == 0) ? (cursorApp ? "\033OH" : "\033[H") : transformForModifiers("\033[1", keyMode, 'H'); + } case KEYCODE_NUMPAD_8: - return keypadApplication ? transformForModifiers("\033O", keyMode, 'x') : "8"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 'x') : "8"; + } else { + // UP + return (keyMode == 0) ? (cursorApp ? "\033OA" : "\033[A") : transformForModifiers("\033[1", keyMode, 'A'); + } case KEYCODE_NUMPAD_9: - return keypadApplication ? transformForModifiers("\033O", keyMode, 'y') : "9"; + if (numLockOn) { + return keypadApplication ? transformForModifiers("\033O", keyMode, 'y') : "9"; + } else { + // PGUP + return "\033[5~"; + } case KEYCODE_NUMPAD_EQUALS: return keypadApplication ? transformForModifiers("\033O", keyMode, 'X') : "="; } diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/colorscheme/comp.kt b/nhterm/src/main/java/com/offsec/nhterm/component/colorscheme/comp.kt index a75b45b..1ecef8f 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/colorscheme/comp.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/colorscheme/comp.kt @@ -1,6 +1,7 @@ package com.offsec.nhterm.component.colorscheme import android.content.Context +import android.os.Build import io.neolang.frontend.ConfigVisitor import com.offsec.nhterm.App import com.offsec.nhterm.R @@ -18,7 +19,7 @@ import java.nio.file.Files class ColorSchemeComponent : ConfigFileBasedComponent(NeoTermPath.COLORS_PATH) { companion object { fun colorFile(colorName: String): File { - return File("${NeoTermPath.COLORS_PATH}/$colorName.nl") + return File("${NeoTermPath.COLORS_PATH}/$colorName.nl") } } @@ -49,18 +50,22 @@ class ColorSchemeComponent : ConfigFileBasedComponent(NeoTermPat fun reloadColorSchemes(): Boolean { colors.clear() - File(baseDir) - .listFiles(NEOLANG_FILTER) - .mapNotNull { this.loadConfigure(it) } - .forEach { - colors.put(it.colorName, it) - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + File(baseDir) + .listFiles(NEOLANG_FILTER) + .mapNotNull { this.loadConfigure(it) } + .forEach { + colors.put(it.colorName, it) + } - if (colors.containsKey(DefaultColorScheme.colorName)) { - DEFAULT_COLOR = colors[DefaultColorScheme.colorName]!! - return true + if (colors.containsKey(DefaultColorScheme.colorName)) { + DEFAULT_COLOR = colors[DefaultColorScheme.colorName]!! + return true + } + return false + } else { + return false } - return false } fun applyColorScheme(view: TerminalView?, extraKeysView: ExtraKeysView?, colorScheme: NeoColorScheme?) { @@ -112,7 +117,11 @@ class ColorSchemeComponent : ConfigFileBasedComponent(NeoTermPat val content = component.newGenerator(colorScheme).generateCode(colorScheme) kotlin.runCatching { - Files.write(colorFile.toPath(), content.toByteArray()) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Files.write(colorFile.toPath(), content.toByteArray()) + } else { + return + } }.onFailure { throw RuntimeException("Failed to save file ${colorFile.absolutePath}") } diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/colorscheme/data.kt b/nhterm/src/main/java/com/offsec/nhterm/component/colorscheme/data.kt index 113e660..fb20665 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/colorscheme/data.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/colorscheme/data.kt @@ -1,5 +1,7 @@ package com.offsec.nhterm.component.colorscheme +import android.os.Build +import com.offsec.nhterm.R import io.neolang.frontend.ConfigVisitor import com.offsec.nhterm.backend.TerminalColorScheme import com.offsec.nhterm.backend.TerminalColors @@ -139,12 +141,16 @@ open class NeoColorScheme : CodeGenObject, ConfigFileBasedObject { if (session != null && session.emulator != null) { session.emulator.setColorScheme(scheme) } - view.setBackgroundColor(TerminalColors.parse(backgroundColor)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + view.setBackgroundColor(TerminalColors.parse(backgroundColor)) + } } if (extraKeysView != null) { - extraKeysView.setBackgroundColor(TerminalColors.parse(backgroundColor)) - extraKeysView.setTextColor(TerminalColors.parse(foregroundColor)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + extraKeysView.setBackgroundColor(TerminalColors.parse(backgroundColor)) + extraKeysView.setTextColor(TerminalColors.parse(foregroundColor)) + } } } diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/config/comp.kt b/nhterm/src/main/java/com/offsec/nhterm/component/config/comp.kt index dc71483..fc96042 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/config/comp.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/config/comp.kt @@ -2,6 +2,7 @@ package com.offsec.nhterm.component.config import android.content.Context import android.content.SharedPreferences +import android.os.Build import android.preference.PreferenceManager import android.system.ErrnoException import android.system.Os @@ -49,14 +50,17 @@ open class NeoConfigureFile(val configureFile: File) { fun getVisitor() = configVisitor ?: throw IllegalStateException("Configure file not loaded or parse failed.") - open fun parseConfigure() = kotlin.runCatching { - val programCode = String(Files.readAllBytes(configureFile.toPath())) - configParser.setInputSource(programCode) - val ast = configParser.parse() - val astVisitor = ast.visit().getVisitor(ConfigVisitor::class.java) ?: return false - astVisitor.start() - configVisitor = astVisitor.getCallback() + open fun parseConfigure() = kotlin.runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val programCode = String(Files.readAllBytes(configureFile.toPath())) + configParser.setInputSource(programCode) + + val ast = configParser.parse() + val astVisitor = ast.visit().getVisitor(ConfigVisitor::class.java) ?: return false + astVisitor.start() + configVisitor = astVisitor.getCallback() + } }.isSuccess } @@ -85,14 +89,16 @@ object NeoPreference { 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]) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // 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]) + } } } } @@ -226,13 +232,6 @@ object NeoPreference { ) } - fun isVibrateEnabled(): Boolean { - return loadBoolean( - R.string.key_general_vibrate, - DefaultValues.enableVibrate - ) - } - fun isExecveWrapperEnabled(): Boolean { return loadBoolean( R.string.key_general_use_execve_wrapper, diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/config/defaults.kt b/nhterm/src/main/java/com/offsec/nhterm/component/config/defaults.kt index a3b1a9d..3037820 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/config/defaults.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/config/defaults.kt @@ -3,10 +3,10 @@ package com.offsec.nhterm.component.config import android.annotation.SuppressLint object DefaultValues { - const val fontSize = 30 + const val fontSize = 18 const val enableBell = false - const val enableVibrate = true + const val enableVibrate = false const val enableExecveWrapper = true const val enableAutoCompletion = false const val enableFullScreen = false @@ -27,6 +27,7 @@ object NeoTermPath { @SuppressLint("SdCardPath") const val ROOT_PATH = "/data/data/com.offsec.nhterm/files" const val USR_PATH = "$ROOT_PATH/usr" + const val BIN_PATH = "$USR_PATH/bin" const val HOME_PATH = "/" const val APT_BIN_PATH = "$USR_PATH/bin/apt" const val LIB_PATH = "$USR_PATH/lib" @@ -43,7 +44,7 @@ object NeoTermPath { 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://example.com/nhterm" + private const val SOURCE = "http://http.kali.org/kali" val DEFAULT_MAIN_PACKAGE_SOURCE: String diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/extrakey/comp.kt b/nhterm/src/main/java/com/offsec/nhterm/component/extrakey/comp.kt index 6b49bc3..118938f 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/extrakey/comp.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/extrakey/comp.kt @@ -1,6 +1,7 @@ package com.offsec.nhterm.component.extrakey import android.content.Context +import android.os.Build import io.neolang.frontend.ConfigVisitor import com.offsec.nhterm.App import com.offsec.nhterm.component.ConfigFileBasedComponent @@ -57,12 +58,14 @@ class ExtraKeyComponent : ConfigFileBasedComponent(NeoTermPath.EKS_ private fun reloadExtraKeyConfig() { extraKeys.clear() - File(baseDir) - .listFiles(NEOLANG_FILTER) - .filter { it.absolutePath != NeoTermPath.EKS_DEFAULT_FILE } - .mapNotNull { this.loadConfigure(it) } - .forEach { - registerShortcutKeys(it) - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + File(baseDir) + .listFiles(NEOLANG_FILTER) + .filter { it.absolutePath != NeoTermPath.EKS_DEFAULT_FILE } + .mapNotNull { this.loadConfigure(it) } + .forEach { + registerShortcutKeys(it) + } + } } } diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/pm/NeoPackageParser.java b/nhterm/src/main/java/com/offsec/nhterm/component/pm/NeoPackageParser.java index 92a0ffd..35bf086 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/pm/NeoPackageParser.java +++ b/nhterm/src/main/java/com/offsec/nhterm/component/pm/NeoPackageParser.java @@ -4,6 +4,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.Objects; /** * @author kiva @@ -38,7 +39,7 @@ public class NeoPackageParser { KEY_HOMEPAGE = "Homepage", KEY_DESC = "Description"; - private BufferedReader reader; + private final BufferedReader reader; private ParseStateListener stateListener; NeoPackageParser(InputStream inputStream) { @@ -100,6 +101,9 @@ public class NeoPackageParser { } switch (key) { + case KEY_PACKAGE_NAME: + packageInfo.setPackageName(value); + break; case KEY_ARCH: packageInfo.setArchitecture(Architecture.Companion.parse(value)); break; @@ -149,16 +153,14 @@ public class NeoPackageParser { 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; + if (KEY_DESC.equals(key)) { + return packageInfo.getDescription() + " " + value; } + return value; } private boolean splitKeyAndValue(String line, String[] splits) { - int valueIndex = line.indexOf(':'); + int valueIndex = line.indexOf(": "); if (valueIndex < 0) { return false; } diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/pm/PackageComponent.java b/nhterm/src/main/java/com/offsec/nhterm/component/pm/PackageComponent.java index ffc55c4..7693eca 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/pm/PackageComponent.java +++ b/nhterm/src/main/java/com/offsec/nhterm/component/pm/PackageComponent.java @@ -7,6 +7,7 @@ import com.offsec.nhterm.component.NeoComponent; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; import java.util.HashMap; /** @@ -56,7 +57,7 @@ public class PackageComponent implements NeoComponent { } private void tryParsePackages(File packageListFile, final boolean clearPrevious) throws IOException { - NeoPackageParser packageParser = new NeoPackageParser(new FileInputStream(packageListFile)); + NeoPackageParser packageParser = new NeoPackageParser(Files.newInputStream(packageListFile.toPath())); packageParser.setStateListener(new NeoPackageParser.ParseStateListener() { @Override public void onStartState() { @@ -88,6 +89,7 @@ public class PackageComponent implements NeoComponent { neoPackages.put(packageInfo.getPackageName(), packageInfo); } }); + packageParser.parse(); } diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/pm/data.kt b/nhterm/src/main/java/com/offsec/nhterm/component/pm/data.kt index 241a7cd..51e10e2 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/pm/data.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/pm/data.kt @@ -17,13 +17,13 @@ enum class Architecture { } class NeoPackageInfo { - var packageName: String? = null + var packageName: String = "dummy" 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 fileName: String = "null" var dependenciesString: String? = null var dependencies: Array? = null var sizeInBytes: Long = 0L @@ -31,5 +31,5 @@ class NeoPackageInfo { var sha1: String? = null var sha256: String? = null var homePage: String? = null - var description: String? = null + var description: String = "Dummy package" } diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/pm/helper.kt b/nhterm/src/main/java/com/offsec/nhterm/component/pm/helper.kt index 9f90435..b8c9aab 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/pm/helper.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/pm/helper.kt @@ -1,11 +1,14 @@ package com.offsec.nhterm.component.pm +import android.util.Log import com.offsec.nhterm.App import com.offsec.nhterm.R import com.offsec.nhterm.component.ComponentManager +import com.offsec.nhterm.component.config.NeoPreference import com.offsec.nhterm.component.config.NeoTermPath import com.offsec.nhterm.framework.NeoTermDatabase import com.offsec.nhterm.utils.NLog +import com.topjohnwu.superuser.Shell import java.io.File import java.net.URL import java.nio.file.Files @@ -19,7 +22,7 @@ object SourceHelper { fun syncSource(sourceManager: SourceManager) { val content = buildString { - this.append("# Generated by NeoTerm-Preference\n") + this.append("# Generated by NetHunter TerminalPreference\n") sourceManager.getEnabledSources() .joinTo(this, "\n") { "deb [trusted=yes] ${it.url} ${it.repo}\n" } } @@ -29,6 +32,10 @@ object SourceHelper { } fun detectSourceFiles(): List { + // Workaround to get things running + // TODO: ( APT ) Make it prettier? + copySourceFromChroot() + val sourceManager = ComponentManager.getComponent().sourceManager val sourceFiles = ArrayList() try { @@ -38,10 +45,9 @@ object SourceHelper { File(NeoTermPath.PACKAGE_LIST_DIR) .listFiles() - .filterTo(sourceFiles) { file -> - prefixes.filter { file.name.startsWith(it) } - .count() > 0 - } + ?.filterTo(sourceFiles) { file -> + prefixes.count { file.name.startsWith(it) } > 0 + } } catch (e: Exception) { sourceFiles.clear() NLog.e("PM", "Failed to detect source files: ${e.localizedMessage}") @@ -50,7 +56,7 @@ object SourceHelper { return sourceFiles } - fun detectSourceFilePrefix(source: Source): String { + private fun detectSourceFilePrefix(source: Source): String { try { val url = URL(source.url) val builder = StringBuilder(url.host) @@ -65,12 +71,42 @@ object SourceHelper { builder.append(fixedPath) } builder.append("_dists_${source.repo.replace(" ".toRegex(), "_")}_binary-") + Log.e("ERROR:", builder.toString()) return builder.toString() } catch (e: Exception) { NLog.e("PM", "Failed to detect source file prefix: ${e.localizedMessage}") return "" } } + + private fun copySourceFromChroot() { + val APP_MNT = NeoTermPath.USR_PATH + val MNT = "/data/local/nhsystem/kalifs" + val sources = "$MNT/etc/apt/sources.list" + val lists = "$MNT/var/lib/apt/lists" + + // Make sure that nhterm has locally required apt dir's + Shell.cmd("mkdir -p $APP_MNT/etc/apt").exec() + Shell.cmd("mkdir -p $APP_MNT/var/lib/apt/lists").exec() + + // Also we cant be sure that folders are empty from last use so lets remove stuff + Shell.cmd("rm -f $APP_MNT/etc/apt/*").exec() + Shell.cmd("rm -f $APP_MNT/var/lib/apt/lists/*").exec() + + // Now lets copy chroot apt sources.list and lists data to app + // This allows us to read and show list of packages for user in manager + Shell.cmd("cp -f $sources $APP_MNT/etc/apt/sources.list").exec() + Shell.cmd("cp -f $lists/* $APP_MNT/var/lib/apt/lists/").exec() + + // Now play with permissions so things are read/writable + Shell.cmd("chmod -R 775 $APP_MNT/etc/apt").exec() + Shell.cmd("chmod -R 775 $APP_MNT/var/lib/apt/lists").exec() + } + + fun updateChrootSource() { + // TODO: ( APT ) Add option for user to edit and update sources.list in Package Manager option + return + } } class SourceManager internal constructor() { @@ -84,7 +120,7 @@ class SourceManager internal constructor() { database.saveBean( Source( it, - "stable main", + "kali-rolling main", true ) ) @@ -122,7 +158,7 @@ class SourceManager internal constructor() { fun getMainPackageSource(): String { return getEnabledSources() .map { it.repo } - .singleOrNull { it.trim() == "stable main" } + .singleOrNull { it.trim() == "kali-rolling main" } ?: NeoTermPath.DEFAULT_MAIN_PACKAGE_SOURCE } diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/session/shell.kt b/nhterm/src/main/java/com/offsec/nhterm/component/session/shell.kt index 8b78d8b..4b63b45 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/session/shell.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/session/shell.kt @@ -113,7 +113,6 @@ class ShellProfile : NeoProfile() { var initialCommand = DefaultValues.initialCommand var enableBell = DefaultValues.enableBell - var enableVibrate = DefaultValues.enableVibrate var enableExecveWrapper = DefaultValues.enableExecveWrapper var enableSpecialVolumeKeys = DefaultValues.enableSpecialVolumeKeys var enableAutoCompletion = DefaultValues.enableAutoCompletion @@ -134,7 +133,6 @@ class ShellProfile : NeoProfile() { loginShell = NeoPreference.getLoginShellPath() initialCommand = NeoPreference.getInitialCommand() enableBell = NeoPreference.isBellEnabled() - enableVibrate = NeoPreference.isVibrateEnabled() enableExecveWrapper = NeoPreference.isExecveWrapperEnabled() enableSpecialVolumeKeys = NeoPreference.isSpecialVolumeKeysEnabled() enableAutoCompletion = NeoPreference.isAutoCompletionEnabled() @@ -148,7 +146,6 @@ class ShellProfile : NeoProfile() { loginShell = configVisitor.getProfileString(LOGIN_SHELL, loginShell) initialCommand = configVisitor.getProfileString(INITIAL_COMMAND, initialCommand) enableBell = configVisitor.getProfileBoolean(BELL, enableBell) - enableVibrate = configVisitor.getProfileBoolean(VIBRATE, enableVibrate) enableExecveWrapper = configVisitor.getProfileBoolean(EXECVE_WRAPPER, enableExecveWrapper) enableSpecialVolumeKeys = configVisitor.getProfileBoolean(SPECIAL_VOLUME_KEYS, enableSpecialVolumeKeys) enableAutoCompletion = configVisitor.getProfileBoolean(AUTO_COMPLETION, enableAutoCompletion) diff --git a/nhterm/src/main/java/com/offsec/nhterm/component/userscript/comp.kt b/nhterm/src/main/java/com/offsec/nhterm/component/userscript/comp.kt index ba20b5f..874c4e1 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/component/userscript/comp.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/component/userscript/comp.kt @@ -2,18 +2,22 @@ package com.offsec.nhterm.component.userscript import android.content.Context import android.system.Os +import android.util.Log import com.offsec.nhterm.App import com.offsec.nhterm.component.NeoComponent import com.offsec.nhterm.component.config.NeoTermPath import com.offsec.nhterm.utils.NLog import com.offsec.nhterm.utils.extractAssetsDir +import com.topjohnwu.superuser.Shell import java.io.File class UserScript(val scriptFile: File) class UserScriptComponent : NeoComponent { var userScripts = listOf() - private val scriptDir = File(NeoTermPath.USER_SCRIPT_PATH) + var binFiles = listOf() + val scriptDir = File(NeoTermPath.USER_SCRIPT_PATH) + val binDir = File(NeoTermPath.BIN_PATH) override fun onServiceInit() = checkForFiles() @@ -22,11 +26,23 @@ class UserScriptComponent : NeoComponent { override fun onServiceObtained() = checkForFiles() - private fun extractDefaultScript(context: Context) = kotlin.runCatching { + fun extractDefaultScript(context: Context) = kotlin.runCatching { + Shell.cmd("mkdir -p /data/data/com.offsec.nhterm/files/usr/").exec() + Shell.cmd("rm -rf /data/data/com.offsec.nhterm/files/usr/bin/*") + + // Usual user script extraction context.extractAssetsDir("scripts", NeoTermPath.USER_SCRIPT_PATH) - scriptDir.listFiles().forEach { + + scriptDir.listFiles()?.forEach { Os.chmod(it.absolutePath, 448 /*Dec of 0700*/) } + + // Lets also extract the usual binaries too here + context.extractAssetsDir("bin", NeoTermPath.BIN_PATH) + binDir.listFiles()?.forEach { + Os.chmod(it.absolutePath, 448 /*Dec of 0700*/) + } + }.onFailure { NLog.e("UserScript", "Failed to extract default user scripts: ${it.localizedMessage}") } @@ -41,5 +57,10 @@ class UserScriptComponent : NeoComponent { .takeWhile { it.canExecute() } .map { UserScript(it) } .toList() + + binFiles = binDir.listFiles() + .takeWhile { it.canExecute() } + .map { UserScript(it) } + .toList() } } diff --git a/nhterm/src/main/java/com/offsec/nhterm/frontend/floating/dialog.kt b/nhterm/src/main/java/com/offsec/nhterm/frontend/floating/dialog.kt index 36dc436..99e21a9 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/frontend/floating/dialog.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/frontend/floating/dialog.kt @@ -6,8 +6,10 @@ import android.content.DialogInterface import android.view.LayoutInflater import android.view.View import androidx.appcompat.app.AlertDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.offsec.nhterm.R import com.offsec.nhterm.backend.TerminalSession +import com.offsec.nhterm.component.config.DefaultValues.initialCommand import com.offsec.nhterm.component.session.ShellParameter import com.offsec.nhterm.component.session.ShellTermSession import com.offsec.nhterm.frontend.session.terminal.BasicSessionCallback @@ -36,12 +38,12 @@ class TerminalDialog(val context: Context) { } } - fun execute(executablePath: String, arguments: Array?): TerminalDialog { + fun execute(executablePath: String, arguments: String, extraarg: String): TerminalDialog { if (terminalSession != null) { terminalSession?.finishIfRunning() } - dialog = AlertDialog.Builder(context) + dialog = MaterialAlertDialogBuilder(context, R.style.DialogStyle) .setView(termWindowView.rootView) .setOnCancelListener { terminalSession?.finishIfRunning() @@ -49,15 +51,19 @@ class TerminalDialog(val context: Context) { } .create() + val cmd = listOf(arguments + " " + extraarg + " && exit 0") + val parameter = ShellParameter() .executablePath(executablePath) - .arguments(arguments) + .initialCommand(cmd.joinToString()) .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) } + termWindowView.attachSession(terminalSession) return this } @@ -107,7 +113,7 @@ class WindowTermView(val context: Context) { Terminals.setupTerminalView(terminalView) } - fun setTerminalViewClient(terminalViewClient: com.offsec.nhterm.frontend.session.view.TerminalViewClient?) { + fun setTerminalViewClient(terminalViewClient: TerminalViewClient?) { terminalView.setTerminalViewClient(terminalViewClient) } diff --git a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/terminal/term-basic.kt b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/terminal/term-basic.kt index 68850a5..431a984 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/terminal/term-basic.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/terminal/term-basic.kt @@ -78,6 +78,14 @@ class BasicViewClient(val terminalView: TerminalView) : return false } + override fun readShiftKey(): Boolean { + return false + } + + override fun readFnKey(): Boolean { + return false + } + override fun onCodePoint(codePoint: Int, ctrlDown: Boolean, session: TerminalSession?): Boolean { return false } diff --git a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/terminal/term-standard.kt b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/terminal/term-standard.kt index 0605822..b27900b 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/terminal/term-standard.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/terminal/term-standard.kt @@ -3,19 +3,13 @@ package com.offsec.nhterm.frontend.session.terminal import android.content.ClipData import android.content.ClipboardManager import android.content.Context -import android.content.Context.VIBRATOR_SERVICE import android.media.AudioManager import android.media.SoundPool -import android.os.VibrationEffect -import android.os.Vibrator -import android.util.Log import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent import android.view.View import android.view.inputmethod.InputMethodManager -import androidx.core.content.ContextCompat.getSystemService -import com.offsec.nhterm.BuildConfig import com.offsec.nhterm.R import com.offsec.nhterm.backend.KeyHandler import com.offsec.nhterm.backend.TerminalSession @@ -36,6 +30,7 @@ class TermViewClient(val context: Context) : TerminalViewClient { private var mVirtualControlKeyDown: Boolean = false private var mVirtualFnKeyDown: Boolean = false + private var mVirtualShiftKeyDown: Boolean = false private var lastTitle: String = "" var termSessionData: TermSessionData? = null @@ -69,11 +64,6 @@ class TermViewClient(val context: Context) : return true } - if (NeoPreference.isVibrateEnabled()) { - val vibrator = context.getSystemService(Vibrator::class.java) - vibrator.vibrate(VibrationEffect.createOneShot(40, VibrationEffect.DEFAULT_AMPLITUDE)) - } - val termUI = termSessionData?.termUI when (keyCode) { @@ -140,6 +130,16 @@ class TermViewClient(val context: Context) : return (extraKeysView != null && extraKeysView.readAltButton()) || mVirtualFnKeyDown } + override fun readShiftKey(): Boolean { + val extraKeysView = termSessionData?.extraKeysView + return (extraKeysView != null && extraKeysView.readShiftButton()) || mVirtualShiftKeyDown + } + + override fun readFnKey(): Boolean { + val extraKeysView = termSessionData?.extraKeysView + return (extraKeysView != null && extraKeysView.readFnButton()) || mVirtualFnKeyDown + } + override fun onCodePoint(codePoint: Int, ctrlDown: Boolean, session: TerminalSession?): Boolean { if (mVirtualFnKeyDown) { var resultingKeyCode: Int = -1 @@ -361,13 +361,6 @@ class BellController { } soundPool?.play(bellId, 1f, 1f, 0, 0, 1f) } - - if (session.shellProfile.enableVibrate) { - if (NeoPreference.isVibrateEnabled()) { - val vibrator = context.getSystemService(Vibrator::class.java) - vibrator.vibrate(VibrationEffect.createOneShot(40, VibrationEffect.DEFAULT_AMPLITUDE)) - } - } } } @@ -434,14 +427,6 @@ class TermCompleteListener(var terminalView: TerminalView?) : OnAutoCompleteList } } - if (BuildConfig.DEBUG) { - Log.e( - "NeoTerm-AC", - "currentEditing: $textNeedCompletion, " + - "deleteLength: $deleteLength, completeString: $newText" - ) - } - pushString(newText) session.write(newText) // Trigger next completion diff --git a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/TerminalView.java b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/TerminalView.java index a7bb0c0..d05b02e 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/TerminalView.java +++ b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/TerminalView.java @@ -104,6 +104,11 @@ public final class TerminalView extends View { private boolean mAccessibilityEnabled; + public final static int KEY_EVENT_SOURCE_VIRTUAL_KEYBOARD = KeyCharacterMap.VIRTUAL_KEYBOARD; // -1 + + /** The {@link KeyEvent} is generated from a non-physical device, like if 0 value is returned by {@link KeyEvent#getDeviceId()}. */ + public final static int KEY_EVENT_SOURCE_SOFT_KEYBOARD = 0; + public TerminalView(Context context) { super(context); commonInit(context); @@ -334,11 +339,12 @@ public final class TerminalView extends View { // https://github.com/termux/termux-app/issues/87. // https://github.com/termux/termux-app/issues/126. // https://github.com/termux/termux-app/issues/137 (japanese chars and TYPE_NULL). + if (mEnableWordBasedIme) { // Workaround for Google Pinying cannot input Chinese - outAttrs.inputType = InputType.TYPE_CLASS_TEXT; + outAttrs.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; } else { - outAttrs.inputType = InputType.TYPE_NULL; + outAttrs.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL | InputType.TYPE_NULL; } // Note that IME_ACTION_NONE cannot be used as that makes it impossible to input newlines using the on-screen @@ -432,7 +438,7 @@ public final class TerminalView extends View { } } - inputCodePoint(codePoint, ctrlHeld, false); + inputCodePoint(KEY_EVENT_SOURCE_SOFT_KEYBOARD, codePoint, ctrlHeld, false); } } @@ -744,14 +750,16 @@ public final class TerminalView extends View { } final int metaState = event.getMetaState(); - final boolean controlDownFromEvent = event.isCtrlPressed(); - final boolean leftAltDownFromEvent = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0; + final boolean controlDown = event.isCtrlPressed() || mClient.readControlKey(); + final boolean leftAltDown = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0 || mClient.readAltKey(); + final boolean shiftDown = event.isShiftPressed() || mClient.readShiftKey(); final boolean rightAltDownFromEvent = (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0; int keyMod = 0; - if (controlDownFromEvent) keyMod |= KeyHandler.KEYMOD_CTRL; - if (event.isAltPressed()) keyMod |= KeyHandler.KEYMOD_ALT; - if (event.isShiftPressed()) keyMod |= KeyHandler.KEYMOD_SHIFT; + if (controlDown) keyMod |= KeyHandler.KEYMOD_CTRL; + if (event.isAltPressed() || leftAltDown) keyMod |= KeyHandler.KEYMOD_ALT; + if (shiftDown) keyMod |= KeyHandler.KEYMOD_SHIFT; + if (event.isNumLockOn()) keyMod |= KeyHandler.KEYMOD_NUM_LOCK; if (!event.isFunctionPressed() && handleKeyCode(keyCode, keyMod)) { if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "handleKeyCode() took key event"); return true; @@ -778,7 +786,7 @@ public final class TerminalView extends View { if ((result & KeyCharacterMap.COMBINING_ACCENT) != 0) { // If entered combining accent previously, write it out: if (mCombiningAccent != 0) - inputCodePoint(mCombiningAccent, controlDownFromEvent, leftAltDownFromEvent); + inputCodePoint(event.getDeviceId(), mCombiningAccent, controlDown, leftAltDown); mCombiningAccent = result & KeyCharacterMap.COMBINING_ACCENT_MASK; } else { if (mCombiningAccent != 0) { @@ -786,7 +794,7 @@ public final class TerminalView extends View { if (combinedChar > 0) result = combinedChar; mCombiningAccent = 0; } - inputCodePoint(result, controlDownFromEvent, leftAltDownFromEvent); + inputCodePoint(event.getDeviceId(), result, controlDown, leftAltDown); } if (mCombiningAccent != oldCombiningAccent) invalidate(); @@ -804,7 +812,7 @@ public final class TerminalView extends View { return true; } - void inputCodePoint(int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) { + public void inputCodePoint(int eventSource, int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) { if (LOG_KEY_EVENTS) { Log.i(EmulatorDebug.LOG_TAG, "inputCodePoint(codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent=" + leftAltDownFromEvent + ")"); @@ -842,19 +850,22 @@ public final class TerminalView extends View { } if (codePoint > -1) { - // Work around bluetooth keyboards sending funny unicode characters instead - // of the more normal ones from ASCII that terminal programs expect - the - // desire to input the original characters should be low. - switch (codePoint) { - case 0x02DC: // SMALL TILDE. - codePoint = 0x007E; // TILDE (~). - break; - case 0x02CB: // MODIFIER LETTER GRAVE ACCENT. - codePoint = 0x0060; // GRAVE ACCENT (`). - break; - case 0x02C6: // MODIFIER LETTER CIRCUMFLEX ACCENT. - codePoint = 0x005E; // CIRCUMFLEX ACCENT (^). - break; + // If not virtual or soft keyboard. + if (eventSource > KEY_EVENT_SOURCE_SOFT_KEYBOARD) { + // Work around bluetooth keyboards sending funny unicode characters instead + // of the more normal ones from ASCII that terminal programs expect - the + // desire to input the original characters should be low. + switch (codePoint) { + case 0x02DC: // SMALL TILDE. + codePoint = 0x007E; // TILDE (~). + break; + case 0x02CB: // MODIFIER LETTER GRAVE ACCENT. + codePoint = 0x0060; // GRAVE ACCENT (`). + break; + case 0x02C6: // MODIFIER LETTER CIRCUMFLEX ACCENT. + codePoint = 0x005E; // CIRCUMFLEX ACCENT (^). + break; + } } // If left alt, send escape before the code point to make e.g. Alt+B and Alt+F work in readline: diff --git a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/TerminalViewClient.java b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/TerminalViewClient.java index fa5ca64..04e0267 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/TerminalViewClient.java +++ b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/TerminalViewClient.java @@ -35,6 +35,10 @@ public interface TerminalViewClient { boolean readAltKey(); + boolean readShiftKey(); + + boolean readFnKey(); + boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session); boolean onLongPress(MotionEvent event); diff --git a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/extrakey/ExtraKeysView.kt b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/extrakey/ExtraKeysView.kt index f99bcd4..469528f 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/extrakey/ExtraKeysView.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/extrakey/ExtraKeysView.kt @@ -2,6 +2,7 @@ package com.offsec.nhterm.frontend.session.view.extrakey import android.content.Context import android.graphics.Typeface +import android.os.Build import android.os.VibrationEffect import android.os.Vibrator import android.util.AttributeSet @@ -29,6 +30,7 @@ class ExtraKeysView(context: Context, attrs: AttributeSet) : LinearLayout(contex private val ARROW_DOWN = ArrowButton(IExtraButton.KEY_ARROW_DOWN) private val ARROW_LEFT = ArrowButton(IExtraButton.KEY_ARROW_LEFT) private val ARROW_RIGHT = ArrowButton(IExtraButton.KEY_ARROW_RIGHT) + private val SLASH = ControlButton(IExtraButton.KEY_SLASH) private val TOGGLE_IME = object : ControlButton(KEY_TOGGLE_IME) { override fun onClick(view: View) { EventBus.getDefault().post(ToggleImeEvent()) @@ -51,8 +53,11 @@ class ExtraKeysView(context: Context, attrs: AttributeSet) : LinearLayout(contex // For avoid memory and context leak. private val CTRL = StatedControlButton(IExtraButton.KEY_CTRL) private val ALT = StatedControlButton(IExtraButton.KEY_ALT) + private val FN = StatedControlButton(IExtraButton.KEY_FN) + private val SHIFT = StatedControlButton(IExtraButton.KEY_SHIFT) private var buttonPanelExpanded = false + private val EXPAND_BUTTONS = object : ControlButton(IExtraButton.KEY_SHOW_ALL_BUTTONS) { override fun onClick(view: View) { expandButtonPanel() @@ -103,6 +108,14 @@ class ExtraKeysView(context: Context, attrs: AttributeSet) : LinearLayout(contex return ALT.readState() } + fun readFnButton(): Boolean { + return FN.readState() + } + + fun readShiftButton(): Boolean { + return SHIFT.readState() + } + fun addUserKey(button: IExtraButton) { addKeyButton(userKeys, button) } @@ -208,11 +221,6 @@ class ExtraKeysView(context: Context, attrs: AttributeSet) : LinearLayout(contex outerButton.isAllCaps = false outerButton.setOnClickListener { - if (NeoPreference.isVibrateEnabled()) { - val vibrator = context.getSystemService(Vibrator::class.java) - vibrator.vibrate(VibrationEffect.createOneShot(40, VibrationEffect.DEFAULT_AMPLITUDE)) - } - val root = rootView extraButton.onClick(root) } @@ -236,7 +244,12 @@ class ExtraKeysView(context: Context, attrs: AttributeSet) : LinearLayout(contex addBuiltinKey(HOME) addBuiltinKey(ARROW_UP) addBuiltinKey(END) - addBuiltinKey(EXPAND_BUTTONS) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + addBuiltinKey(EXPAND_BUTTONS) + } else { + addBuiltinKey(SLASH) + } } private fun calculateButtonWidth(): Int { diff --git a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/extrakey/buttons.kt b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/extrakey/buttons.kt index 5034dc1..edf8f17 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/extrakey/buttons.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/frontend/session/view/extrakey/buttons.kt @@ -50,6 +50,7 @@ abstract class IExtraButton : View.OnClickListener { const val KEY_ARROW_LEFT_TEXT = "Left" const val KEY_ARROW_RIGHT_TEXT = "Right" const val KEY_SHOW_ALL_BUTTONS = "···" + const val KEY_SLASH = "/" const val KEY_TOGGLE_IME = "⌨" const val KEY_ARROW_UP = "▲" @@ -73,6 +74,7 @@ abstract class IExtraButton : View.OnClickListener { const val KEY_F12 = "F12" // Extra keys + const val KEY_SHIFT = "LShift" const val KEY_DEL = "Del" const val KEY_ENTER = "Enter" @@ -97,6 +99,9 @@ abstract class IExtraButton : View.OnClickListener { KEY_PAGE_DOWN -> keyCode = KeyEvent.KEYCODE_PAGE_DOWN KEY_HOME -> keyCode = KeyEvent.KEYCODE_MOVE_HOME KEY_END -> keyCode = KeyEvent.KEYCODE_MOVE_END + KEY_FN -> keyCode = KeyEvent.KEYCODE_FUNCTION + KEY_SHIFT -> keyCode = KeyEvent.KEYCODE_SHIFT_LEFT + KEY_SLASH -> keyCode = KeyEvent.KEYCODE_SLASH "―" -> chars = "-" // Function keys diff --git a/nhterm/src/main/java/com/offsec/nhterm/setup/setup.kt b/nhterm/src/main/java/com/offsec/nhterm/setup/setup.kt index 4ecafa7..bfca056 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/setup/setup.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/setup/setup.kt @@ -5,6 +5,7 @@ import android.content.Context import android.os.Build import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.offsec.nhterm.App import com.offsec.nhterm.R import com.offsec.nhterm.component.config.NeoTermPath @@ -69,7 +70,7 @@ object SetupHelper { } fun makeErrorDialog(context: Context, message: String): AlertDialog { - return AlertDialog.Builder(context) + return MaterialAlertDialogBuilder(context, R.style.DialogStyle) .setTitle(R.string.error) .setMessage(message) .setPositiveButton(android.R.string.yes, null) diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/customize/ColorSchemeActivity.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/customize/ColorSchemeActivity.kt index 952d3d0..7449045 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/customize/ColorSchemeActivity.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/customize/ColorSchemeActivity.kt @@ -13,6 +13,7 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager import com.github.wrdlbrnft.sortedlistadapter.SortedListAdapter +import com.google.android.material.dialog.MaterialAlertDialogBuilder import es.dmoral.coloromatic.ColorOMaticDialog import es.dmoral.coloromatic.IndicatorMode import es.dmoral.coloromatic.colormode.ColorMode @@ -77,7 +78,7 @@ class ColorSchemeActivity : BaseCustomizeActivity() { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (keyCode == KeyEvent.KEYCODE_BACK && event!!.action == KeyEvent.ACTION_DOWN && changed) { - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setMessage(getString(R.string.discard_changes)) .setPositiveButton(R.string.save) { _, _ -> applyColorScheme(editingColorScheme, true) @@ -129,7 +130,7 @@ class ColorSchemeActivity : BaseCustomizeActivity() { changed = true } - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(model.colorName) .setView(view) .setNegativeButton(android.R.string.no, null) @@ -159,7 +160,7 @@ class ColorSchemeActivity : BaseCustomizeActivity() { val edit = view.findViewById(R.id.dialog_edit_text_editor) edit.setText(getString(R.string.save_color_scheme_name_template)) - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(R.string.save_color) .setView(view) .setPositiveButton(android.R.string.yes) { _, _ -> diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/other/AboutActivity.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/other/AboutActivity.kt index 3704080..8a250a4 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/other/AboutActivity.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/other/AboutActivity.kt @@ -1,14 +1,17 @@ package com.offsec.nhterm.ui.other +import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Bundle +import android.system.Os import android.view.MenuItem import android.view.View import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.dialog.MaterialAlertDialogBuilder import de.psdev.licensesdialog.LicensesDialog import de.psdev.licensesdialog.licenses.ApacheSoftwareLicense20 import de.psdev.licensesdialog.licenses.GnuGeneralPublicLicense30 @@ -17,9 +20,13 @@ import de.psdev.licensesdialog.model.Notice import de.psdev.licensesdialog.model.Notices import com.offsec.nhterm.App import com.offsec.nhterm.R +import com.offsec.nhterm.component.config.NeoTermPath +import com.offsec.nhterm.frontend.floating.TerminalDialog import com.offsec.nhterm.utils.extractAssetsDir +import com.topjohnwu.superuser.Shell import de.psdev.licensesdialog.licenses.SILOpenFontLicense11 import java.io.BufferedReader +import java.io.File import java.io.InputStreamReader @@ -161,10 +168,10 @@ class AboutActivity : AppCompatActivity() { } findViewById(R.id.about_reset_app_view).setOnClickListener { - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setMessage(R.string.reset_app_warning) - .setPositiveButton(R.string.yes) { _, _ -> - resetApp() + .setPositiveButton("yes") { _, _ -> + resetApp(this) resetisdone() } .setNegativeButton(android.R.string.no, null) @@ -173,41 +180,29 @@ class AboutActivity : AppCompatActivity() { } private fun resetisdone() { - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setMessage(R.string.done) - .setPositiveButton(R.string.ok) { _, _ -> + .setPositiveButton("Ok") { _, _ -> return@setPositiveButton } .show() } - fun resetApp() { - // Manual way of resetting required assets - Runtime.getRuntime().exec("mkdir -p "+" "+"/data/data/com.offsec.nhterm/files/usr/").waitFor() - Executer("/system/bin/rm -rf /data/data/com.offsec.nhterm/files/usr/bin") - Thread.sleep(1200) - extractAssetsDir("bin", "/data/data/com.offsec.nhterm/files/usr/bin/") - Thread.sleep(800) - Executer("/system/bin/chmod +x /data/data/com.offsec.nhterm/files/usr/bin/bash") // Static bash for arm ( works for *64 too ) - Executer("/system/bin/chmod +x /data/data/com.offsec.nhterm/files/usr/bin/kali") // Kali chroot scriptlet - Executer("/system/bin/chmod +x /data/data/com.offsec.nhterm/files/usr/bin/android-su") // Android su scriptlet - } + private fun resetApp(context: Context) { + val binDir = File(NeoTermPath.BIN_PATH) + //// + // As some roms act weird and cause issues like no assets are extracted on fresh run then we need to force + // assets extraction + //// + Shell.cmd("mkdir -p /data/data/com.offsec.nhterm/files/usr/").exec() + Shell.cmd("rm -rf /data/data/com.offsec.nhterm/files/usr/bin/*").exec() - fun Executer(command: String?): String? { - val output = StringBuilder() - val p: Process - try { - p = Runtime.getRuntime().exec(command) - p.waitFor() - val reader = BufferedReader(InputStreamReader(p.inputStream)) - var line: String? - while (reader.readLine().also { line = it } != null) { - output.append(line).append('\n') - } - } catch (e: Exception) { - e.printStackTrace() - } - return output.toString() + extractAssetsDir("bin", "/data/data/com.offsec.nhterm/files/usr/bin/") + + context.extractAssetsDir("bin", NeoTermPath.BIN_PATH) + binDir.listFiles()?.forEach { + Os.chmod(it.absolutePath, 448 /*Dec of 0700*/) + } } private fun openUrl(url: String) { diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/other/SetupActivity.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/other/SetupActivity.kt index b0ce87f..411aaa1 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/other/SetupActivity.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/other/SetupActivity.kt @@ -9,6 +9,7 @@ import android.view.View import android.widget.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.offsec.nhterm.App import com.offsec.nhterm.R import com.offsec.nhterm.component.config.NeoTermPath @@ -142,7 +143,7 @@ class SetupActivity : AppCompatActivity(), View.OnClickListener, ResultListener val edit = view.findViewById(R.id.dialog_edit_text_editor) - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(R.string.new_source) .setView(view) .setPositiveButton(android.R.string.yes) { _, _ -> @@ -192,7 +193,7 @@ class SetupActivity : AppCompatActivity(), View.OnClickListener, ResultListener val titleId = if (needSetup) R.string.setup_confirm else R.string.setup_reset_confirm val messageId = if (needSetup) R.string.setup_confirm_text else R.string.setup_reset_confirm_text - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(titleId) .setMessage(messageId) .setPositiveButton(android.R.string.yes) { _, _ -> @@ -213,7 +214,7 @@ class SetupActivity : AppCompatActivity(), View.OnClickListener, ResultListener executeAptUpdate() } else { - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(R.string.error) .setMessage(error.toString()) .setNegativeButton(R.string.use_system_shell) { _, _ -> @@ -228,11 +229,11 @@ class SetupActivity : AppCompatActivity(), View.OnClickListener, ResultListener } } - private fun executeAptUpdate() = runApt("update") { + private fun executeAptUpdate() = runApt("apt","update", "") { it.onSuccess { executeAptUpgrade() } } - private fun executeAptUpgrade() = runApt("upgrade", "-y") { + private fun executeAptUpgrade() = runApt("apt", "upgrade", "") { it.onSuccess { finish() } } } diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/pm/PackageManagerActivity.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/pm/PackageManagerActivity.kt index c44fa7b..9e5e97b 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/pm/PackageManagerActivity.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/pm/PackageManagerActivity.kt @@ -15,12 +15,15 @@ import androidx.appcompat.widget.Toolbar import androidx.core.view.MenuItemCompat import androidx.recyclerview.widget.LinearLayoutManager import com.github.wrdlbrnft.sortedlistadapter.SortedListAdapter +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.offsec.nhterm.R import com.offsec.nhterm.component.ComponentManager import com.offsec.nhterm.component.config.NeoPreference import com.offsec.nhterm.component.pm.* import com.offsec.nhterm.utils.StringDistance import com.offsec.nhterm.utils.runApt +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import java.util.* /** @@ -29,14 +32,14 @@ import java.util.* class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListener, SortedListAdapter.Callback { private val comparator = SortedListAdapter.ComparatorBuilder() - .setOrderForModel(PackageModel::class.java) { a, b -> + .setOrderForModel(PackageModel::class.java) { a, b -> a.packageInfo.packageName!!.compareTo(b.packageInfo.packageName!!) } .build() - lateinit var recyclerView: androidx.recyclerview.widget.RecyclerView - lateinit var adapter: PackageAdapter - var models = listOf() + private lateinit var recyclerView: androidx.recyclerview.widget.RecyclerView + private lateinit var adapter: PackageAdapter + private var models = listOf() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -49,7 +52,7 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen recyclerView.setHasFixedSize(true) adapter = PackageAdapter(this, comparator, object : PackageAdapter.Listener { override fun onModelClicked(model: PackageModel) { - AlertDialog.Builder(this@PackageManagerActivity) + MaterialAlertDialogBuilder(this@PackageManagerActivity, R.style.DialogStyle) .setTitle(model.packageInfo.packageName) .setMessage(model.getPackageDetails(this@PackageManagerActivity)) .setPositiveButton(R.string.install) { _, _ -> @@ -63,11 +66,12 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = adapter + refreshPackageList() } - private fun installPackage(packageName: String?) = packageName?.let { - runApt("install", "-y", it, autoClose = false) { + private fun installPackage(packageName: String?) = packageName?.let { it -> + runApt("apt install", "-y", it, autoClose = true) { it -> it.onSuccess { it.setTitle(getString(R.string.done)) } } } @@ -81,14 +85,14 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen } override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item?.itemId) { + when (item.itemId) { android.R.id.home -> finish() R.id.action_source -> changeSource() R.id.action_update_and_refresh -> executeAptUpdate() R.id.action_refresh -> refreshPackageList() R.id.action_upgrade -> executeAptUpgrade() } - return item?.let { super.onOptionsItemSelected(it) } + return item.let { super.onOptionsItemSelected(it) } } private fun changeSource() { @@ -97,7 +101,7 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen val items = sourceList.map { "${it.url} :: ${it.repo}" }.toTypedArray() val selection = sourceList.map { it.enabled }.toBooleanArray() - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(R.string.pref_package_source) .setMultiChoiceItems(items, selection) { _, which, isChecked -> sourceList[which].enabled = isChecked @@ -116,9 +120,9 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen val urlEditor = view.findViewById(R.id.dialog_edit_text_editor) val repoEditor = view.findViewById(R.id.dialog_edit_text2_editor) - repoEditor.setText("stable main") + repoEditor.setText("kali-rolling main") - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(R.string.pref_package_source) .setView(view) .setNegativeButton(android.R.string.no, null) @@ -156,14 +160,16 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen executeAptUpdate() } - private fun executeAptUpdate() = runApt("update") { + private fun executeAptUpdate() = runApt("apt","update", "", autoClose = true) { + Toast.makeText(this, R.string.apt_update_ok, Toast.LENGTH_LONG).show() + it.onSuccess { refreshPackageList() } } - private fun executeAptUpgrade() = runApt("update") { update -> + private fun executeAptUpgrade() = runApt("apt", "update", "", autoClose = true) { update -> update.onSuccess { - runApt("upgrade", "-y") { - it.onSuccess { Toast.makeText(this, R.string.apt_upgrade_ok, Toast.LENGTH_SHORT).show() } + runApt("apt", "upgrade", "-y", autoClose = true) { + it.onSuccess { Toast.makeText(this, R.string.apt_upgrade_ok, Toast.LENGTH_LONG).show() } } } } @@ -190,7 +196,7 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen ): List> { return models .map { - it to StringDistance.distance(mapper(it.packageInfo).toLowerCase(Locale.ROOT), query.toLowerCase(Locale.ROOT)) + it to StringDistance.distance(mapper(it.packageInfo).lowercase(Locale.ENGLISH), query.lowercase(Locale.ENGLISH)) } .sortedWith { l, r -> r.second.compareTo(l.second) } .toList() @@ -199,11 +205,9 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen private fun filter(models: List, query: String): List { val prepared = models.filter { it.packageInfo.packageName!!.contains(query, true) - || it.packageInfo.description!!.contains(query, true) } return sortDistance(prepared, query) { it.packageName!! } - .plus(sortDistance(prepared, query) { it.description!! }) .map { it.first } .toList() } diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/pm/model.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/pm/model.kt index a849965..01f927f 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/pm/model.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/pm/model.kt @@ -14,12 +14,12 @@ import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView class PackageAdapter( context: Context, comparator: Comparator, - private val listener: PackageAdapter.Listener + private val listener: Listener ) : SortedListAdapter(context, PackageModel::class.java, comparator), FastScrollRecyclerView.SectionedAdapter { override fun getSectionName(position: Int): String { - return getItem(position).packageInfo.packageName?.substring(0, 1) ?: "#" + return getItem(position).packageInfo.packageName!!.substring(0, 1) ?: "#" } interface Listener { @@ -53,14 +53,14 @@ class PackageViewHolder(private val rootView: View, private val listener: Packag */ class PackageModel(val packageInfo: NeoPackageInfo) : SortedListAdapter.ViewModel { - override fun isSameModelAs(t: T): Boolean { + override fun isSameModelAs(t: T): Boolean { if (t is PackageModel) { return t.packageInfo.packageName == packageInfo.packageName } return false } - override fun isContentTheSameAs(t: T): Boolean { + override fun isContentTheSameAs(t: T): Boolean { return isSameModelAs(t) } diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/pm/view/RecyclerTabLayout.java b/nhterm/src/main/java/com/offsec/nhterm/ui/pm/view/RecyclerTabLayout.java index 145539b..987a10a 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/pm/view/RecyclerTabLayout.java +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/pm/view/RecyclerTabLayout.java @@ -36,6 +36,8 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager.widget.ViewPager; import com.offsec.nhterm.R; +import java.util.Objects; + public class RecyclerTabLayout extends RecyclerView { protected static final long DEFAULT_SCROLL_DURATION = 200; @@ -397,6 +399,7 @@ public class RecyclerTabLayout extends RecyclerView { int center = mRecyclerTabLayout.getWidth() / 2; for (int position = first; position <= last; position++) { View view = mLinearLayoutManager.findViewByPosition(position); + assert view != null; if (view.getLeft() + view.getWidth() >= center) { mRecyclerTabLayout.setCurrentItem(position, false); break; @@ -535,14 +538,14 @@ public class RecyclerTabLayout extends RecyclerView { @Override public void onBindViewHolder(DefaultAdapter.ViewHolder holder, int position) { - CharSequence title = getViewPager().getAdapter().getPageTitle(position); + CharSequence title = Objects.requireNonNull(getViewPager().getAdapter()).getPageTitle(position); holder.title.setText(title); holder.title.setSelected(getCurrentIndicatorPosition() == position); } @Override public int getItemCount() { - return getViewPager().getAdapter().getCount(); + return Objects.requireNonNull(getViewPager().getAdapter()).getCount(); } public void setTabPadding(int tabPaddingStart, int tabPaddingTop, int tabPaddingEnd, diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/settings/BasePreferenceActivity.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/settings/BasePreferenceActivity.kt index bfb6a82..899aa9b 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/settings/BasePreferenceActivity.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/settings/BasePreferenceActivity.kt @@ -16,14 +16,20 @@ package com.offsec.nhterm.ui.settings import android.content.res.Configuration +import android.content.res.Resources.Theme import android.os.Bundle import android.preference.PreferenceActivity import android.view.MenuInflater import android.view.View import android.view.ViewGroup import androidx.annotation.LayoutRes +import androidx.annotation.StyleRes import androidx.appcompat.app.ActionBar import androidx.appcompat.app.AppCompatDelegate +import androidx.core.content.res.ResourcesCompat.ThemeCompat +import androidx.preference.Preference +import androidx.preference.PreferenceScreen +import com.offsec.nhterm.R /** * A [android.preference.PreferenceActivity] which implements and proxies the necessary calls @@ -39,6 +45,7 @@ abstract class BasePreferenceActivity : PreferenceActivity() { override fun onCreate(savedInstanceState: Bundle?) { delegate.installViewFactory() delegate.onCreate(savedInstanceState) + delegate.setTheme(R.style.AppTheme) super.onCreate(savedInstanceState) } diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/settings/GeneralSettingsActivity.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/settings/GeneralSettingsActivity.kt index 01b3c1d..81ac909 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/settings/GeneralSettingsActivity.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/settings/GeneralSettingsActivity.kt @@ -1,7 +1,9 @@ package com.offsec.nhterm.ui.settings import android.os.Bundle +import android.preference.PreferenceActivity import android.view.MenuItem +import androidx.preference.Preference import com.offsec.nhterm.R /** @@ -16,7 +18,7 @@ class GeneralSettingsActivity : BasePreferenceActivity() { addPreferencesFromResource(R.xml.setting_general) } - override fun onBuildHeaders(target: MutableList
?) { + override fun onBuildHeaders(target: MutableList?) { } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/settings/SettingActivity.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/settings/SettingActivity.kt index 299da40..f78c555 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/settings/SettingActivity.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/settings/SettingActivity.kt @@ -1,5 +1,6 @@ package com.offsec.nhterm.ui.settings +import android.os.Build import android.os.Bundle import android.view.MenuItem import com.offsec.nhterm.R @@ -13,7 +14,11 @@ class SettingActivity : BasePreferenceActivity() { super.onCreate(savedInstanceState) supportActionBar?.title = getString(R.string.settings) supportActionBar?.setDisplayHomeAsUpEnabled(true) - addPreferencesFromResource(R.xml.settings_main) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + addPreferencesFromResource(R.xml.settings_main) + } else { + addPreferencesFromResource(R.xml.older_settings_main) + } } override fun onBuildHeaders(target: MutableList
?) { diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/term/NeoTermActivity.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/term/NeoTermActivity.kt index 6db1e04..0f64072 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/term/NeoTermActivity.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/term/NeoTermActivity.kt @@ -9,6 +9,7 @@ import android.os.Build import android.os.Bundle import android.os.Handler import android.os.IBinder +import android.util.Log import android.view.* import android.view.inputmethod.InputMethodManager import android.widget.ImageButton @@ -21,8 +22,8 @@ import androidx.core.content.ContextCompat import androidx.core.view.OnApplyWindowInsetsListener import androidx.core.view.ViewCompat import androidx.preference.PreferenceManager +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.offsec.nhterm.App -import com.offsec.nhterm.BuildConfig import com.offsec.nhterm.R import com.offsec.nhterm.backend.TerminalSession import com.offsec.nhterm.component.ComponentManager @@ -34,14 +35,18 @@ import com.offsec.nhterm.component.session.XParameter import com.offsec.nhterm.component.session.XSession import com.offsec.nhterm.frontend.session.terminal.* import com.offsec.nhterm.services.NeoTermService +import com.offsec.nhterm.ui.pm.PackageManagerActivity import com.offsec.nhterm.ui.settings.SettingActivity import com.offsec.nhterm.utils.FullScreenHelper import com.offsec.nhterm.utils.NeoPermission import com.offsec.nhterm.utils.RangedInt +import com.topjohnwu.superuser.Shell import de.mrapp.android.tabswitcher.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import java.lang.System.`in` +import java.lang.System.out class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreferences.OnSharedPreferenceChangeListener { @@ -60,8 +65,18 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val SDCARD_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1 + NeoPermission.initAppPermission(this, NeoPermission.REQUEST_APP_PERMISSION) + if (ContextCompat.checkSelfPermission( + this, + Manifest.permission.READ_EXTERNAL_STORAGE, + ) != PackageManager.PERMISSION_GRANTED + ) { + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), SDCARD_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE) + } + val fullscreen = NeoPreference.isFullScreenEnabled() if (fullscreen) { window.setFlags( @@ -70,19 +85,6 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference ) } - val SDCARD_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1 - if (ContextCompat.checkSelfPermission( - this, - Manifest.permission.WRITE_EXTERNAL_STORAGE, - ) != PackageManager.PERMISSION_GRANTED - ) { - ActivityCompat.requestPermissions( - this, - arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), - SDCARD_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE, - ) - } - setContentView(R.layout.ui_main) toolbar = findViewById(R.id.terminal_toolbar) @@ -142,7 +144,11 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference } override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.menu_main, menu) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + menuInflater.inflate(R.menu.menu_main, menu) + } else { + menuInflater.inflate(R.menu.older_menu_main, menu) + } TabSwitcher.setupWithMenu( tabSwitcher, toolbar.menu, @@ -169,15 +175,23 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference true } R.id.menu_item_new_session -> { - addNewNetHunterSession("KALI LINUX") + addNewNetHunterSession("Kali Shell") true } - R.id.menu_item_new_system_session -> { - addNewAndroidSession("Android") + R.id.menu_item_new_emergency_session -> { + addNewEmergencySession("Emergency Shell") + true + } + R.id.menu_item_new_bash_session -> { + addNewAndroidSession("Android Shell") true } R.id.menu_item_new_root_session -> { - addNewRootSession("Android SU") + addNewRootSession("Root Shell") + true + } + R.id.menu_item_package_settings -> { + startActivity(Intent(this, PackageManagerActivity::class.java)) true } else -> item?.let { super.onOptionsItemSelected(it) } @@ -233,6 +247,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference } }, ) + val tab = tabSwitcher.selectedTab as NeoTab? tab?.onResume() } @@ -241,7 +256,9 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference super.onStart() EventBus.getDefault().register(this) val tab = tabSwitcher.selectedTab as NeoTab? - tab?.onStart() + if (tab != null) { + tab.onStart() + } } override fun onStop() { @@ -303,7 +320,8 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED ) { - AlertDialog.Builder(this).setMessage(R.string.permission_denied) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) + .setMessage(R.string.permission_denied) .setPositiveButton( android.R.string.ok, { _: DialogInterface, _: Int -> @@ -459,7 +477,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference val profilesShell = profiles.filterIsInstance() if (profiles.isEmpty()) { - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(R.string.error) .setMessage(R.string.no_profile_available) .setPositiveButton(android.R.string.yes, null) @@ -467,7 +485,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference return } - AlertDialog.Builder(this) + MaterialAlertDialogBuilder(this, R.style.DialogStyle) .setTitle(R.string.new_session_with_profile) .setItems( profiles.map { it.profileName }.toTypedArray(), @@ -481,11 +499,11 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference } private fun addNewSession() { - addNewNetHunterSession("KALI LINUX") + addNewNetHunterSession("Kali Shell") } private fun addNewSession(sessionName: String?, systemShell: Boolean, animation: Animation) { - addNewNetHunterSession("KALI LINUX") + addNewNetHunterSession("Kali Shell") } private fun addNewSessionWithProfile(profile: ShellProfile) { @@ -511,7 +529,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference .profile(profile) val session = termService!!.createTermSession(parameter) - session.mSessionName = sessionName ?: generateSessionName("Kali Linux") + session.mSessionName = sessionName ?: generateSessionName("Kali Shell") val tab = createTab(session.mSessionName) as TermTab tab.termData.initializeSessionWith(session, sessionCallback, viewClient) @@ -520,6 +538,27 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference switchToSession(tab) } + @SuppressLint("SdCardPath") + private fun addNewEmergencySession(sessionName: String?) { + val sessionCallback = TermSessionCallback() + val viewClient = TermViewClient(this) + + val parameter = ShellParameter() + .callback(sessionCallback) + .executablePath("/system/bin/sh") + .systemShell(true) + + val session = termService!!.createTermSession(parameter) + + session.mSessionName = sessionName ?: generateSessionName("Emergency Shell") + + val tab = createTab(session.mSessionName) as TermTab + tab.termData.initializeSessionWith(session, sessionCallback, viewClient) + + addNewTab(tab, createRevealAnimation()) + switchToSession(tab) + } + @SuppressLint("SdCardPath") private fun addNewAndroidSession(sessionName: String?) { val sessionCallback = TermSessionCallback() @@ -532,7 +571,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference val session = termService!!.createTermSession(parameter) - session.mSessionName = sessionName ?: generateSessionName("Android") + session.mSessionName = sessionName ?: generateSessionName("Android Shell") val tab = createTab(session.mSessionName) as TermTab tab.termData.initializeSessionWith(session, sessionCallback, viewClient) @@ -550,7 +589,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference .executablePath("/data/data/com.offsec.nhterm/files/usr/bin/kali") val session = termService!!.createTermSession(parameter) - session.mSessionName = sessionName ?: generateSessionName("KALI LINUX") + session.mSessionName = sessionName ?: generateSessionName("Kali Shell") val tab = createTab(session.mSessionName) as TermTab tab.termData.initializeSessionWith(session, sessionCallback, viewClient) @@ -572,7 +611,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference val session = termService!!.createTermSession(parameter) generateSessionName("Android") - session.mSessionName = sessionName ?: generateSessionName("ANDROID SU") + session.mSessionName = sessionName ?: generateSessionName("Root Shell") val tab = createTab(session.mSessionName) as TermTab tab.termData.initializeSessionWith(session, sessionCallback, viewClient) @@ -605,15 +644,6 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference } private fun addXSession() { - if (!BuildConfig.DEBUG) { - AlertDialog.Builder(this) - .setTitle(R.string.error) - .setMessage(R.string.sorry_for_development) - .setPositiveButton(android.R.string.yes, null) - .show() - return - } - if (!tabSwitcher.isSwitcherShown) { toggleSwitcher(showSwitcher = true, easterEgg = false) } @@ -700,7 +730,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference } private fun createXTab(tabTitle: String?): Tab { - return postTabCreated(XSessionTab(tabTitle ?: "Kali Linux")) + return postTabCreated(XSessionTab(tabTitle ?: "Kali Shell")) } private fun postTabCreated(tab: T): T { diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/term/NeoTermRemoteInterface.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/term/NeoTermRemoteInterface.kt index 291fb85..8411eb9 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/term/NeoTermRemoteInterface.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/term/NeoTermRemoteInterface.kt @@ -10,6 +10,7 @@ import android.widget.ArrayAdapter import android.widget.ListView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.offsec.nhterm.App import com.offsec.nhterm.R import com.offsec.nhterm.bridge.Bridge.* @@ -161,7 +162,7 @@ class NeoTermRemoteInterface : AppCompatActivity(), ServiceConnection { val filesAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, filesToHandle) filesList.adapter = filesAdapter filesList.setOnItemClickListener { _, _, position, _ -> - AlertDialog.Builder(this@NeoTermRemoteInterface) + MaterialAlertDialogBuilder(this@NeoTermRemoteInterface, R.style.DialogStyle) .setMessage(R.string.confirm_remove_file_from_list) .setPositiveButton(android.R.string.yes) { _, _ -> filesToHandle.removeAt(position) diff --git a/nhterm/src/main/java/com/offsec/nhterm/ui/term/tabs.kt b/nhterm/src/main/java/com/offsec/nhterm/ui/term/tabs.kt index 3572044..9883375 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/ui/term/tabs.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/ui/term/tabs.kt @@ -3,6 +3,7 @@ package com.offsec.nhterm.ui.term import android.content.Context import android.content.res.Configuration import android.graphics.Rect +import android.os.Build import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -60,10 +61,12 @@ class NeoTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() { Terminals.setupExtraKeysView(extraKeysView) val colorSchemeManager = ComponentManager.getComponent() - colorSchemeManager.applyColorScheme( - terminalView, extraKeysView, - colorSchemeManager.getCurrentColorScheme() - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + colorSchemeManager.applyColorScheme( + terminalView, extraKeysView, + colorSchemeManager.getCurrentColorScheme() + ) + } view } diff --git a/nhterm/src/main/java/com/offsec/nhterm/utils/NeoPermission.kt b/nhterm/src/main/java/com/offsec/nhterm/utils/NeoPermission.kt index 0570e34..49cd8fb 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/utils/NeoPermission.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/utils/NeoPermission.kt @@ -4,10 +4,13 @@ import android.Manifest import android.content.ActivityNotFoundException import android.content.DialogInterface import android.content.pm.PackageManager +import android.os.Build import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.offsec.nhterm.R /** * @author kiva @@ -16,39 +19,35 @@ object NeoPermission { const val REQUEST_APP_PERMISSION = 10086 fun initAppPermission(context: AppCompatActivity, requestCode: Int) { - if (ContextCompat.checkSelfPermission( - context, - Manifest.permission.READ_EXTERNAL_STORAGE - ) - != PackageManager.PERMISSION_GRANTED - ) { - - if (ActivityCompat.shouldShowRequestPermissionRationale( + if (ContextCompat.checkSelfPermission( context, Manifest.permission.READ_EXTERNAL_STORAGE ) - ) { - AlertDialog.Builder(context).setMessage("需要存储权限来访问存储设备上的文件") - .setPositiveButton(android.R.string.ok, { _: DialogInterface, _: Int -> - doRequestPermission(context, requestCode) - }) - .show() + != PackageManager.PERMISSION_GRANTED) { - } else { - doRequestPermission(context, requestCode) + if (ActivityCompat.shouldShowRequestPermissionRationale( + context, + Manifest.permission.READ_EXTERNAL_STORAGE + )) { + MaterialAlertDialogBuilder(context, R.style.DialogStyle).setMessage("Please enable Storage permission") + .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> + doRequestPermission(context, requestCode) + }.show() + + } else { + doRequestPermission(context, requestCode) + } } } } private fun doRequestPermission(context: AppCompatActivity, requestCode: Int) { try { - ActivityCompat.requestPermissions( - context, - arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), - requestCode - ) - } catch (ignore: ActivityNotFoundException) { - // for MIUI, we ignore it. - } + ActivityCompat.requestPermissions( + context, + arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), + requestCode) + } catch (ignore: ActivityNotFoundException) { + // for MIUI, we ignore it. } } diff --git a/nhterm/src/main/java/com/offsec/nhterm/utils/utils.kt b/nhterm/src/main/java/com/offsec/nhterm/utils/utils.kt index bb8676d..ac087da 100644 --- a/nhterm/src/main/java/com/offsec/nhterm/utils/utils.kt +++ b/nhterm/src/main/java/com/offsec/nhterm/utils/utils.kt @@ -3,6 +3,7 @@ package com.offsec.nhterm.utils import android.content.ContentUris import android.content.Context import android.net.Uri +import android.os.Build import android.os.Environment import android.provider.DocumentsContract import android.provider.MediaStore @@ -37,25 +38,41 @@ fun Long.formatSizeInKB(): String { } fun Context.extractAssetsDir(assetDir: String, extractDir: String) = kotlin.runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val targetDir = Paths.get(extractDir) - Files.createDirectories(targetDir) - val assets = this.assets - assets.list(assetDir)?.let { - it.map { targetDir.resolve(it) } - .takeWhile { !Files.exists(it) } - .forEach { targetPath -> - assets.open("$assetDir/${targetPath.fileName}").use { - Files.copy(it, targetPath) + Paths.get(extractDir) + Files.createDirectories(targetDir) + val assets = this.assets + assets.list(assetDir)?.let { + it.map { targetDir.resolve(it) } + .takeWhile { !Files.exists(it) } + .forEach { targetPath -> + assets.open("$assetDir/${targetPath.fileName}").use { + Files.copy(it, targetPath) + } } - } + } + } else { + val targetDir = com.llamalab.safs.Paths.get(extractDir) + com.llamalab.safs.Files.createDirectories(targetDir) + val assets = this.assets + assets.list(assetDir)?.let { + it.map { targetDir.resolve(it) } + .takeWhile { !com.llamalab.safs.Files.exists(it) } + .forEach { targetPath -> + assets.open("$assetDir/${targetPath.fileName}").use { + com.llamalab.safs.Files.copy(it, targetPath) + } + } + } } } fun Context.runApt( - subCommand: String, vararg extraArgs: String, + command: String, subCommand: String, extraArgs: String, autoClose: Boolean = true, block: (Result) -> Unit ) = TerminalDialog(this) - .execute(NeoTermPath.APT_BIN_PATH, arrayOf(NeoTermPath.APT_BIN_PATH, subCommand, *extraArgs)) + .execute(NeoTermPath.BIN_PATH + "/kali", command + " " + subCommand, extraArgs) .imeEnabled(true) .onFinish { dialog, session -> val exit = session?.exitStatus ?: 1 diff --git a/nhterm/src/main/res/drawable/ic_launcher_background.xml b/nhterm/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..e009ebe --- /dev/null +++ b/nhterm/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nhterm/src/main/res/layout/item_package.xml b/nhterm/src/main/res/layout/item_package.xml index b5fa57e..2bebfc3 100644 --- a/nhterm/src/main/res/layout/item_package.xml +++ b/nhterm/src/main/res/layout/item_package.xml @@ -9,7 +9,7 @@ android:orientation="vertical" android:padding="@dimen/package_item_padding"> - - - \ No newline at end of file + diff --git a/nhterm/src/main/res/layout/ui_main.xml b/nhterm/src/main/res/layout/ui_main.xml index 11ceb02..a0ce6d5 100644 --- a/nhterm/src/main/res/layout/ui_main.xml +++ b/nhterm/src/main/res/layout/ui_main.xml @@ -6,15 +6,14 @@ android:background="@color/terminal_background" android:orientation="vertical"> - - - + android:layout_below="@id/pm_tab_header"> + diff --git a/nhterm/src/main/res/layout/ui_term_dialog.xml b/nhterm/src/main/res/layout/ui_term_dialog.xml index 036582c..9d05a02 100644 --- a/nhterm/src/main/res/layout/ui_term_dialog.xml +++ b/nhterm/src/main/res/layout/ui_term_dialog.xml @@ -4,10 +4,13 @@ android:layout_height="match_parent"> + + + - \ No newline at end of file + diff --git a/nhterm/src/main/res/menu/older_menu_main.xml b/nhterm/src/main/res/menu/older_menu_main.xml new file mode 100644 index 0000000..d3f2f10 --- /dev/null +++ b/nhterm/src/main/res/menu/older_menu_main.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/nhterm/src/main/res/mipmap-hdpi/ic_launcher.png b/nhterm/src/main/res/mipmap-hdpi/ic_launcher.png index 9547f8a..d46bc0c 100644 Binary files a/nhterm/src/main/res/mipmap-hdpi/ic_launcher.png and b/nhterm/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/nhterm/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/nhterm/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index 08598a3..3337c40 100644 Binary files a/nhterm/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/nhterm/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/nhterm/src/main/res/mipmap-hdpi/ic_launcher_round.png b/nhterm/src/main/res/mipmap-hdpi/ic_launcher_round.png index aa68fe6..f3ea4ca 100644 Binary files a/nhterm/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/nhterm/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/nhterm/src/main/res/mipmap-mdpi/ic_launcher.png b/nhterm/src/main/res/mipmap-mdpi/ic_launcher.png index 98719c0..1918731 100644 Binary files a/nhterm/src/main/res/mipmap-mdpi/ic_launcher.png and b/nhterm/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/nhterm/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/nhterm/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index b3af75e..f4b7d0d 100644 Binary files a/nhterm/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/nhterm/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/nhterm/src/main/res/mipmap-mdpi/ic_launcher_round.png b/nhterm/src/main/res/mipmap-mdpi/ic_launcher_round.png index 0ef5a4f..9d1c1ad 100644 Binary files a/nhterm/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/nhterm/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/nhterm/src/main/res/mipmap-xhdpi/ic_launcher.png b/nhterm/src/main/res/mipmap-xhdpi/ic_launcher.png index 004a73b..93e9734 100644 Binary files a/nhterm/src/main/res/mipmap-xhdpi/ic_launcher.png and b/nhterm/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/nhterm/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/nhterm/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index aee36dc..165ff61 100644 Binary files a/nhterm/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/nhterm/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/nhterm/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/nhterm/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 55afe34..6ac8787 100644 Binary files a/nhterm/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/nhterm/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher.png b/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher.png index 1ab2299..0987e91 100644 Binary files a/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index 01a91d8..e2e7a4c 100644 Binary files a/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index f62847d..c8b0600 100644 Binary files a/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/nhterm/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 8a51ad8..8ae7b33 100644 Binary files a/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 7b7faa4..5b0745d 100644 Binary files a/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 812c61d..e0d579a 100644 Binary files a/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/nhterm/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/nhterm/src/main/res/values/colors.xml b/nhterm/src/main/res/values/colors.xml index 8702830..837b84e 100644 --- a/nhterm/src/main/res/values/colors.xml +++ b/nhterm/src/main/res/values/colors.xml @@ -10,4 +10,10 @@ #AE000000 #efefef #363636 + + #202124 + #131415 + #A8D8D8D8 + + #A88B9FBA diff --git a/nhterm/src/main/res/values/dimens.xml b/nhterm/src/main/res/values/dimens.xml index 7122697..30b14d7 100644 --- a/nhterm/src/main/res/values/dimens.xml +++ b/nhterm/src/main/res/values/dimens.xml @@ -9,7 +9,7 @@ 32dp 72dp 36dp - 256dp + 350dp 48dp 36dp 36dp diff --git a/nhterm/src/main/res/values/ic_launcher_background.xml b/nhterm/src/main/res/values/ic_launcher_background.xml index 6feac1c..0c85e35 100644 --- a/nhterm/src/main/res/values/ic_launcher_background.xml +++ b/nhterm/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - #242424 + #595959 \ No newline at end of file diff --git a/nhterm/src/main/res/values/strings.xml b/nhterm/src/main/res/values/strings.xml index 7a3043f..399d155 100644 --- a/nhterm/src/main/res/values/strings.xml +++ b/nhterm/src/main/res/values/strings.xml @@ -8,7 +8,8 @@ Toggle Tabs New Session New Session… - New System Shell + New Emergency Shell + New Bash Shell New Root Shell %d session(s) (Wake Locked) @@ -47,6 +48,7 @@ Package Settings Source, Updates, Upgrades Font, ColorScheme + Disabled as of no required functions available by old android api Customization Toggle Keyboard @@ -61,7 +63,7 @@ APT source changed, you may get it updated by executing: apt update Done Install - Package: %s\nVersion: %s\nDepends: %s\nInstalled Size: %s\nDescription: %s\nHome + Package: %s\n\nVersion: %s\n\nDepends: %s\n\nInstalled Size: %s\n\nDescription: %s\n\nHome Page: %s Package list is empty, please check your source. @@ -185,8 +187,7 @@ Danger Zone This will delete and re-copy required files to boot kali chroot, confirm? - https://example.com/nhterm - + http://http.kali.org/kali sh diff --git a/nhterm/src/main/res/values/styles.xml b/nhterm/src/main/res/values/styles.xml index 4f637b8..cdf6d12 100644 --- a/nhterm/src/main/res/values/styles.xml +++ b/nhterm/src/main/res/values/styles.xml @@ -1,18 +1,17 @@ - - - - - + + + + + + + + + + + + diff --git a/nhterm/src/main/res/xml/older_settings_main.xml b/nhterm/src/main/res/xml/older_settings_main.xml new file mode 100644 index 0000000..be2826f --- /dev/null +++ b/nhterm/src/main/res/xml/older_settings_main.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/nhterm/src/main/res/xml/setting_general.xml b/nhterm/src/main/res/xml/setting_general.xml index 53f03b2..5e2c073 100644 --- a/nhterm/src/main/res/xml/setting_general.xml +++ b/nhterm/src/main/res/xml/setting_general.xml @@ -5,12 +5,6 @@ android:summary="@string/pref_general_initial_command_desc" android:title="@string/pref_general_initial_command"/> - -