Big list of bugfixes and features

* Fixed the issue where some devices didnt like certain chmod option ( caused permission denied for initial scripts )
* Fixed up broken packages with values of null and weird names
  * Issue was how some perl packages had double :: in description
* Overall cahnges for layout where we move forwards for MaterialUI
* Kotlin upgrade 1.5.30 -> 1.6.10
* Removed vibrate functions fully ( no one likes it anyway )
* Updated dependencies
* Half implemented package manager with functions of ( view packages, update and upgrade )
* Root shell now has proper PS1 with a change how shell is started by script ( su -mm -s pathtoshell )
* Bumped api levels
  - min: 21
  - target: 29
  - compile: 30
* Now is hardware accelerated
* Forced script recovery with correct chmod ( Runs on every new launch )
* Settings -> about -> Reset App ( Now uses LibSU module for command execution )
* Android SU session now has nethunter scripts path in PATH export
This commit is contained in:
Martin Valba 2023-09-09 17:57:54 +00:00
parent 07389d7313
commit fe2ae1d22b
48 changed files with 518 additions and 300 deletions

View File

@ -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"
]
@ -35,7 +35,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.android.tools.build:gradle:8.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath rootProject.ext.deps["kotlin-gradle-plugin"]

View File

@ -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);
}
}
}

View File

@ -15,20 +15,63 @@ License.
-->
<resources>
<declare-styleable name="TabSwitcher">
<attr name="android:background"/>
<attr name="layoutPolicy" format="enum">
<enum name="auto" value="0"/>
<enum name="phone" value="1"/>
<enum name="tablet" value="2"/>
<attr name="tabSwitcherThemeGlobal" format="reference"/>
<attr name="tabSwitcherThemePhone" format="reference"/>
<attr name="tabSwitcherThemeTablet" format="reference"/>
<attr name="tabSwitcherBackground" format="reference|color"/>
<attr name="tabSwitcherLayoutPolicy" format="enum">
<enum name="auto" value="1"/>
<enum name="phone" value="2"/>
<enum name="tablet" value="3"/>
</attr>
<attr name="tabIcon" format="reference"/>
<attr name="tabBackgroundColor" format="color"/>
<attr name="tabTitleTextColor" format="color"/>
<attr name="tabCloseButtonIcon" format="reference"/>
<attr name="toolbarTitle" format="string"/>
<attr name="toolbarMenu" format="reference"/>
<attr name="toolbarNavigationIcon" format="reference"/>
</declare-styleable>
<attr name="tabSwitcherTabIcon" format="reference"/>
<attr name="tabSwitcherTabIconTint" format="color"/>
<attr name="tabSwitcherTabBackgroundColor" format="color"/>
<attr name="tabSwitcherTabContentBackgroundColor" format="color"/>
<attr name="tabSwitcherAddTabButtonColor" format="color"/>
<attr name="tabSwitcherTabTitleTextColor" format="color"/>
<attr name="tabSwitcherTabCloseButtonIcon" format="reference"/>
<attr name="tabSwitcherTabCloseButtonIconTint" format="color"/>
<attr name="tabSwitcherTabProgressBarColor" format="color"/>
<attr name="tabSwitcherToolbarTitle" format="string"/>
<attr name="tabSwitcherToolbarMenu" format="reference"/>
<attr name="tabSwitcherToolbarNavigationIcon" format="reference"/>
<attr name="tabSwitcherToolbarNavigationIconTint" format="color"/>
<attr name="tabSwitcherTabToolbarPreviewFadeThreshold" format="integer"/>
<attr name="tabSwitcherTabToolbarPreviewFadeDuration" format="integer"/>
<attr name="tabSwitcherEmptyView" format="reference"/>
<attr name="tabSwitcherEmptyViewAnimationDuration" format="integer"/>
<attr name="tabSwitcherToolbarPopupTheme" format="reference"/>
</resources>
<declare-styleable name="TabSwitcher">
<attr name="android:background"/>
<attr name="preserveState" format="boolean"/>
<attr name="layoutPolicy" format="enum">
<enum name="auto" value="0"/>
<enum name="phone" value="1"/>
<enum name="tablet" value="2"/>
<enum name="tablet_landscape" value="3"/>
</attr>
<attr name="tabIcon" format="reference"/>
<attr name="tabIconTint" format="color"/>
<attr name="tabBackgroundColor" format="color"/>
<attr name="tabContentBackgroundColor" format="color"/>
<attr name="addTabButtonColor" format="color"/>
<attr name="tabTitleTextColor" format="color"/>
<attr name="tabCloseButtonIcon" format="reference"/>
<attr name="tabCloseButtonIconTint" format="color"/>
<attr name="showToolbars" format="boolean"/>
<attr name="toolbarTitle" format="string"/>
<attr name="toolbarMenu" format="reference"/>
<attr name="toolbarNavigationIcon" format="reference"/>
<attr name="toolbarNavigationIconTint" format="color"/>
<attr name="tabPreviewFadeThreshold" format="integer"/>
<attr name="tabPreviewFadeDuration" format="integer"/>
<attr name="emptyView" format="reference"/>
<attr name="emptyViewAnimationDuration" format="integer"/>
<attr name="themeGlobal" format="reference"/>
<attr name="themePhone" format="reference"/>
<attr name="themeTablet" format="reference"/>
</declare-styleable>
</resources>

