├── .gitattributes ├── .gitignore ├── LICENSE ├── MODULE_LICENSE_APACHE2 ├── NOTICE ├── README.adoc ├── build.gradle ├── buildTools.gradle ├── docs ├── UTF-8-SMP-chars-demo.txt ├── UTF-8-demo.txt ├── atari_small_notice.txt ├── google-play-description ├── termoneplus-launcher-icon.xcf ├── termoneplus-launcher-round_icon.xcf ├── termoneplus-logo.xcf └── termoneplus-notification-icon.svg ├── emulatorview ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── com │ │ └── termoneplus │ │ │ ├── compat │ │ │ └── CharacterCompat.java │ │ │ └── utils │ │ │ └── SimpleClipboardManager.java │ └── jackpal │ │ └── androidterm │ │ └── emulatorview │ │ ├── BaseTextRenderer.java │ │ ├── Bitmap4x8FontRenderer.java │ │ ├── ByteQueue.java │ │ ├── ColorScheme.java │ │ ├── EmulatorDebug.java │ │ ├── EmulatorView.java │ │ ├── GrowableIntArray.java │ │ ├── PaintRenderer.java │ │ ├── Screen.java │ │ ├── StyleRow.java │ │ ├── TermKeyListener.java │ │ ├── TermSession.java │ │ ├── TerminalEmulator.java │ │ ├── TextRenderer.java │ │ ├── TextStyle.java │ │ ├── TranscriptScreen.java │ │ ├── UnicodeTranscript.java │ │ ├── UpdateCallback.java │ │ ├── compat │ │ ├── KeyCharacterMapCompat.java │ │ ├── KeycodeConstants.java │ │ └── Patterns.java │ │ └── package.html │ └── res │ └── drawable-nodpi │ └── atari_small_nodpi.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libtermexec ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── CMakeLists.txt │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── jackpal │ │ └── androidterm │ │ └── libtermexec │ │ └── v1 │ │ └── ITerminal.aidl │ ├── cpp │ ├── compat.c │ ├── compat.h │ ├── process.c │ ├── registration.c │ ├── registration.h │ └── termio.c │ └── java │ ├── com │ └── termoneplus │ │ ├── Process.java │ │ └── TermIO.java │ └── jackpal │ └── androidterm │ └── TermExec.java ├── samples ├── NOTE.pathbroadcasts ├── intents │ ├── .gitignore │ ├── build.gradle │ ├── lint.xml │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── termoneplus │ │ │ └── sample │ │ │ └── intents │ │ │ └── IntentSampleActivity.java │ │ └── res │ │ ├── dimens.xml │ │ ├── drawable │ │ └── background_rectangle.xml │ │ ├── layout │ │ └── main.xml │ │ └── values │ │ ├── dimens.xml │ │ └── strings.xml ├── pathbroadcasts │ ├── .gitignore │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ ├── .gitattributes │ │ ├── hello │ │ └── ls │ │ ├── java │ │ └── jackpal │ │ │ └── androidterm │ │ │ └── sample │ │ │ └── pathbroadcasts │ │ │ └── PathReceiver.java │ │ └── res │ │ └── values │ │ └── strings.xml └── telnet │ ├── .gitignore │ ├── build.gradle │ └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── jackpal │ │ └── androidterm │ │ └── sample │ │ └── telnet │ │ ├── LaunchActivity.java │ │ ├── TelnetSession.java │ │ └── TermActivity.java │ └── res │ ├── layout │ ├── launch_activity.xml │ └── term_activity.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── scripts ├── termoneplus-launcher-foreground.sh ├── termoneplus-launcher-resources.sh └── termoneplus-notification-resources.sh ├── settings.gradle ├── term ├── .gitignore ├── build.gradle ├── lint.xml └── src │ ├── CMakeLists.txt │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── skel │ │ └── shrc │ ├── java │ ├── com │ │ └── termoneplus │ │ │ ├── Application.java │ │ │ ├── BaseActivity.java │ │ │ ├── Installer.java │ │ │ ├── Permissions.java │ │ │ ├── RemoteActionActivity.java │ │ │ ├── Settings.java │ │ │ ├── TermActionBar.java │ │ │ ├── TermActivity.java │ │ │ ├── TermPreferencesActivity.java │ │ │ ├── WindowListActivity.java │ │ │ ├── WindowListAdapter.java │ │ │ ├── WindowListFragment.java │ │ │ ├── services │ │ │ ├── CommandService.java │ │ │ └── UnixSocketServer.java │ │ │ ├── shortcuts │ │ │ ├── AddShortcut.java │ │ │ ├── ColorValue.java │ │ │ └── FileSelection.java │ │ │ └── utils │ │ │ ├── ConsoleStartupScript.java │ │ │ ├── TextIcon.java │ │ │ ├── ThemeManager.java │ │ │ ├── WakeLock.java │ │ │ └── WrapOpenURL.java │ └── jackpal │ │ └── androidterm │ │ ├── BoundSession.java │ │ ├── GenericTermSession.java │ │ ├── RemoteInterface.java │ │ ├── RunScript.java │ │ ├── RunShortcut.java │ │ ├── ShellTermSession.java │ │ ├── Term.java │ │ ├── TermService.java │ │ ├── TermView.java │ │ ├── TermViewFlipper.java │ │ ├── compat │ │ ├── Base64.java │ │ ├── PRNGFixes.java │ │ ├── PathCollector.java │ │ └── PathSettings.java │ │ └── util │ │ ├── SessionList.java │ │ ├── ShortcutEncryption.java │ │ └── TermSettings.java │ ├── jni │ ├── appcmd.c │ ├── appinfo.h │ ├── atomicio.c │ └── socket.c │ └── res │ ├── drawable-hdpi │ └── ic_stat_service_notification_icon.png │ ├── drawable-ldpi │ └── ic_stat_service_notification_icon.png │ ├── drawable-mdpi │ └── ic_stat_service_notification_icon.png │ ├── drawable-v21 │ └── header_background.xml │ ├── drawable-xhdpi │ └── ic_stat_service_notification_icon.png │ ├── drawable-xxhdpi │ └── ic_stat_service_notification_icon.png │ ├── drawable │ ├── background_rectangle.xml │ ├── fs_directory_24dp.xml │ ├── fs_file_24dp.xml │ ├── fs_parent_24dp.xml │ ├── fs_unknown_24dp.xml │ ├── header_background.xml │ ├── ic_add_circle_outline_24dp.xml │ ├── ic_close_24dp.xml │ ├── ic_email_white_24dp.xml │ ├── ic_help_outline_white_24dp.xml │ ├── ic_launcher_background.xml │ ├── ic_settings_white_24dp.xml │ ├── ic_stat_service_notification_icon.png │ ├── ic_vpn_key_white_24dp.xml │ └── ic_windows_white_24dp.xml │ ├── layout │ ├── actionbar_windowlist.xml │ ├── activity_addshortcut.xml │ ├── activity_color_value.xml │ ├── activity_file_selection.xml │ ├── activity_preferences.xml │ ├── activity_term.xml │ ├── activity_term_floatbar.xml │ ├── activity_windowlist.xml │ ├── content_file_selection.xml │ ├── fragment_windowlist.xml │ ├── item_window.xml │ └── pref_shell_script.xml │ ├── menu │ └── main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-ldpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── values-bg │ ├── arrays.xml │ └── strings.xml │ ├── values-cs │ ├── arrays.xml │ └── strings.xml │ ├── values-de │ ├── arrays.xml │ └── strings.xml │ ├── values-en-rGB │ ├── arrays.xml │ └── strings.xml │ ├── values-es │ ├── arrays.xml │ └── strings.xml │ ├── values-eu │ ├── arrays.xml │ └── strings.xml │ ├── values-fr │ ├── arrays.xml │ └── strings.xml │ ├── values-hu │ ├── arrays.xml │ └── strings.xml │ ├── values-it │ ├── arrays.xml │ └── strings.xml │ ├── values-iw │ ├── arrays.xml │ └── strings.xml │ ├── values-ja │ ├── arrays.xml │ └── strings.xml │ ├── values-ka │ ├── arrays.xml │ └── strings.xml │ ├── values-ko │ ├── arrays.xml │ └── strings.xml │ ├── values-nb │ ├── arrays.xml │ └── strings.xml │ ├── values-night │ ├── colors.xml │ └── styles.xml │ ├── values-nl │ ├── arrays.xml │ └── strings.xml │ ├── values-pl │ ├── arrays.xml │ └── strings.xml │ ├── values-pt-rPT │ ├── arrays.xml │ └── strings.xml │ ├── values-pt │ ├── arrays.xml │ └── strings.xml │ ├── values-ro │ ├── arrays.xml │ └── strings.xml │ ├── values-ru │ ├── arrays.xml │ └── strings.xml │ ├── values-sk │ ├── arrays.xml │ └── strings.xml │ ├── values-sr │ ├── arrays.xml │ └── strings.xml │ ├── values-sv │ ├── arrays.xml │ └── strings.xml │ ├── values-tr │ ├── arrays.xml │ └── strings.xml │ ├── values-uk │ ├── arrays.xml │ └── strings.xml │ ├── values-zh-rCN │ ├── arrays.xml │ └── strings.xml │ ├── values-zh-rTW │ ├── arrays.xml │ └── strings.xml │ ├── values │ ├── arrays.xml │ ├── arraysNoLocalize.xml │ ├── attrs.xml │ ├── colors.xml │ ├── defaults.xml │ ├── dimens.xml │ ├── id.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── preferences.xml ├── tests ├── .gitattributes ├── background-lastline ├── background-outofindex ├── controlSequences │ ├── 256color.txt │ ├── combiningCharReplacement.txt │ ├── combiningChars.txt │ ├── hideCursor.txt │ ├── setTitle.txt │ ├── showCursor.txt │ └── textStyle.txt ├── cursor-down ├── cursor-down2 ├── cursor-down3 ├── cursor-up ├── cursor-up2 ├── cursor-up3 ├── echo-smiley ├── emulatorview-test │ ├── AndroidManifest.xml │ ├── proguard-project.txt │ ├── res │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-ldpi │ │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ └── values │ │ │ └── strings.xml │ └── src │ │ └── jackpal │ │ └── androidterm │ │ └── emulatorview │ │ ├── InputConnectionTest.java │ │ ├── ModifierKeyTest.java │ │ └── TermKeyListenerTest.java ├── fullWidthText ├── issue145 │ ├── README.md │ ├── fuzzer.go │ ├── issue145repro-2.txt │ └── issue145repro.txt ├── issue149 │ └── colors.go ├── reset ├── scroll-at-bottom-margin └── wideChars │ ├── combining-chars.txt │ ├── last-column-wrapping.txt │ ├── linkification.txt │ ├── overwriting1.txt │ ├── overwriting2.txt │ ├── overwriting3.txt │ ├── overwriting4.txt │ ├── overwriting5.txt │ └── sip-chars.txt └── version.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize line endings for text files. 2 | # This only affects how files are stored within the repo. Git will 3 | # automatically convert line endings to the platform choice when checking out, 4 | # and automatically convert them back to the normal form when checking in. 5 | 6 | * text=auto 7 | 8 | # Ensure XML files are treated a text files. 9 | 10 | *.xml text 11 | 12 | # Use java diff syntax for java files. 13 | 14 | *.java diff=java 15 | 16 | # Declare files that will always have CRLF line endings on checkout. 17 | *.bat text eol=crlf 18 | 19 | # shell files have unix(LF) end-of-line conversion 20 | 21 | *.sh text eol=lf 22 | 23 | # gradle wrapper shell script must have unix(LF) end-of-line conversion 24 | 25 | gradlew eol=lf 26 | 27 | # skeleton files must have end-of-line conversion as shell script, i.e. unix(LF) 28 | 29 | /**/assets/skel/* eol=lf 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | .DS_Store 4 | /build 5 | /.idea 6 | *.iml 7 | -------------------------------------------------------------------------------- /MODULE_LICENSE_APACHE2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuntashirAkon/termoneplus/02c1bfa9cfa4d24f39deb0047513e9ea42f6a183/MODULE_LICENSE_APACHE2 -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2005-2008, The Android Open Source Project 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | Terminal Emulator for Android 2 | ============================= 3 | 4 | https://termoneplus.com/ 5 | 6 | *"TermOne Plus"* is an Android application that turns your Android device into an computer terminal. It is successor of abandoned Jack Palevich's terminal emulator. 7 | 8 | Terminal is useful for accessing the command line shell (built into every Android phone) or use custom one (installed separately). 9 | This allows you to run various command line utilities. 10 | It emulates a reasonably large subset of Digital Equipment Corporation VT-100 terminal codes. 11 | Supported terminal types include *vt100*, *screen* (default), *linux* and *xterm*. 12 | As result console programs like *ssh* (PKIX-SSH), *vi* (unix visual editor), *emacs*, *mc* (Midnight Commander file manager), *nano* (Nano's ANOther editor), and other work smoothly. 13 | 14 | Code of "TermOne Plus" is hosted in https://gitlab.com/termapps/termoneplus[public repository]. 15 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | //maven { url 'https://maven.google.com' } 7 | google() 8 | mavenCentral() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:4.0.2' 12 | } 13 | } 14 | 15 | apply from: 'buildTools.gradle' 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | //maven { url 'https://maven.google.com' } 20 | google() 21 | mavenCentral() 22 | } 23 | project.ext { 24 | compileSdkVersion = 30 25 | targetSdkVersion = 30 26 | minSdkVersion = 21 27 | } 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /buildTools.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | project.ext { 3 | buildToolsVersion = '30.0.2' 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/UTF-8-SMP-chars-demo.txt: -------------------------------------------------------------------------------- 1 | These are some characters in the Supplementary Multilingual Plane: 2 | 𝄞 𝄴𝅘𝅥𝅯𝅗𝅥 𝑥𝄽 3 | -------------------------------------------------------------------------------- /docs/atari_small_notice.txt: -------------------------------------------------------------------------------- 1 | COMMENT Copyright (c) 1999, Thomas A. Fine 2 | COMMENT 3 | COMMENT License to copy, modify, and distribute for both commercial and 4 | COMMENT non-commercial use is herby granted, provided this notice 5 | COMMENT is preserved. 6 | COMMENT 7 | COMMENT Email to my last name at head.cfa.harvard.edu 8 | COMMENT http://hea-www.harvard.edu/~fine/ 9 | COMMENT 10 | COMMENT Produced with bdfedit, a tcl/tk font editing program 11 | COMMENT written by Thomas A. Fine -------------------------------------------------------------------------------- /docs/google-play-description: -------------------------------------------------------------------------------- 1 | "TermOne Plus" is a terminal emulator application for Android devices. 2 | Each android device has a build-in born shell and a number of shell command that allows user to: 3 | - manage files and directories: list, create, move(rename), delete, compare, view, and etc; 4 | - get information for running processes, network status and connections, mounted file system, free space, device; 5 | - use package and application manager; 6 | - do screen-shots. 7 | 8 | Application supports multiple terminal windows(screens). Each terminal starts own console session with build-in shell (by default). 9 | Terminal emulates large subset of Digital Equipment Corporation VT-100 terminal capabilities - supported are following terminal types: vt100, screen (default), linux, screen-256color, xterm and xterm-256color. Also it supports UTF-8 console text mode by default. 10 | For instance supported terminal capabilities allows users fully to use textual interface in remote login programs (ssh connections). 11 | Terminal screen support color schemes like "Dark Pastels", "Solarized Light", "Solarized Dark", "Linux Console" and etc. Also user could chose size of text. 12 | 13 | Terminal session use "shell start-up script" where you could tune environment setting, add shell functions or command aliases. 14 | 15 | "TermOne Plus" user interface is based on material design - icons, colors. It uses navigation drawer as main menu. Also user could switch between "Light" and "Dark" theme mode. 16 | 17 | Launcher short-cut functionality allows user to create "button"(android short-cut widget) to a command or shell script. 18 | Build-in file selector (aka file explorer) is exported and so allows other applications easy to pick a file. 19 | 20 | Application is localized in many languages and/or territories (locales). 21 | 22 | "TermOne Plus" is successor of excellent but un-maintained since 2015 "Terminal Emulator for Android". This new application includes rewritten user interface, many compatibility and portability improvements, stability and defect fixes, and localization enhancements. As result it works fine with recent android releases (Android 11). Even on ancient like Gingerbread(2.3) it looks and works the same as for recent one. 23 | 24 | Please visit application site to find out how to participate into development and/or localization, how to request new or enhanced functionality. 25 | 26 | WARNING: Upgrade from pre 3.1.0 versions: 27 | Due to hardened SE Linux rules on Android 9.0(Pie) and to allow use of executables provided by other applications "TermOne Plus" start to use "shared user id" functionality. Unfortunately Android package manager (PM) consider this as incompatible change and does not allow automatic upgrade. The only way to bay-pass limitation of Android PM is to reinstall application. 28 | Advantage is that now on Android 9.0(Pie) executables provided by SecureBox are again available for use in terminal application. 29 | -------------------------------------------------------------------------------- /docs/termoneplus-launcher-icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuntashirAkon/termoneplus/02c1bfa9cfa4d24f39deb0047513e9ea42f6a183/docs/termoneplus-launcher-icon.xcf -------------------------------------------------------------------------------- /docs/termoneplus-launcher-round_icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuntashirAkon/termoneplus/02c1bfa9cfa4d24f39deb0047513e9ea42f6a183/docs/termoneplus-launcher-round_icon.xcf -------------------------------------------------------------------------------- /docs/termoneplus-logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuntashirAkon/termoneplus/02c1bfa9cfa4d24f39deb0047513e9ea42f6a183/docs/termoneplus-logo.xcf -------------------------------------------------------------------------------- /docs/termoneplus-notification-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | >1+ 6 | 7 | -------------------------------------------------------------------------------- /emulatorview/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /emulatorview/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion project.ext.compileSdkVersion 5 | buildToolsVersion project.ext.buildToolsVersion 6 | defaultConfig { 7 | minSdkVersion project.ext.minSdkVersion 8 | targetSdkVersion project.ext.targetSdkVersion 9 | 10 | //Major.Minor.Patch 11 | versionCode 10601 12 | versionName '1.6.1' 13 | } 14 | compileOptions { 15 | sourceCompatibility JavaVersion.VERSION_1_8 16 | targetCompatibility JavaVersion.VERSION_1_8 17 | } 18 | productFlavors { 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | implementation "androidx.annotation:annotation:1.1.0" 30 | } 31 | -------------------------------------------------------------------------------- /emulatorview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/com/termoneplus/compat/CharacterCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Roumen Petrov. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.termoneplus.compat; 18 | 19 | import android.icu.lang.UCharacter; 20 | import android.icu.lang.UProperty; 21 | import android.os.Build; 22 | 23 | import androidx.annotation.RequiresApi; 24 | 25 | public class CharacterCompat { 26 | public static int charCount(int cp /*code point*/) { 27 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N /*API Level 24*/) { 28 | return Compat1.charCount(cp); 29 | } else { 30 | return Compat24.charCount(cp); 31 | } 32 | } 33 | 34 | public static int toChars(int cp, char[] dst, int dstIndex) { 35 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N /*API Level 24*/) { 36 | return Compat1.toChars(cp, dst, dstIndex); 37 | } else { 38 | return Compat24.toChars(cp, dst, dstIndex); 39 | } 40 | } 41 | 42 | public static boolean isEastAsianDoubleWidth(int ch /*code point*/) { 43 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N /*API Level 24*/) { 44 | return Compat1.isEastAsianDoubleWidth(ch); 45 | } else { 46 | return Compat24.isEastAsianDoubleWidth(ch); 47 | } 48 | } 49 | 50 | private static class Compat1 { 51 | private static int charCount(int cp) { 52 | return Character.charCount(cp); 53 | } 54 | 55 | private static int toChars(int cp, char[] dst, int dstIndex) { 56 | return Character.toChars(cp, dst, dstIndex); 57 | } 58 | 59 | @SuppressWarnings("deprecation") 60 | private static boolean isEastAsianDoubleWidth(int ch) { 61 | // Android's getEastAsianWidth() only works for BMP characters 62 | // use fully-qualified class name to avoid deprecation warning on import 63 | switch (android.text.AndroidCharacter.getEastAsianWidth((char) ch)) { 64 | case android.text.AndroidCharacter.EAST_ASIAN_WIDTH_FULL_WIDTH: 65 | case android.text.AndroidCharacter.EAST_ASIAN_WIDTH_WIDE: 66 | return true; 67 | } 68 | return false; 69 | } 70 | } 71 | 72 | @RequiresApi(24) 73 | private static class Compat24 { 74 | private static int charCount(int cp) { 75 | return UCharacter.charCount(cp); 76 | } 77 | 78 | private static int toChars(int cp, char[] dst, int dstIndex) { 79 | return UCharacter.toChars(cp, dst, dstIndex); 80 | } 81 | 82 | private static boolean isEastAsianDoubleWidth(int ch) { 83 | int ea = UCharacter.getIntPropertyValue(ch, UProperty.EAST_ASIAN_WIDTH); 84 | switch (ea) { 85 | case UCharacter.EastAsianWidth.FULLWIDTH: 86 | case UCharacter.EastAsianWidth.WIDE: 87 | return true; 88 | } 89 | return false; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/com/termoneplus/utils/SimpleClipboardManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Roumen Petrov. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.termoneplus.utils; 18 | 19 | import android.content.ClipData; 20 | import android.content.ClipDescription; 21 | import android.content.ClipboardManager; 22 | import android.content.Context; 23 | 24 | public class SimpleClipboardManager { 25 | private final ClipboardManager clip; 26 | 27 | public SimpleClipboardManager(Context context) { 28 | clip = (ClipboardManager) context.getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE); 29 | } 30 | 31 | public CharSequence getText() { 32 | ClipData.Item item = clip.getPrimaryClip().getItemAt(0); 33 | return item.getText(); 34 | } 35 | 36 | public void setText(CharSequence text) { 37 | ClipData clipData = ClipData.newPlainText("simple text", text); 38 | clip.setPrimaryClip(clipData); 39 | } 40 | 41 | public boolean hasText() { 42 | return (clip.hasPrimaryClip() && 43 | clip.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/jackpal/androidterm/emulatorview/EmulatorDebug.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package jackpal.androidterm.emulatorview; 18 | 19 | /** 20 | * Debug settings. 21 | */ 22 | class EmulatorDebug { 23 | /** 24 | * Set to true to add debugging code and logging. 25 | */ 26 | public static final boolean DEBUG = false; 27 | 28 | /** 29 | * Set to true to log IME calls. 30 | */ 31 | public static final boolean LOG_IME = DEBUG & false; 32 | 33 | /** 34 | * Set to true to log each character received from the remote process to the 35 | * android log, which makes it easier to debug some kinds of problems with 36 | * emulating escape sequences and control codes. 37 | */ 38 | public static final boolean LOG_CHARACTERS_FLAG = DEBUG & false; 39 | 40 | /** 41 | * Set to true to log unknown escape sequences. 42 | */ 43 | public static final boolean LOG_UNKNOWN_ESCAPE_SEQUENCES = DEBUG & false; 44 | 45 | /** 46 | * The tag we use when logging, so that our messages can be distinguished 47 | * from other messages in the log. Public because it's used by several 48 | * classes. 49 | */ 50 | public static final String LOG_TAG = "EmulatorView"; 51 | 52 | public static String bytesToString(byte[] data, int base, int length) { 53 | StringBuilder buf = new StringBuilder(); 54 | for (int i = 0; i < length; i++) { 55 | byte b = data[base + i]; 56 | if (b < 32 || b > 126) { 57 | buf.append(String.format("\\x%02x", b)); 58 | } else { 59 | buf.append((char) b); 60 | } 61 | } 62 | return buf.toString(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/jackpal/androidterm/emulatorview/GrowableIntArray.java: -------------------------------------------------------------------------------- 1 | package jackpal.androidterm.emulatorview; 2 | 3 | class GrowableIntArray { 4 | GrowableIntArray(int initalCapacity) { 5 | mData = new int[initalCapacity]; 6 | mLength = 0; 7 | } 8 | 9 | void append(int i) { 10 | if (mLength + 1 > mData.length) { 11 | int newLength = Math.max((mData.length * 3) >> 1, 16); 12 | int[] temp = new int[newLength]; 13 | System.arraycopy(mData, 0, temp, 0, mLength); 14 | mData = temp; 15 | } 16 | mData[mLength++] = i; 17 | } 18 | 19 | int length() { 20 | return mLength; 21 | } 22 | 23 | int at(int index) { 24 | return mData[index]; 25 | } 26 | 27 | int[] mData; 28 | int mLength; 29 | } 30 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/jackpal/androidterm/emulatorview/StyleRow.java: -------------------------------------------------------------------------------- 1 | package jackpal.androidterm.emulatorview; 2 | 3 | /** 4 | * Utility class for dealing with text style lines. 5 | *