View File

@ -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

View File

@ -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

0
gradlew vendored Executable file → Normal file
View File

View File

@ -15,7 +15,7 @@ android {
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
abiFilters 'arm64-v8a', 'armeabi-v7a'
}
}
signingConfigs {
@ -63,18 +63,22 @@ 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"
implementation project(':chrome-tabs')
implementation project(':NeoLang')

View File

@ -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"

View File

@ -2,14 +2,14 @@
# Version: 1.0.3
# export path for android bins/tools
export PATH=/data/data/com.offsec.nhterm/files/home/.nhterm/script:/data/data/com.offsec.nhterm/files/usr/bin:/data/data/com.offsec.nhterm/files/usr/sbin:/sbin:/system/bin:/system/xbin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/odm/bin:/vendor/bin:.
export PATH="/data/data/com.offsec.nethunter/files/scripts:/data/data/com.offsec.nethunter/files/scripts/bin:/data/data/com.offsec.nhterm/files/home/.nhterm/script:/data/data/com.offsec.nhterm/files/usr/bin:/data/data/com.offsec.nhterm/files/usr/sbin:/sbin:/system/bin:/system/xbin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/odm/bin:/vendor/bin:."
# Remove some exports that break default android binaries from running
unset LD_LIBRARY_PATH
unset LD_PRELOAD
export PS1="\\[\\e[1;32m\\]\\u [ \\[\\e[0m\\]\\w\\[\\e[1;32m\\] ]\$ \\[\\e[0m\\]"
# Find and remember su location
SU1=$(which su)
SU2="$SU1 -mm -c $@"
SU2="$SU1 -mm -s $@"
# clear out old view
clear

View File

@ -0,0 +1,3 @@
// Switching eks profiles
* rename current default to something else
* and now rename the chosen profile to default.nm ( to apply restart the terminal )

View File

@ -1,57 +0,0 @@
#!/data/data/io.neoterm/files/usr/bin/bash
function file_suffix() {
echo "${1##*.}"
}
function detect_program() {
case "$1" in
*.tar.* | *.tar ) echo "tar xvf %s" ;;
*.7z ) echo "7za x %s" ;;
*.rar ) echo "unrar x %s" ;;
*.zip ) echo "unzip %s" ;;
* ) echo "" ;;
esac
}
function do_extract() {
local file="$1"
local dir="$(dirname $file)"
if [[ ! -f "$file" ]]; then
echo "$file: no such file or directory"
return 1
fi
local program="$(detect_program $file)"
if [[ "$program" == "" ]]; then
echo "Unsupported format: $(file_suffix $file)"
return 1
fi
local command="$(printf "$program" "$file")"
if [[ ! -w "$dir" || ! -r "$file" ]]; then
command="sudo $command"
fi
cd "$dir" || {
echo "Failed to cd: $dir"
return 1
}
eval "$command"
}
if [[ "$#" == 0 ]]; then
echo "You must specific at least a file to extract."
exit 1
fi
clear
while [[ "$#" != 0 ]]; do
file="$1"; shift
echo "[Extracting] $(basename $file)"
do_extract "$file"
done

View File

@ -0,0 +1,5 @@
#!/data/data/io.neoterm/files/usr/bin/bash
set -e
bash

View File

@ -1,12 +0,0 @@
#!/data/data/io.neoterm/files/usr/bin/bash
set -e
VIM="$(which vim)"
if [[ "$VIM"x == ""x ]]; then
echo "Vim is not installed, now installing..."
apt update && apt install -y vim
fi
$VIM "$@"

View File