6 | * We pack color and formatting information for a particular character into an 7 | * int -- see the TextStyle class for details. The simplest way of storing 8 | * that information for a screen row would be to use an array of int -- but 9 | * given that we only use the lower three bytes of the int to store information, 10 | * that effectively wastes one byte per character -- nearly 8 KB per 100 lines 11 | * with an 80-column transcript. 12 | *

13 | * Instead, we use an array of bytes and store the bytes of each int 14 | * consecutively in big-endian order. 15 | */ 16 | final class StyleRow { 17 | private int mStyle; 18 | private final int mColumns; 19 | /** 20 | * Initially null, will be allocated when needed. 21 | */ 22 | private byte[] mData; 23 | 24 | StyleRow(int style, int columns) { 25 | mStyle = style; 26 | mColumns = columns; 27 | } 28 | 29 | void set(int column, int style) { 30 | if (style == mStyle && mData == null) { 31 | return; 32 | } 33 | ensureData(); 34 | setStyle(column, style); 35 | } 36 | 37 | int get(int column) { 38 | if (mData == null) { 39 | return mStyle; 40 | } 41 | return getStyle(column); 42 | } 43 | 44 | boolean isSolidStyle() { 45 | return mData == null; 46 | } 47 | 48 | int getSolidStyle() { 49 | if (mData != null) { 50 | throw new IllegalArgumentException("Not a solid style"); 51 | } 52 | return mStyle; 53 | } 54 | 55 | void copy(int start, StyleRow dst, int offset, int len) { 56 | // fast case 57 | if (mData == null && dst.mData == null && start == 0 && offset == 0 58 | && len == mColumns) { 59 | dst.mStyle = mStyle; 60 | return; 61 | } 62 | // There are other potentially fast cases, but let's just treat them 63 | // all the same for simplicity. 64 | ensureData(); 65 | dst.ensureData(); 66 | System.arraycopy(mData, 3 * start, dst.mData, 3 * offset, 3 * len); 67 | 68 | } 69 | 70 | void ensureData() { 71 | if (mData == null) { 72 | allocate(); 73 | } 74 | } 75 | 76 | private void allocate() { 77 | mData = new byte[3 * mColumns]; 78 | for (int i = 0; i < mColumns; i++) { 79 | setStyle(i, mStyle); 80 | } 81 | } 82 | 83 | private int getStyle(int column) { 84 | int index = 3 * column; 85 | byte[] line = mData; 86 | return line[index] & 0xff | (line[index + 1] & 0xff) << 8 87 | | (line[index + 2] & 0xff) << 16; 88 | } 89 | 90 | private void setStyle(int column, int value) { 91 | int index = 3 * column; 92 | byte[] line = mData; 93 | line[index] = (byte) (value & 0xff); 94 | line[index + 1] = (byte) ((value >> 8) & 0xff); 95 | line[index + 2] = (byte) ((value >> 16) & 0xff); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/jackpal/androidterm/emulatorview/TextRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package jackpal.androidterm.emulatorview; 18 | 19 | import android.graphics.Canvas; 20 | 21 | /** 22 | * Text renderer interface 23 | */ 24 | interface TextRenderer { 25 | int MODE_OFF = 0; 26 | int MODE_ON = 1; 27 | int MODE_LOCKED = 2; 28 | int MODE_MASK = 3; 29 | 30 | int MODE_SHIFT_SHIFT = 0; 31 | int MODE_ALT_SHIFT = 2; 32 | int MODE_CTRL_SHIFT = 4; 33 | int MODE_FN_SHIFT = 6; 34 | 35 | void setReverseVideo(boolean reverseVideo); 36 | 37 | float getCharacterWidth(); 38 | 39 | int getCharacterHeight(); 40 | 41 | /** 42 | * @return pixels above top row of text to avoid looking cramped. 43 | */ 44 | int getTopMargin(); 45 | 46 | /** 47 | * Draw a run of text 48 | * 49 | * @param canvas The canvas to draw into. 50 | * @param x Canvas coordinate of the left edge of the whole line. 51 | * @param y Canvas coordinate of the bottom edge of the whole line. 52 | * @param lineOffset The screen character offset of this text run (0..length of line) 53 | * @param runWidth 54 | * @param text 55 | * @param index 56 | * @param count 57 | * @param selectionStyle True to draw the text using the "selected" style (for clipboard copy) 58 | * @param textStyle 59 | * @param cursorOffset The screen character offset of the cursor (or -1 if not on this line.) 60 | * @param cursorIndex The index of the cursor in text chars. 61 | * @param cursorIncr The width of the cursor in text chars. (1 or 2) 62 | * @param cursorWidth The width of the cursor in screen columns (1 or 2) 63 | * @param cursorMode The cursor mode (used to show state of shift/control/alt/fn locks. 64 | */ 65 | void drawTextRun(Canvas canvas, float x, float y, 66 | int lineOffset, int runWidth, char[] text, 67 | int index, int count, boolean selectionStyle, int textStyle, 68 | int cursorOffset, int cursorIndex, int cursorIncr, int cursorWidth, int cursorMode); 69 | } 70 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/jackpal/androidterm/emulatorview/TextStyle.java: -------------------------------------------------------------------------------- 1 | package jackpal.androidterm.emulatorview; 2 | 3 | final class TextStyle { 4 | // Effect bitmasks: 5 | final static int fxNormal = 0; 6 | final static int fxBold = 1; // Originally Bright 7 | //final static int fxFaint = 2; 8 | final static int fxItalic = 1 << 1; 9 | final static int fxUnderline = 1 << 2; 10 | final static int fxBlink = 1 << 3; 11 | final static int fxInverse = 1 << 4; 12 | final static int fxInvisible = 1 << 5; 13 | 14 | // Special color indices 15 | final static int ciForeground = 256; // VT100 text foreground color 16 | final static int ciBackground = 257; // VT100 text background color 17 | final static int ciCursorForeground = 258; // VT100 text cursor foreground color 18 | final static int ciCursorBackground = 259; // VT100 text cursor background color 19 | 20 | final static int ciColorLength = ciCursorBackground + 1; 21 | 22 | final static int kNormalTextStyle = encode(ciForeground, ciBackground, fxNormal); 23 | 24 | static int encode(int foreColor, int backColor, int effect) { 25 | return ((effect & 0x3f) << 18) | ((foreColor & 0x1ff) << 9) | (backColor & 0x1ff); 26 | } 27 | 28 | static int decodeForeColor(int encodedColor) { 29 | return (encodedColor >> 9) & 0x1ff; 30 | } 31 | 32 | static int decodeBackColor(int encodedColor) { 33 | return encodedColor & 0x1ff; 34 | } 35 | 36 | static int decodeEffect(int encodedColor) { 37 | return (encodedColor >> 18) & 0x3f; 38 | } 39 | 40 | private TextStyle() { 41 | // Prevent instantiation 42 | throw new UnsupportedOperationException(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/jackpal/androidterm/emulatorview/UpdateCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package jackpal.androidterm.emulatorview; 18 | 19 | /** 20 | * Generic callback to be invoked to notify of updates. 21 | */ 22 | public interface UpdateCallback { 23 | /** 24 | * Callback function to be invoked when an update happens. 25 | */ 26 | void onUpdate(); 27 | } 28 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/jackpal/androidterm/emulatorview/compat/KeyCharacterMapCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Jack Palevich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package jackpal.androidterm.emulatorview.compat; 18 | 19 | import android.view.KeyCharacterMap; 20 | 21 | public abstract class KeyCharacterMapCompat { 22 | public static final int MODIFIER_BEHAVIOR_CHORDED = 0; 23 | public static final int MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED = 1; 24 | 25 | public static KeyCharacterMapCompat wrap(Object map) { 26 | if (map == null) return null; 27 | 28 | return new KeyCharacterMapApi11OrLater(map); 29 | } 30 | 31 | private static class KeyCharacterMapApi11OrLater extends KeyCharacterMapCompat { 32 | private final KeyCharacterMap mMap; 33 | 34 | public KeyCharacterMapApi11OrLater(Object map) { 35 | mMap = (KeyCharacterMap) map; 36 | } 37 | 38 | public int getModifierBehaviour() { 39 | return mMap.getModifierBehavior(); 40 | } 41 | } 42 | 43 | public abstract int getModifierBehaviour(); 44 | } 45 | -------------------------------------------------------------------------------- /emulatorview/src/main/java/jackpal/androidterm/emulatorview/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This package provides a fairly complete VT100 terminal emulator {@link 4 | jackpal.androidterm.emulatorview.TermSession TermSession} and a corresponding 5 | Android view {@link jackpal.androidterm.emulatorview.EmulatorView EmulatorView}. 6 | 7 |

Most users will create a TermSession, connect it to an {@link 8 | java.io.InputStream InputStream} and {@link java.io.OutputStream OutputStream} 9 | from the emulation client, then instantiate the EmulatorView and 10 | add it to an activity's layout. 11 | 12 | 13 | -------------------------------------------------------------------------------- /emulatorview/src/main/res/drawable-nodpi/atari_small_nodpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuntashirAkon/termoneplus/02c1bfa9cfa4d24f39deb0047513e9ea42f6a183/emulatorview/src/main/res/drawable-nodpi/atari_small_nodpi.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | 16 | # AndroidX package structure to make it clearer which packages are bundled with the 17 | # Android operating system, and which are packaged with your app's APK 18 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 19 | android.useAndroidX=true 20 | # Automatically convert third-party libraries to use AndroidX 21 | android.enableJetifier=true 22 | 23 | # NOTE: R8 enabled by default in gradle plugin 3.4.0! 24 | # Disables R8 for Android Library modules only. 25 | #android.enableR8.libraries = false 26 | # Disables R8 for all modules. 27 | #android.enableR8 = false 28 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuntashirAkon/termoneplus/02c1bfa9cfa4d24f39deb0047513e9ea42f6a183/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /libtermexec/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.externalNativeBuild 3 | /.cxx 4 | -------------------------------------------------------------------------------- /libtermexec/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion project.ext.compileSdkVersion 5 | buildToolsVersion project.ext.buildToolsVersion 6 | defaultConfig { 7 | minSdkVersion project.ext.minSdkVersion 8 | targetSdkVersion project.ext.targetSdkVersion 9 | 10 | //Major.Minor.Patch 11 | versionCode 10100 12 | versionName '1.1.0' 13 | } 14 | externalNativeBuild { 15 | cmake { 16 | path "src/CMakeLists.txt" 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | productFlavors { 24 | } 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | } 32 | 33 | // by default recent plugin version does not copy any AIDL files "to avoid publishing too much" 34 | android.libraryVariants.all { variant -> 35 | Sync packageAidl = project.tasks.create("addPublic${variant.name.capitalize()}Aidl", Sync) { sync -> 36 | from "$project.projectDir/src/main/aidl/" 37 | into "$buildDir/intermediates/bundles/${variant.dirName}/aidl/" 38 | } 39 | 40 | Task javaCompileTask 41 | if (variant.hasProperty('javaCompileProvider')) { 42 | // gradle plugin 3.3.+ 43 | javaCompileTask = variant.javaCompileProvider.get() 44 | } else { 45 | javaCompileTask = variant.javaCompile 46 | } 47 | javaCompileTask.dependsOn packageAidl 48 | } 49 | 50 | dependencies { 51 | implementation fileTree(include: ['*.jar'], dir: 'libs') 52 | implementation "androidx.annotation:annotation:1.1.0" 53 | } 54 | -------------------------------------------------------------------------------- /libtermexec/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/uniqa/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /libtermexec/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## 2 | # Copyright (C) 2018-2019 Roumen Petrov. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | ## 16 | 17 | cmake_minimum_required(VERSION 2.4) 18 | 19 | add_library( 20 | term-system 21 | SHARED 22 | main/cpp/registration.c 23 | main/cpp/process.c 24 | main/cpp/termio.c 25 | main/cpp/compat.c 26 | ) 27 | target_link_libraries( 28 | term-system 29 | log 30 | ) 31 | -------------------------------------------------------------------------------- /libtermexec/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libtermexec/src/main/aidl/jackpal/androidterm/libtermexec/v1/ITerminal.aidl: -------------------------------------------------------------------------------- 1 | package jackpal.androidterm.libtermexec.v1; 2 | 3 | import android.content.IntentSender; 4 | import android.os.ParcelFileDescriptor; 5 | import android.os.ResultReceiver; 6 | 7 | // see also: 8 | // the (clumsy) way to handle object inheritance with Binder: 9 | // https://kevinhartman.github.io/blog/2012/07/23/inheritance-through-ipc-using-aidl-in-android/ 10 | // some (possibly outdated) notes on preserving backward compatibility: 11 | // https://stackoverflow.com/questions/18197783/android-aidl-interface-parcelables-and-backwards-compatibility 12 | /** 13 | * An interface for interacting with Terminal implementation. 14 | * 15 | * The version of the interface is encoded in Intent action and the AIDL package name. New versions 16 | * of this interface may be implemented in future. Those versions will be made available 17 | * in separate packages and older versions will continue to work. 18 | */ 19 | interface ITerminal { 20 | /** 21 | * Start a new Terminal session. A session will remain hosted by service, that provides binding, 22 | * but no gurantees of process pesistence as well as stability of connection are made. You 23 | * should keep your ParcelFileDescriptor around and allow ServiceConnection to call this method 24 | * again, when reconnection happens, in case service hosting the session is killed by system. 25 | * 26 | * Allows caller to be notified of terminal session events. Multiple calls can happen on each, 27 | * and new call types can be introduced, so prepare to ignore unknown event codes. 28 | * 29 | * So far only notifications about session end (code 0) are supported. This notification is 30 | * issued after abovementioned file descriptor is closed and the session is ended from 31 | * Terminal's standpoint. 32 | * 33 | * @param pseudoTerminalMultiplexerFd file descriptor, obtained by opening /dev/ptmx. 34 | * @param a callback 35 | * 36 | * @return IntentSender, that can be used to start corresponding Terminal Activity. 37 | */ 38 | IntentSender startSession(in ParcelFileDescriptor pseudoTerminalMultiplexerFd, in ResultReceiver callback); 39 | } 40 | -------------------------------------------------------------------------------- /libtermexec/src/main/cpp/compat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Roumen Petrov. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "compat.h" 18 | 19 | #if defined(__cplusplus) 20 | # error "__cplusplus" 21 | #endif 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | void 30 | closefrom(int lowfd) { 31 | int pws_fd = -1; 32 | DIR *dirp; 33 | 34 | { /* keep android property workspace open */ 35 | char *pws_env = getenv("ANDROID_PROPERTY_WORKSPACE"); 36 | if (pws_env) { 37 | /* format "int,int" */ 38 | pws_fd = atoi(pws_env); 39 | } 40 | } 41 | 42 | dirp = opendir("/proc/self/fd"); 43 | if (dirp != NULL) { 44 | int dir_fd = dirfd(dirp); 45 | struct dirent *dent; 46 | long fd; 47 | char *endp; 48 | 49 | for (dent = readdir(dirp); dent != NULL; dent = readdir(dirp)) { 50 | fd = strtol(dent->d_name, &endp, 10); 51 | if ( 52 | (dent->d_name != endp) && (*endp == '\0') && 53 | (fd >= 0) && (fd < INT_MAX) && 54 | (fd >= lowfd) && 55 | (fd != pws_fd) && (fd != dir_fd) 56 | ) 57 | (void) close((int) fd); 58 | } 59 | 60 | (void) closedir(dirp); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /libtermexec/src/main/cpp/compat.h: -------------------------------------------------------------------------------- 1 | #ifndef TERMONEPLUS_COMPAT_H 2 | #define TERMONEPLUS_COMPAT_H 3 | /* 4 | * Copyright (C) 2018 Roumen Petrov. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /** 20 | * NAME 21 | * closefrom -- delete open file descriptors 22 | * 23 | * SYNOPSIS 24 | * void closefrom(int lowfd); 25 | * 26 | * DESCRIPTION 27 | * The closefrom() system call deletes all open file descriptors greater 28 | * than or equal to lowfd from the per-process object reference table. Any 29 | * errors encountered while closing file descriptors are ignored. 30 | */ 31 | void closefrom(int lowfd); 32 | 33 | #endif /* ndef TERMONEPLUS_COMPAT_H */ 34 | -------------------------------------------------------------------------------- /libtermexec/src/main/cpp/registration.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2020 Roumen Petrov. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "registration.h" 18 | 19 | #if defined(__cplusplus) 20 | # error "__cplusplus" 21 | #endif 22 | 23 | #include 24 | 25 | 26 | /* 27 | * Register several "native"-methods for specified class. 28 | */ 29 | int register_native( 30 | JNIEnv *env, const char *class_name, 31 | JNINativeMethod *methods, size_t num_methods 32 | ) { 33 | jint r; 34 | jclass clazz; 35 | jint nMethods; 36 | 37 | clazz = (*env)->FindClass(env, class_name); 38 | if (clazz == NULL) { 39 | LOGE("Unable to find class '%s'", class_name); 40 | return JNI_ERR; 41 | } 42 | 43 | nMethods = (jint) num_methods; 44 | if (num_methods != (size_t) nMethods) { /*paranoid check*/ 45 | LOGE("Number of methods overflow for '%s'", class_name); 46 | return JNI_ERR; 47 | } 48 | 49 | r = (*env)->RegisterNatives(env, clazz, methods, nMethods); 50 | if (r < 0) { 51 | return r; 52 | } 53 | 54 | return JNI_OK; 55 | } 56 | 57 | 58 | static void 59 | throwNewException(JNIEnv *env, const char *clazz, const char *msg) { 60 | jclass exception = (*env)->FindClass(env, clazz); 61 | (*env)->ThrowNew(env, exception, msg); 62 | } 63 | 64 | void 65 | throwOutOfMemoryError(JNIEnv *env, const char *msg) { 66 | throwNewException(env, "java/lang/OutOfMemoryError", msg); 67 | } 68 | 69 | void 70 | throwIOException(JNIEnv *env, const char *fmt, ...) { 71 | char msg[1024]; 72 | va_list args; 73 | 74 | va_start(args, fmt); 75 | (void)vsnprintf(msg, sizeof(msg), fmt, args); 76 | va_end(args); 77 | 78 | throwNewException(env, "java/io/IOException", msg); 79 | } 80 | 81 | 82 | int 83 | termoneplus_log_print(int prio, const char *fmt, ...) { 84 | int r; 85 | va_list args; 86 | 87 | va_start(args, fmt); 88 | r = __android_log_vprint(prio, "TermOnePlus(native)", fmt, args); 89 | va_end(args); 90 | 91 | return r; 92 | } 93 | 94 | 95 | jint JNICALL 96 | JNI_OnLoad(JavaVM *vm, void *reserved) { 97 | jint r; 98 | union { 99 | JNIEnv *p; 100 | void *v; 101 | } env; 102 | 103 | (void) reserved; 104 | 105 | r = (*vm)->GetEnv(vm, &env.v, JNI_VERSION_1_2); 106 | if (r != JNI_OK) { 107 | LOGE("ERROR: GetEnv failed with error code %d", r); 108 | return -1; 109 | } 110 | 111 | r = register_process(env.p); 112 | if (r != JNI_OK) { 113 | LOGE("ERROR: 'process' registration fail"); 114 | return -1; 115 | } 116 | 117 | r = register_termio(env.p); 118 | if (r != JNI_OK) { 119 | LOGE("ERROR: 'termio' registration fail"); 120 | return -1; 121 | } 122 | 123 | return JNI_VERSION_1_2; 124 | } 125 | -------------------------------------------------------------------------------- /libtermexec/src/main/cpp/registration.h: -------------------------------------------------------------------------------- 1 | #ifndef TERMONEPLUS_REGISTRATION_H 2 | #define TERMONEPLUS_REGISTRATION_H 3 | /* 4 | * Copyright (C) 2018-2020 Roumen Petrov. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | 23 | int register_native( 24 | JNIEnv *env, const char *class_name, 25 | JNINativeMethod *methods, size_t num_methods 26 | ); 27 | 28 | int register_process(JNIEnv *env); 29 | int register_termio(JNIEnv *env); 30 | 31 | 32 | void throwOutOfMemoryError(JNIEnv *env, const char *msg) ; 33 | void throwIOException(JNIEnv *env, const char *fmt, ...); 34 | 35 | 36 | int termoneplus_log_print(int prio, const char *fmt, ...); 37 | #define LOGE(...) do { termoneplus_log_print(ANDROID_LOG_ERROR, __VA_ARGS__); } while(0) 38 | 39 | #endif /* ndef TERMONEPLUS_REGISTRATION_H */ 40 | -------------------------------------------------------------------------------- /libtermexec/src/main/cpp/termio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2020 Roumen Petrov. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "registration.h" 18 | 19 | #if defined(__cplusplus) 20 | # error "__cplusplus" 21 | #endif 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | static void 30 | termios_setUTF8Input( 31 | JNIEnv *env, jobject clazz, 32 | jint fd, jboolean flag 33 | ) { 34 | struct termios tio; 35 | 36 | (void) clazz; 37 | 38 | if (tcgetattr(fd, &tio) != 0) { 39 | throwIOException(env, "Failed to get the parameters associated with the terminal"); 40 | return; 41 | } 42 | 43 | /* quoted from termios(3) manual page: 44 | * c_iflag flag constants: 45 | * ... 46 | * IUTF8 (since Linux 2.6.4) 47 | * (not in POSIX) Input is UTF8; this allows character-erase to be correctly performed in cooked mode. 48 | */ 49 | if (flag) { 50 | tio.c_iflag |= IUTF8; 51 | } else { 52 | tio.c_iflag &= ~IUTF8; 53 | } 54 | 55 | if (tcsetattr(fd, TCSANOW, &tio) != 0) { 56 | throwIOException(env, "Failed to set the parameters associated with the terminal"); 57 | } 58 | } 59 | 60 | 61 | static void 62 | ioctl_setWindowSize( 63 | JNIEnv *env, jobject clazz, 64 | jint fd, jint row, jint col, jint xpixel, jint ypixel 65 | ) { 66 | struct winsize arg; 67 | 68 | (void) clazz; 69 | 70 | arg.ws_row = (unsigned short) row; 71 | arg.ws_col = (unsigned short) col; 72 | arg.ws_xpixel = (unsigned short) xpixel; 73 | arg.ws_ypixel = (unsigned short) ypixel; 74 | 75 | /* quoted from tty_ioctl(4) manual page: 76 | * TIOCSWINSZ const struct winsize *argp 77 | * Set window size. 78 | * ... 79 | * When the window size changes, a SIGWINCH signal is sent to the foreground process group. 80 | */ 81 | if (ioctl(fd, TIOCSWINSZ, &arg) != 0) { 82 | throwIOException(env, "ioctl: set tty window size fail / error %d/%s", 83 | errno, strerror(errno)); 84 | } 85 | } 86 | 87 | 88 | int 89 | register_termio(JNIEnv *env) { 90 | static JNINativeMethod methods[] = { 91 | {"setUTF8Input", "(IZ)V", (void *) termios_setUTF8Input}, 92 | {"setWindowSize", "(IIIII)V", (void *) ioctl_setWindowSize} 93 | }; 94 | return register_native( 95 | env, 96 | "com/termoneplus/TermIO$Native", 97 | methods, sizeof(methods) / sizeof(*methods) 98 | ); 99 | } 100 | -------------------------------------------------------------------------------- /libtermexec/src/main/java/com/termoneplus/Process.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Roumen Petrov. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.termoneplus; 18 | 19 | import android.os.ParcelFileDescriptor; 20 | 21 | import java.io.IOException; 22 | import java.nio.charset.StandardCharsets; 23 | 24 | 25 | public class Process { 26 | 27 | static { 28 | System.loadLibrary("term-system"); 29 | } 30 | 31 | public static int createSubprocess(ParcelFileDescriptor masterPty, 32 | String cmd, 33 | String[] arguments, 34 | String[] environment) 35 | throws IOException { 36 | // Let convert to UTF-8 in java code instead in native methods 37 | // prepare command path 38 | byte[] path = cmd.getBytes(StandardCharsets.UTF_8); 39 | 40 | // prepare command arguments 41 | byte[][] argv; 42 | argv = new byte[arguments.length][0]; 43 | for (int k = 0; k < arguments.length; k++) { 44 | String val = arguments[k]; 45 | argv[k] = val.getBytes(StandardCharsets.UTF_8); 46 | } 47 | 48 | // prepare command environment 49 | byte[][] envp; 50 | envp = new byte[environment.length][0]; 51 | for (int k = 0; k < environment.length; k++) { 52 | String val = environment[k]; 53 | envp[k] = val.getBytes(StandardCharsets.UTF_8); 54 | } 55 | 56 | // create terminal process ... 57 | int ptm = masterPty.getFd(); 58 | return Native.createSubprocess(ptm, path, argv, envp); 59 | } 60 | 61 | public static int waitExit(int pid) { 62 | return Native.waitExit(pid); 63 | } 64 | 65 | public static void finishChilds(int pid) { 66 | Native.finishChilds(pid); 67 | } 68 | 69 | 70 | private static class Native { 71 | private static native int createSubprocess(int ptm, byte[] path, byte[][] argv, byte[][] envp) 72 | throws IOException; 73 | 74 | private static native int waitExit(int pid); 75 | 76 | private static native void finishChilds(int pid); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /libtermexec/src/main/java/com/termoneplus/TermIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Roumen Petrov. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.termoneplus; 18 | 19 | import android.os.ParcelFileDescriptor; 20 | 21 | import java.io.IOException; 22 | 23 | 24 | public class TermIO { 25 | 26 | static { 27 | System.loadLibrary("term-system"); 28 | } 29 | 30 | public static void setUTF8Input(ParcelFileDescriptor masterPty, boolean flag) throws IOException { 31 | int fd = masterPty.getFd(); 32 | Native.setUTF8Input(fd, flag); 33 | } 34 | 35 | public static void setWindowSize(ParcelFileDescriptor masterPty, int row, int col) throws IOException { 36 | int fd = masterPty.getFd(); 37 | Native.setWindowSize(fd, row, col, 0, 0); 38 | } 39 | 40 | private static class Native { 41 | private static native void setUTF8Input(int fd, boolean flag) throws IOException; 42 | 43 | private static native void setWindowSize(int fd, int row, int col, int xpixel, int ypixel) throws IOException; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /libtermexec/src/main/java/jackpal/androidterm/TermExec.java: -------------------------------------------------------------------------------- 1 | package jackpal.androidterm; 2 | 3 | import android.os.Looper; 4 | import android.os.ParcelFileDescriptor; 5 | 6 | import com.termoneplus.Process; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Hashtable; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import androidx.annotation.NonNull; 16 | 17 | 18 | /** 19 | * Utility methods for creating and managing a subprocess. This class differs from 20 | * {@link java.lang.ProcessBuilder} in that a pty is used to communicate with the subprocess. 21 | *

22 | * Pseudo-terminals are a powerful Unix feature, that allows programs to interact with other programs 23 | * they start in slightly more human-like way. For example, a pty owner can send ^C (aka SIGINT) 24 | * to attached shell, even if said shell runs under a different user ID. 25 | */ 26 | public class TermExec { 27 | public static final String SERVICE_ACTION_V1 = "jackpal.androidterm.action.START_TERM.v1"; 28 | 29 | private final List command; 30 | private final Map environment; 31 | 32 | public TermExec(@NonNull String... command) { 33 | this(new ArrayList<>(Arrays.asList(command))); 34 | } 35 | 36 | public TermExec(@NonNull List command) { 37 | this.command = command; 38 | this.environment = new Hashtable<>(System.getenv()); 39 | } 40 | 41 | 42 | public @NonNull 43 | List command() { 44 | return command; 45 | } 46 | 47 | public @NonNull 48 | Map environment() { 49 | return environment; 50 | } 51 | 52 | public @NonNull 53 | TermExec command(@NonNull String... command) { 54 | return command(new ArrayList<>(Arrays.asList(command))); 55 | } 56 | 57 | public @NonNull 58 | TermExec command(List command) { 59 | this.command.clear(); 60 | this.command.addAll(command); 61 | return this; 62 | } 63 | 64 | /** 65 | * Start the process and attach it to the pty, corresponding to given file descriptor. 66 | * You have to obtain this file descriptor yourself by calling 67 | * {@link android.os.ParcelFileDescriptor#open} on special terminal multiplexer 68 | * device (located at /dev/ptmx). 69 | *

70 | * Callers are responsible for closing the descriptor. 71 | * 72 | * @return the PID of the started process. 73 | */ 74 | public int start(@NonNull ParcelFileDescriptor ptmxFd) throws IOException { 75 | if (Looper.getMainLooper() == Looper.myLooper()) 76 | throw new IllegalStateException("This method must not be called from the main thread!"); 77 | 78 | if (command.size() == 0) 79 | throw new IllegalStateException("Empty command!"); 80 | 81 | final String cmd = command.remove(0); 82 | final String[] cmdArray = command.toArray(new String[0]); 83 | final String[] envArray = new String[environment.size()]; 84 | int i = 0; 85 | for (Map.Entry entry : environment.entrySet()) { 86 | envArray[i++] = entry.getKey() + "=" + entry.getValue(); 87 | } 88 | 89 | return Process.createSubprocess(ptmxFd, cmd, cmdArray, envArray); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /samples/NOTE.pathbroadcasts: -------------------------------------------------------------------------------- 1 | DEPRECATION: 2 | 3 | Functionality that uses "broadcasts" to collect extra paths with shell commands is deprecated. 4 | 5 | 1) It collects only one path for "prepend" and only one for "append" to the system PATH. Winner is last received. 6 | 2) It is used only in normal start of terminal session but not if terminal is opened from external applications. 7 | 3) Collection of "extra" path components slow down opening of normal sessions. 8 | 4) Applications that run on Android 8.0 (Oreo, API Level 26) or higher no longer receive implicit broadcasts registered in their manifest. 9 | 10 | In transition period "broadcast" functionality will be restricted only to applications signed with the same certificate, i.e. "SecureBox". 11 | 12 | 13 | FUTURE: 14 | Broadcasts based path collection will be replaced with one action based. 15 | External application, according to permissions, could send one or more path components. 16 | "TermOne Plus" activity will allow user to position paths sent in PATH variable. 17 | Also activity will allows user to ignore or delete a path component. 18 | -------------------------------------------------------------------------------- /samples/intents/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /samples/intents/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion project.ext.compileSdkVersion 5 | buildToolsVersion project.ext.buildToolsVersion 6 | defaultConfig { 7 | applicationId "com.termoneplus.sample.intents" 8 | minSdkVersion project.ext.minSdkVersion 9 | targetSdkVersion project.ext.targetSdkVersion 10 | 11 | versionCode 210 12 | versionName '2.1.0' 13 | 14 | // TermOnePlus applicationId 15 | manifestPlaceholders = [ termApplicationId:"com.termoneplus"] 16 | buildConfigField("String", "TERM_APPLICATION_ID", "\"com.termoneplus\"") 17 | } 18 | compileOptions { 19 | sourceCompatibility JavaVersion.VERSION_1_8 20 | targetCompatibility JavaVersion.VERSION_1_8 21 | } 22 | productFlavors { 23 | } 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 28 | } 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation 'androidx.appcompat:appcompat:1.3.0-beta01' 34 | implementation 'com.google.android.material:material:1.3.0-rc01' 35 | } 36 | -------------------------------------------------------------------------------- /samples/intents/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples/intents/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /samples/intents/src/main/res/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /samples/intents/src/main/res/drawable/background_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/intents/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 19 | 20 | 26 | 27 | 33 | 34 | 40 | 41 | 49 | 50 | 59 | 60 | 61 | 62 | 68 | 69 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /samples/intents/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /samples/intents/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Intents (sample) 5 | This sample shows how to send intents to terminal emulator \"TermOne Plus\". 6 | 7 | Open New Window 8 | Script: 9 | Run Script 10 | Run Script and Save Window 11 | Run Script in Saved Window 12 | 13 | 14 | echo \'Hello world!\' 15 | \necho \'Здравейте!\' 16 | \necho \'Καλημέρα κόσμε!\' 17 | \necho \'コンニチハ!\' 18 | 19 | No Activity found 20 | 21 | Permission denial. You have to reinstall application to grant permissions. 22 | Permission denial. You have to grant permissions. 23 | Permission is required to run script in a terminal window. 24 | 25 | -------------------------------------------------------------------------------- /samples/pathbroadcasts/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /samples/pathbroadcasts/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion project.ext.compileSdkVersion 5 | buildToolsVersion project.ext.buildToolsVersion 6 | defaultConfig { 7 | applicationId "com.termoneplus.sample.pathbroadcasts" 8 | minSdkVersion project.ext.minSdkVersion 9 | targetSdkVersion project.ext.targetSdkVersion 10 | 11 | versionCode 210 12 | versionName '2.1.0' 13 | 14 | // TermOnePlus applicationId 15 | manifestPlaceholders = [ termApplicationId:"com.termoneplus"] 16 | buildConfigField("String", "TERM_APPLICATION_ID", "\"com.termoneplus\"") 17 | } 18 | compileOptions { 19 | sourceCompatibility JavaVersion.VERSION_1_8 20 | targetCompatibility JavaVersion.VERSION_1_8 21 | } 22 | productFlavors { 23 | } 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 28 | } 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation project(':emulatorview') 34 | } 35 | -------------------------------------------------------------------------------- /samples/pathbroadcasts/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /samples/pathbroadcasts/src/main/assets/.gitattributes: -------------------------------------------------------------------------------- 1 | hello eol=lf 2 | ls eol=lf 3 | -------------------------------------------------------------------------------- /samples/pathbroadcasts/src/main/assets/hello: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | echo "Hello, world!" 4 | exit 0 5 | -------------------------------------------------------------------------------- /samples/pathbroadcasts/src/main/assets/ls: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | echo "Hello, I'm pretending to be ls!" 4 | exec /system/bin/ls "$@" 5 | -------------------------------------------------------------------------------- /samples/pathbroadcasts/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Deprecated: Path Broadcast 4 | 5 | -------------------------------------------------------------------------------- /samples/telnet/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /samples/telnet/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion project.ext.compileSdkVersion 5 | buildToolsVersion project.ext.buildToolsVersion 6 | defaultConfig { 7 | applicationId "com.termoneplus.sample.telnet" 8 | minSdkVersion project.ext.minSdkVersion 9 | targetSdkVersion project.ext.targetSdkVersion 10 | 11 | versionCode 210 12 | versionName '2.1.0' 13 | } 14 | compileOptions { 15 | sourceCompatibility JavaVersion.VERSION_1_8 16 | targetCompatibility JavaVersion.VERSION_1_8 17 | } 18 | productFlavors { 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | implementation project(':emulatorview') 30 | implementation 'androidx.appcompat:appcompat:1.3.0-beta01' 31 | } 32 | -------------------------------------------------------------------------------- /samples/telnet/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /samples/telnet/src/main/java/jackpal/androidterm/sample/telnet/LaunchActivity.java: -------------------------------------------------------------------------------- 1 | package jackpal.androidterm.sample.telnet; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View.OnClickListener; 7 | import android.widget.EditText; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | 12 | /** 13 | * Provides a UI to launch the terminal emulator activity, connected to 14 | * either a local shell or a Telnet server. 15 | */ 16 | public class LaunchActivity extends AppCompatActivity { 17 | private static final String TAG = "TelnetLaunchActivity"; 18 | 19 | /** 20 | * Called when the activity is first created. 21 | */ 22 | @Override 23 | public void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.launch_activity); 26 | final Context context = this; 27 | addClickListener(R.id.launchLocal, v -> { 28 | Intent intent = new Intent(context, TermActivity.class); 29 | intent.putExtra("type", "local"); 30 | startActivity(intent); 31 | }); 32 | 33 | final EditText hostEdit = findViewById(R.id.hostname); 34 | addClickListener(R.id.launchTelnet, v -> { 35 | Intent intent = new Intent(context, TermActivity.class); 36 | intent.putExtra("type", "telnet"); 37 | String hostname = hostEdit.getText().toString(); 38 | intent.putExtra("host", hostname); 39 | startActivity(intent); 40 | }); 41 | } 42 | 43 | private void addClickListener(int buttonId, OnClickListener onClickListener) { 44 | findViewById(buttonId).setOnClickListener(onClickListener); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /samples/telnet/src/main/res/layout/launch_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 |