@ -7,11 +7,13 @@ import android.content.Intent
import android.net.Uri
import android.view.Gravity
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.offsec.nhterm.component.NeoInitializer
import com.offsec.nhterm.component.config.NeoPreference
import com.offsec.nhterm.ui.other.BonusActivity
import com.offsec.nhterm.utils.CrashHandler
import com.offsec.nhterm.utils.NeoPermission
import com.topjohnwu.superuser.Shell
/**
* @author kiva
@ -21,6 +23,8 @@ class App : Application() {
super.onCreate()
app = this
Shell.cmd("setenforce 0").exec()
NeoPreference.init(this)
CrashHandler.init()
NeoInitializer.init(this)
@ -31,7 +35,7 @@ class App : Application() {
}
fun errorDialog(context: Context, message: String, dismissCallback: (() -> Unit)?) {
AlertDialog.Builder(context)
MaterialAlertDialogBuilder(context, R.style.DialogStyleCompat)
.setTitle(R.string.error)
.setMessage(message)
.setNegativeButton(android.R.string.no, null)

View File

@ -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<String, Integer> 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') : "=";
}

View File

@ -226,13 +226,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,

View File

@ -6,7 +6,7 @@ object DefaultValues {
const val fontSize = 30
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

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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<NeoPackageInfo>? = 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"
}

View File

@ -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<File> {
// Workaround to get things running
// TODO: ( APT ) Make it prettier?
copySourceFromChroot()
val sourceManager = ComponentManager.getComponent<PackageComponent>().sourceManager
val sourceFiles = ArrayList<File>()
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
}

View File

@ -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)

View File

@ -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<UserScript>()
var binFiles = listOf<UserScript>()
private val scriptDir = File(NeoTermPath.USER_SCRIPT_PATH)
private 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()
}
}

View File

@ -8,6 +8,7 @@ import android.view.View
import androidx.appcompat.app.AlertDialog
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,7 +37,7 @@ class TerminalDialog(val context: Context) {
}
}
fun execute(executablePath: String, arguments: Array<String>?): TerminalDialog {
fun execute(executablePath: String, arguments: String, extraarg: String): TerminalDialog {
if (terminalSession != null) {
terminalSession?.finishIfRunning()
}
@ -49,15 +50,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 +112,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)
}

View File

@ -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
}

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -51,6 +51,8 @@ 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) {
@ -103,6 +105,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 +218,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)
}

View File

@ -73,6 +73,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 +98,8 @@ 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
"" -> chars = "-"
// Function keys

View File

@ -1,9 +1,11 @@
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
@ -17,9 +19,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
@ -163,8 +169,8 @@ class AboutActivity : AppCompatActivity() {
findViewById<View>(R.id.about_reset_app_view).setOnClickListener {
AlertDialog.Builder(this)
.setMessage(R.string.reset_app_warning)
.setPositiveButton(R.string.yes) { _, _ ->
resetApp()
.setPositiveButton("yes") { _, _ ->
resetApp(this)
resetisdone()
}
.setNegativeButton(android.R.string.no, null)
@ -175,39 +181,27 @@ class AboutActivity : AppCompatActivity() {
private fun resetisdone() {
AlertDialog.Builder(this)
.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) {

View File

@ -228,11 +228,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() }
}
}

View File

@ -21,6 +21,8 @@ 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 +31,14 @@ import java.util.*
class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListener, SortedListAdapter.Callback {
private val comparator = SortedListAdapter.ComparatorBuilder<PackageModel>()
.setOrderForModel<PackageModel>(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<PackageModel>()
private lateinit var recyclerView: androidx.recyclerview.widget.RecyclerView
private lateinit var adapter: PackageAdapter
private var models = listOf<PackageModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -63,11 +65,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 +84,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() {
@ -116,7 +119,7 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
val urlEditor = view.findViewById<EditText>(R.id.dialog_edit_text_editor)
val repoEditor = view.findViewById<EditText>(R.id.dialog_edit_text2_editor)
repoEditor.setText("stable main")
repoEditor.setText("kali-rolling main")
AlertDialog.Builder(this)
.setTitle(R.string.pref_package_source)
@ -156,14 +159,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 +195,7 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
): List<Pair<PackageModel, Int>> {
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 +204,9 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
private fun filter(models: List<PackageModel>, query: String): List<PackageModel> {
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()
}

View File

@ -14,12 +14,12 @@ import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
class PackageAdapter(
context: Context,
comparator: Comparator<PackageModel>,
private val listener: PackageAdapter.Listener
private val listener: Listener
) : SortedListAdapter<PackageModel>(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 <T> isSameModelAs(t: T): Boolean {
override fun <T : Any> isSameModelAs(t: T): Boolean {
if (t is PackageModel) {
return t.packageInfo.packageName == packageInfo.packageName
}
return false
}
override fun <T> isContentTheSameAs(t: T): Boolean {
override fun <T : Any> isContentTheSameAs(t: T): Boolean {
return isSameModelAs(t)
}

View File

@ -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,

View File

@ -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
@ -22,7 +23,6 @@ import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.preference.PreferenceManager
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 +34,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 {
@ -73,12 +77,12 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
val SDCARD_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE,
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
arrayOf(Manifest.permission.MANAGE_EXTERNAL_STORAGE),
SDCARD_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE,
)
}
@ -180,6 +184,10 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
addNewRootSession("Android SU")
true
}
R.id.menu_item_package_settings -> {
startActivity(Intent(this, PackageManagerActivity::class.java))
true
}
else -> item?.let { super.onOptionsItemSelected(it) }
}
}
@ -233,6 +241,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
}
},
)
val tab = tabSwitcher.selectedTab as NeoTab?
tab?.onResume()
}
@ -241,7 +250,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() {
@ -605,15 +616,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)
}

View File

@ -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
@ -28,10 +31,10 @@ object NeoPermission {
Manifest.permission.READ_EXTERNAL_STORAGE
)
) {
AlertDialog.Builder(context).setMessage("需要存储权限来访问存储设备上的文件")
.setPositiveButton(android.R.string.ok, { _: DialogInterface, _: Int ->
MaterialAlertDialogBuilder(context, R.style.DialogStyleCompat).setMessage("Please enable Storage permission")
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
doRequestPermission(context, requestCode)
})
}
.show()
} else {
@ -42,11 +45,17 @@ object NeoPermission {
private fun doRequestPermission(context: AppCompatActivity, requestCode: Int) {
try {
ActivityCompat.requestPermissions(
context,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
requestCode
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ActivityCompat.requestPermissions(
context,
arrayOf(Manifest.permission.MANAGE_EXTERNAL_STORAGE),
requestCode)
} else {
ActivityCompat.requestPermissions(
context,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE),
requestCode)
}
} catch (ignore: ActivityNotFoundException) {
// for MIUI, we ignore it.
}

View File

@ -52,10 +52,10 @@ fun Context.extractAssetsDir(assetDir: String, extractDir: String) = kotlin.runC
}
fun Context.runApt(
subCommand: String, vararg extraArgs: String,
command: String, subCommand: String, extraArgs: String,
autoClose: Boolean = true, block: (Result<TerminalDialog>) -> 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

View File

@ -9,7 +9,7 @@
android:orientation="vertical"
android:padding="@dimen/package_item_padding">
<TextView
<com.google.android.material.textview.MaterialTextView
android:id="@+id/package_item_name"
style="@style/TextAppearance.AppCompat.Large"
android:layout_width="match_parent"
@ -18,7 +18,7 @@
android:textColor="@color/textColor"
tools:text="Package Name"/>
<TextView
<com.google.android.material.textview.MaterialTextView
android:id="@+id/package_item_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -27,4 +27,4 @@
android:textColor="@color/textColorSecondary"
tools:text="Package Description"/>
</LinearLayout>
</LinearLayout>

View File

@ -27,10 +27,11 @@
android:background="@color/colorPrimary"/>
</LinearLayout>
<androidx.viewpager.widget.ViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pm_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/pm_tab_header"/>
android:layout_below="@id/pm_tab_header">
</androidx.viewpager2.widget.ViewPager2>
</RelativeLayout>

View File

@ -4,10 +4,13 @@
android:layout_height="match_parent">
<com.offsec.nhterm.frontend.session.view.TerminalView
android:layout_margin="@dimen/text_margin"
android:id="@+id/terminal_view_dialog"
android:layout_width="match_parent"
android:layout_height="@dimen/terminal_dialog_height"
android:layout_marginStart="@dimen/text_margin"
android:layout_marginTop="@dimen/text_margin"
android:layout_marginEnd="@dimen/text_margin"
android:layout_marginBottom="@dimen/text_margin"
android:background="@color/terminal_background"
android:fadeScrollbars="true"
android:focusable="false"

View File

@ -29,6 +29,11 @@
</menu>
</item>
<item
android:id="@+id/menu_item_package_settings"
android:title="@string/package_settings"
app:showAsAction="never"/>
<item
android:id="@+id/menu_item_settings"
android:title="@string/settings"

View File

@ -27,6 +27,7 @@
<item
android:id="@+id/action_source"
android:title="@string/pref_package_source"
android:enabled="false"
app:showAsAction="never"/>
</menu>
</menu>

View File

@ -10,4 +10,10 @@
<color name="popup_background">#AE000000</color>
<color name="popup_split_background">#efefef</color>
<color name="list_download_item_color_dark">#363636</color>
<color name="tab_switcher_background_color">#202124</color>
<color name="tab_background_color">#131415</color>
<color name="tab_title_text_color">#A8D8D8D8</color>
<color name="button_color">#A88B9FBA</color>
</resources>

View File

@ -9,7 +9,7 @@
<dimen name="eks_height">32dp</dimen>
<dimen name="eks_height_two_line">72dp</dimen>
<dimen name="eks_height_one_line">36dp</dimen>
<dimen name="terminal_dialog_height">256dp</dimen>
<dimen name="terminal_dialog_height">350dp</dimen>
<dimen name="custom_editor_line_height">48dp</dimen>
<dimen name="custom_install_icon_width">36dp</dimen>
<dimen name="custom_install_icon_height">36dp</dimen>

View File

@ -185,8 +185,7 @@
<string name="dangerous_zone">Danger Zone</string>
<string name="reset_app_warning">This will delete and re-copy required files to boot kali chroot, confirm?</string>
<string name="default_source_url" translatable="false">https://example.com/nhterm
</string>
<string name="default_source_url" translatable="false">http://http.kali.org/kali</string>
<string-array name="pref_general_shell_entries" translatable="false">
<item>sh</item>

View File

@ -51,4 +51,74 @@
<item name="android:textStyle">normal</item>
</style>
<style name="DialogStyle" parent="@style/MaterialAlertDialog.Material3">
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
<item name="colorPrimary">#547383</item>
<item name="android:textColor">@color/textColor</item>
<item name="android:textColorSecondary">@color/textColor</item>
<item name="android:textColorLink">#2D55A3</item>
<item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item>
<item name="buttonBarNegativeButtonStyle">@style/Alert.Button.Neutral</item>
<item name="buttonBarNeutralButtonStyle">@style/Alert.Button.Neutral</item>
</style>
<style name="DialogStyleCompat" parent="@style/ThemeOverlay.Material3.MaterialAlertDialog">
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
<item name="colorPrimary">#547383</item>
<item name="android:textColor">@color/textColor</item>
<item name="android:textColorSecondary">@color/textColor</item>
<item name="android:textColorLink">#2D55A3</item>
<item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item>
<item name="buttonBarNegativeButtonStyle">@style/Alert.Button.Neutral</item>
<item name="buttonBarNeutralButtonStyle">@style/Alert.Button.Neutral</item>
</style>
<style name="Alert.Button.Positive" parent="@style/Widget.Material3.Button.TextButton">
<item name="backgroundTint">@color/colorPrimary</item>
<item name="colorPrimary">#61A3D7</item>
<item name="colorPrimaryDark">#8DB2D3</item>
<item name="colorAccent">#61A3D7</item>
<item name="android:textColor">@color/textColor</item>
<item name="android:textColorSecondary">@color/textColor</item>
<item name="android:textColorLink">#2255D3</item>
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">false</item>
<item name="android:layout_marginStart">10dp</item>
<item name="android:textColorPrimary">@color/textColor</item>
</style>
<style name="Alert.Button.Negative" parent="@style/Widget.Material3.Button.TextButton">
<item name="backgroundTint">@color/colorPrimary</item>
<item name="colorPrimary">#61A3D7</item>
<item name="colorPrimaryDark">#8DB2D3</item>
<item name="colorAccent">#61A3D7</item>
<item name="android:textColor">@color/textColor</item>
<item name="android:textColorSecondary">@color/textColor</item>
<item name="android:textColorLink">#2255D3</item>
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">false</item>
<item name="android:layout_marginStart">10dp</item>
<item name="android:textColorPrimary">@color/textColor</item>
</style>
<style name="Alert.Button.Neutral" parent="@style/Widget.Material3.Button.TextButton">
<item name="backgroundTint">@color/colorPrimary</item>
<item name="colorPrimary">#61A3D7</item>
<item name="colorPrimaryDark">#8DB2D3</item>
<item name="colorAccent">#61A3D7</item>
<item name="android:textColor">@color/textColor</item>
<item name="android:textColorSecondary">@color/textColor</item>
<item name="android:textColorLink">#2255D3</item>
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">false</item>
<item name="android:layout_marginStart">10dp</item>
<item name="android:textColorPrimary">@color/textColor</item>
</style>
</resources>

View File

@ -5,12 +5,6 @@
android:summary="@string/pref_general_initial_command_desc"
android:title="@string/pref_general_initial_command"/>
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/key_general_vibrate"
android:summary="@string/pref_general_vibrate_desc"
android:title="@string/pref_general_vibrate"/>
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/key_generaL_backspace_map_to_esc"